"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";

import { useDebouncedValue } from "@/hooks/useDebouncedValue";
import { fetchProject, patchProjectSceneData } from "@/lib/project-api";
import { isDevProjectId } from "@/lib/project-ids";
import { isImageSceneDataEmpty } from "@/lib/image-scene-data";
import { useImageEditorStore } from "@/lib/image-editor-store";
import {
  PROJECT_SAVE_DEBOUNCE_MS,
  PROJECT_SAVED_DISPLAY_MS,
  type ProjectSaveStatus,
} from "@/lib/project-save-status";

export interface UseImageProjectPersistenceResult {
  projectName: string;
  setProjectName: (name: string) => void;
  saveStatus: ProjectSaveStatus;
  retrySave: () => void;
}

export function useImageProjectPersistence(
  projectId: string | undefined
): UseImageProjectPersistenceResult {
  const layers = useImageEditorStore((state) => state.layers);
  const canvasWidth = useImageEditorStore((state) => state.canvasWidth);
  const canvasHeight = useImageEditorStore((state) => state.canvasHeight);
  const adjustments = useImageEditorStore((state) => state.adjustments);
  const activeFilterPreset = useImageEditorStore(
    (state) => state.activeFilterPreset
  );
  const hydrateFromSceneData = useImageEditorStore(
    (state) => state.hydrateFromSceneData
  );
  const getSceneDataForSave = useImageEditorStore(
    (state) => state.getSceneDataForSave
  );

  const [projectName, setProjectName] = useState("Untitled image");
  const [saveStatus, setSaveStatus] = useState<ProjectSaveStatus>("idle");

  const skipSaveRef = useRef(true);
  const lastSavedPayloadRef = useRef<string | null>(null);
  const savedTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  const persistPayload = useMemo(
    () => JSON.stringify(getSceneDataForSave()),
    [layers, canvasWidth, canvasHeight, adjustments, activeFilterPreset, getSceneDataForSave]
  );

  const debouncedPayload = useDebouncedValue(
    persistPayload,
    PROJECT_SAVE_DEBOUNCE_MS
  );

  const clearSavedTimer = useCallback(() => {
    if (savedTimerRef.current) {
      clearTimeout(savedTimerRef.current);
      savedTimerRef.current = null;
    }
  }, []);

  const performSave = useCallback(
    async (payloadJson: string) => {
      if (!projectId || isDevProjectId(projectId)) return;

      setSaveStatus("saving");
      try {
        const scene_data = JSON.parse(payloadJson) as Record<string, unknown>;
        await patchProjectSceneData(projectId, scene_data);
        lastSavedPayloadRef.current = payloadJson;
        setSaveStatus("saved");
        clearSavedTimer();
        savedTimerRef.current = setTimeout(() => {
          setSaveStatus((current) => (current === "saved" ? "idle" : current));
        }, PROJECT_SAVED_DISPLAY_MS);
      } catch {
        setSaveStatus("error");
      }
    },
    [projectId, clearSavedTimer]
  );

  const retrySave = useCallback(() => {
    void performSave(persistPayload);
  }, [performSave, persistPayload]);

  useEffect(() => {
    if (!projectId || isDevProjectId(projectId)) return;

    let cancelled = false;
    skipSaveRef.current = true;

    void (async () => {
      try {
        const { project } = await fetchProject(projectId);
        if (cancelled) return;

        setProjectName(project.name);

        if (!isImageSceneDataEmpty(project.scene_data)) {
          hydrateFromSceneData(project.scene_data);
        }

        lastSavedPayloadRef.current = JSON.stringify(
          useImageEditorStore.getState().getSceneDataForSave()
        );
      } catch {
        if (!cancelled) {
          setSaveStatus("error");
        }
      } finally {
        if (!cancelled) {
          skipSaveRef.current = false;
        }
      }
    })();

    return () => {
      cancelled = true;
    };
  }, [projectId, hydrateFromSceneData]);

  useEffect(() => {
    if (!projectId || isDevProjectId(projectId) || skipSaveRef.current) return;
    if (persistPayload === lastSavedPayloadRef.current) return;
    setSaveStatus("pending");
  }, [projectId, persistPayload]);

  useEffect(() => {
    if (!projectId || isDevProjectId(projectId) || skipSaveRef.current) return;
    if (debouncedPayload === lastSavedPayloadRef.current) return;
    void performSave(debouncedPayload);
  }, [projectId, debouncedPayload, performSave]);

  useEffect(() => clearSavedTimer, [clearSavedTimer]);

  return { projectName, setProjectName, saveStatus, retrySave };
}
