import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import type { MeasurementFrame } from '../protocol/types';
import * as StorageService from '../services/StorageService';
import { fileStorage } from '../utils/storage';

const MAX_HISTORY = 50;

interface DataState {
  hasProject: boolean;
  currentFrame: MeasurementFrame | null;
  history: MeasurementFrame[];
  sessionId: string;
  projectId: string | null;
  _frameCounter: number;
  nextFrameId: () => number;
  addFrame: (frame: MeasurementFrame) => void;
  addFrameUI: (frame: MeasurementFrame) => void;
  deleteFrame: (frameId: number) => void;
  clearHistory: () => void;
  newSession: (projectId: string) => Promise<void>;
  resumeSession: (sessionId: string, projectId?: string | null) => Promise<void>;
  setProject: (projectId: string | null) => Promise<void>;
  resetToNoProject: () => void;
  init: () => Promise<void>;
}

export const useDataStore = create<DataState>()(
  persist(
    (set, get) => ({
      hasProject: false,
      currentFrame: null,
      history: [],
      sessionId: '',
      projectId: null,
      _frameCounter: 0,

      nextFrameId: () => {
        const id = get()._frameCounter;
        set((s) => ({ _frameCounter: s._frameCounter + 1 }));
        return id;
      },

      addFrame: (frame) => {
        set((s) => ({
          currentFrame: frame,
          history: [frame, ...s.history].slice(0, MAX_HISTORY),
        }));
        void StorageService.persistFrame(frame, get().sessionId);
      },

      addFrameUI: (frame) => {
        set((s) => ({
          currentFrame: frame,
          history: [frame, ...s.history].slice(0, MAX_HISTORY),
        }));
      },

      deleteFrame: (frameId) => {
        const { sessionId, history, currentFrame } = get();
        void StorageService.deleteFrame(frameId, sessionId);
        const next = history.filter((f) => f.frameId !== frameId);
        set({
          history: next,
          currentFrame: currentFrame?.frameId === frameId ? (next[0] ?? null) : currentFrame,
        });
      },

      clearHistory: () => set({ history: [], currentFrame: null }),

      newSession: async (projectId: string) => {
        const id = generateSessionId();
        await StorageService.ensureSession(id, projectId);
        set({ sessionId: id, projectId, history: [], currentFrame: null, _frameCounter: 0, hasProject: true });
      },

      resumeSession: async (sessionId, projectId) => {
        const pid = projectId !== undefined ? projectId : get().projectId;
        await StorageService.ensureSession(sessionId, pid);
        const maxId = await StorageService.getMaxFrameId(sessionId);
        set({
          sessionId,
          projectId: pid ?? null,
          history: [],
          currentFrame: null,
          _frameCounter: maxId + 1,
          hasProject: true,
        });
      },

      setProject: async (projectId) => {
        const { sessionId } = get();
        await StorageService.assignSessionToProject(sessionId, projectId);
        set({ projectId });
      },

      resetToNoProject: () => set({
        hasProject: false,
        projectId: null,
        sessionId: '',
        history: [],
        currentFrame: null,
        _frameCounter: 0,
      }),

      init: async () => {
        await StorageService.initStorage();

        const exists = await StorageService.hasAnyProject();
        if (!exists) {
          set({ hasProject: false });
          return;
        }

        set({ hasProject: true });
        const { sessionId, projectId } = get();

        if (projectId && sessionId) {
          const projects = await StorageService.listProjects();
          if (projects.some((p) => p.projectId === projectId)) {
            await StorageService.ensureSession(sessionId, projectId);
            const maxId = await StorageService.getMaxFrameId(sessionId);
            set({ _frameCounter: maxId + 1 });
            return;
          }
        }

        const first = await StorageService.getFirstProject();
        if (first) {
          const sessions = await StorageService.listSessionsByProject(first.projectId);
          if (sessions.length > 0) {
            await get().resumeSession(sessions[0].sessionId, first.projectId);
          } else {
            await get().newSession(first.projectId);
          }
        }
      },
    }),
    {
      name: 'data-session',
      storage: createJSONStorage(() => fileStorage),
      partialize: (state) => ({
        sessionId: state.sessionId,
        projectId: state.projectId,
      }),
    },
  ),
);

function generateSessionId(): string {
  const now = new Date();
  const pad = (n: number, d = 2) => String(n).padStart(d, '0');
  const rand = Math.random().toString(36).slice(2, 6);
  return (
    `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}` +
    `_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}` +
    `${pad(now.getMilliseconds(), 3)}_${rand}`
  );
}
