"use client";

import {
  createContext,
  useContext,
  useState,
  useCallback,
  ReactNode,
  useEffect,
  useRef,
} from "react";
import {
  BarcodeLabelComponent,
  BarcodeLabelLayout,
  BarcodeLabelMaster,
} from "@/types/barcode";
import { barcodeApi } from "@/lib/api/barcodeApi";
import { useToast } from "@/hooks/use-toast";
import { v4 as uuidv4 } from "uuid";

interface BarcodeDesignerContextType {
  labelId: string;
  labelMaster: BarcodeLabelMaster | null;
  widthMm: number;
  heightMm: number;
  components: BarcodeLabelComponent[];
  selectedComponentId: string | null;
  isLoading: boolean;
  isSaving: boolean;

  setWidthMm: (v: number) => void;
  setHeightMm: (v: number) => void;
  addComponent: (component: BarcodeLabelComponent) => void;
  updateComponent: (id: string, updates: Partial<BarcodeLabelComponent>) => void;
  removeComponent: (id: string) => void;
  selectComponent: (id: string | null) => void;
  reorderComponent: (id: string, direction: "up" | "down") => void;

  loadLabel: () => Promise<void>;
  loadLayout: () => Promise<void>;
  saveLayout: () => Promise<void>;
  applyTemplate: (templateId: string) => Promise<void>;

  gridSize: number;
  showGrid: boolean;
  setShowGrid: (v: boolean) => void;
  snapValueToGrid: (v: number) => number;
}

const BarcodeDesignerContext = createContext<BarcodeDesignerContextType | undefined>(undefined);

const MM_TO_PX = 4;
const DEFAULT_WIDTH_MM = 50;
const DEFAULT_HEIGHT_MM = 30;

export function BarcodeDesignerProvider({
  labelId,
  children,
}: {
  labelId: string;
  children: ReactNode;
}) {
  const [labelMaster, setLabelMaster] = useState<BarcodeLabelMaster | null>(null);
  const [widthMm, setWidthMm] = useState(DEFAULT_WIDTH_MM);
  const [heightMm, setHeightMm] = useState(DEFAULT_HEIGHT_MM);
  const [components, setComponents] = useState<BarcodeLabelComponent[]>([]);
  const [selectedComponentId, setSelectedComponentId] = useState<string | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [isSaving, setIsSaving] = useState(false);
  const [showGrid, setShowGrid] = useState(true);
  const [gridSize] = useState(2); // mm
  const { toast } = useToast();
  const selectedComponentIdRef = useRef<string | null>(null);
  selectedComponentIdRef.current = selectedComponentId;

  const snapValueToGrid = useCallback(
    (v: number) => Math.round(v / (gridSize * MM_TO_PX)) * (gridSize * MM_TO_PX),
    [gridSize]
  );

  const loadLabel = useCallback(async () => {
    if (labelId === "new") {
      setLabelMaster(null);
      setWidthMm(DEFAULT_WIDTH_MM);
      setHeightMm(DEFAULT_HEIGHT_MM);
      setComponents([]);
      setIsLoading(false);
      return;
    }
    try {
      const res = await barcodeApi.getLabelById(labelId);
      if (res.success && res.data) {
        setLabelMaster(res.data);
        if (res.data.width_mm != null) setWidthMm(res.data.width_mm);
        if (res.data.height_mm != null) setHeightMm(res.data.height_mm);
      }
    } catch (e: any) {
      toast({
        title: "오류",
        description: e.message || "라벨 정보를 불러오지 못했습니다.",
        variant: "destructive",
      });
    } finally {
      setIsLoading(false);
    }
  }, [labelId, toast]);

  const loadLayout = useCallback(async () => {
    if (labelId === "new") return;
    try {
      const res = await barcodeApi.getLayout(labelId);
      if (res.success && res.data) {
        setWidthMm(res.data.width_mm);
        setHeightMm(res.data.height_mm);
        setComponents(res.data.components || []);
      }
    } catch {
      // 레이아웃 없으면 빈 상태 유지
    }
  }, [labelId]);

  // labelId 변경 시에만 초기 로드 (loadLabel/loadLayout을 deps에 넣지 않아 무한 루프 방지)
  useEffect(() => {
    let cancelled = false;
    setIsLoading(true);

    const run = async () => {
      if (labelId === "new") {
        setLabelMaster(null);
        setWidthMm(DEFAULT_WIDTH_MM);
        setHeightMm(DEFAULT_HEIGHT_MM);
        setComponents([]);
        if (!cancelled) setIsLoading(false);
        return;
      }
      try {
        const res = await barcodeApi.getLabelById(labelId);
        if (cancelled) return;
        if (res.success && res.data) {
          setLabelMaster(res.data);
          if (res.data.width_mm != null) setWidthMm(res.data.width_mm);
          if (res.data.height_mm != null) setHeightMm(res.data.height_mm);
        }
        const layoutRes = await barcodeApi.getLayout(labelId);
        if (cancelled) return;
        if (layoutRes.success && layoutRes.data) {
          setWidthMm(layoutRes.data.width_mm);
          setHeightMm(layoutRes.data.height_mm);
          setComponents(layoutRes.data.components || []);
        }
      } catch (e: any) {
        if (!cancelled) {
          toast({
            title: "오류",
            description: e.message || "라벨을 불러오지 못했습니다.",
            variant: "destructive",
          });
        }
      } finally {
        if (!cancelled) setIsLoading(false);
      }
    };

    run();
    return () => {
      cancelled = true;
    };
  }, [labelId]); // eslint-disable-line react-hooks/exhaustive-deps

  const addComponent = useCallback((component: BarcodeLabelComponent) => {
    setComponents((prev) => [...prev, { ...component, id: component.id || `comp_${uuidv4()}` }]);
    setSelectedComponentId(component.id || null);
  }, []);

  const updateComponent = useCallback((id: string, updates: Partial<BarcodeLabelComponent>) => {
    setComponents((prev) =>
      prev.map((c) => (c.id === id ? { ...c, ...updates } : c))
    );
  }, []);

  const removeComponent = useCallback((id: string) => {
    setComponents((prev) => prev.filter((c) => c.id !== id));
    setSelectedComponentId((sid) => (sid === id ? null : sid));
  }, []);

  // Delete / Backspace 키로 선택된 요소 삭제 (입력 필드에서는 무시)
  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key !== "Delete" && e.key !== "Backspace") return;
      const target = e.target as HTMLElement;
      if (
        target.tagName === "INPUT" ||
        target.tagName === "TEXTAREA" ||
        target.isContentEditable
      ) {
        return;
      }
      const sid = selectedComponentIdRef.current;
      if (sid) {
        e.preventDefault();
        removeComponent(sid);
      }
    };
    document.addEventListener("keydown", handleKeyDown);
    return () => document.removeEventListener("keydown", handleKeyDown);
  }, [removeComponent]);

  const reorderComponent = useCallback((id: string, direction: "up" | "down") => {
    setComponents((prev) => {
      const idx = prev.findIndex((c) => c.id === id);
      if (idx < 0) return prev;
      const next = [...prev];
      const swap = direction === "up" ? idx - 1 : idx + 1;
      if (swap < 0 || swap >= next.length) return prev;
      [next[idx], next[swap]] = [next[swap], next[idx]];
      return next.map((c, i) => ({ ...c, zIndex: i }));
    });
  }, []);

  const saveLayout = useCallback(async () => {
    if (labelId === "new") {
      toast({
        title: "저장 불가",
        description: "먼저 라벨을 저장한 뒤 레이아웃을 저장할 수 있습니다.",
        variant: "destructive",
      });
      return;
    }
    setIsSaving(true);
    try {
      const layout: BarcodeLabelLayout = {
        width_mm: widthMm,
        height_mm: heightMm,
        components: components.map((c, i) => ({ ...c, zIndex: i })),
      };
      await barcodeApi.saveLayout(labelId, layout);
      toast({ title: "저장됨", description: "레이아웃이 저장되었습니다." });
    } catch (e: any) {
      toast({
        title: "저장 실패",
        description: e.message || "레이아웃 저장에 실패했습니다.",
        variant: "destructive",
      });
    } finally {
      setIsSaving(false);
    }
  }, [labelId, widthMm, heightMm, components, toast]);

  const applyTemplate = useCallback(
    async (templateId: string) => {
      try {
        const res = await barcodeApi.getTemplateById(templateId);
        const layout = res.success && res.data ? (res.data as { layout?: BarcodeLabelLayout }).layout : null;
        if (layout && typeof layout.width_mm === "number" && typeof layout.height_mm === "number") {
          setWidthMm(layout.width_mm);
          setHeightMm(layout.height_mm);
          setComponents(
            (layout.components || []).map((c) => ({
              ...c,
              id: c.id || `comp_${uuidv4()}`,
            }))
          );
          setSelectedComponentId(null);
          const name = (res.data as { template_name_kor?: string }).template_name_kor || "템플릿";
          toast({
            title: "템플릿 적용",
            description: `${name} 적용됨 (${layout.width_mm}×${layout.height_mm}mm)`,
          });
        } else {
          toast({
            title: "템플릿 적용 실패",
            description: "레이아웃 데이터가 없습니다.",
            variant: "destructive",
          });
        }
      } catch (e: any) {
        toast({
          title: "템플릿 적용 실패",
          description: e.message || "템플릿을 불러오지 못했습니다.",
          variant: "destructive",
        });
      }
    },
    [toast]
  );

  const value: BarcodeDesignerContextType = {
    labelId,
    labelMaster,
    widthMm,
    heightMm,
    components,
    selectedComponentId,
    isLoading,
    isSaving,
    setWidthMm,
    setHeightMm,
    addComponent,
    updateComponent,
    removeComponent,
    selectComponent: setSelectedComponentId,
    reorderComponent,
    loadLabel,
    loadLayout,
    saveLayout,
    applyTemplate,
    gridSize,
    showGrid,
    setShowGrid,
    snapValueToGrid,
  };

  return (
    <BarcodeDesignerContext.Provider value={value}>
      {children}
      {isLoading && (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/20">
          <div className="bg-background flex flex-col items-center gap-2 rounded-lg border p-4 shadow-lg">
            <div className="border-primary h-8 w-8 animate-spin rounded-full border-2 border-t-transparent" />
            <span className="text-sm">라벨 불러오는 중...</span>
          </div>
        </div>
      )}
    </BarcodeDesignerContext.Provider>
  );
}

export function useBarcodeDesigner() {
  const ctx = useContext(BarcodeDesignerContext);
  if (ctx === undefined) {
    throw new Error("useBarcodeDesigner must be used within BarcodeDesignerProvider");
  }
  return ctx;
}

export { MM_TO_PX };
