/**
 * useCartSync - 장바구니 DB 동기화 훅
 *
 * DB(cart_items 테이블) <-> 로컬 상태를 동기화하고 변경사항(dirty)을 감지한다.
 *
 * 동작 방식:
 * 1. 마운트 시 DB에서 해당 screen_id + user_id의 장바구니를 로드
 * 2. addItem/removeItem/updateItem은 로컬 상태만 변경 (DB 미반영, dirty 상태)
 * 3. saveToDb 호출 시 로컬 상태를 DB에 일괄 반영 (추가/수정/삭제)
 * 4. isDirty = 로컬 상태와 DB 마지막 로드 상태의 차이 존재 여부
 *
 * 사용 예시:
 * ```typescript
 * const cart = useCartSync("SCR-001", "item_info");
 *
 * // 품목 추가 (로컬만, DB 미반영)
 * cart.addItem({ row, quantity: 10 }, "D1710008");
 *
 * // DB 저장 (pop-icon 확인 모달에서 호출)
 * await cart.saveToDb();
 * ```
 */

"use client";

import { useState, useCallback, useRef, useEffect } from "react";
import { dataApi } from "@/lib/api/data";
import type {
  CartItem,
  CartItemWithId,
  CartSyncStatus,
  CartItemStatus,
} from "@/lib/registry/pop-components/types";

// ===== 반환 타입 =====

export interface CartChanges {
  toCreate: Record<string, unknown>[];
  toUpdate: Record<string, unknown>[];
  toDelete: (string | number)[];
}

export interface UseCartSyncReturn {
  cartItems: CartItemWithId[];
  savedItems: CartItemWithId[];
  syncStatus: CartSyncStatus;
  cartCount: number;
  isDirty: boolean;
  loading: boolean;

  addItem: (item: CartItem, rowKey: string) => void;
  removeItem: (rowKey: string) => void;
  updateItemQuantity: (rowKey: string, quantity: number, packageUnit?: string, packageEntries?: CartItem["packageEntries"]) => void;
  isItemInCart: (rowKey: string) => boolean;
  getCartItem: (rowKey: string) => CartItemWithId | undefined;

  getChanges: (selectedColumns?: string[]) => CartChanges;
  saveToDb: (selectedColumns?: string[]) => Promise<boolean>;
  loadFromDb: () => Promise<void>;
  resetToSaved: () => void;
}

// ===== DB 행 -> CartItemWithId 변환 =====

function dbRowToCartItem(dbRow: Record<string, unknown>): CartItemWithId {
  let rowData: Record<string, unknown> = {};
  try {
    const raw = dbRow.row_data;
    if (typeof raw === "string" && raw.trim()) {
      rowData = JSON.parse(raw);
    } else if (typeof raw === "object" && raw !== null) {
      rowData = raw as Record<string, unknown>;
    }
  } catch {
    rowData = {};
  }

  let packageEntries: CartItem["packageEntries"] | undefined;
  try {
    const raw = dbRow.package_entries;
    if (typeof raw === "string" && raw.trim()) {
      packageEntries = JSON.parse(raw);
    } else if (Array.isArray(raw)) {
      packageEntries = raw;
    }
  } catch {
    packageEntries = undefined;
  }

  return {
    row: rowData,
    quantity: Number(dbRow.quantity) || 0,
    packageUnit: (dbRow.package_unit as string) || undefined,
    packageEntries,
    cartId: (dbRow.id as string) || undefined,
    sourceTable: (dbRow.source_table as string) || "",
    rowKey: (dbRow.row_key as string) || "",
    status: ((dbRow.status as string) || "in_cart") as CartItemStatus,
    _origin: "db",
    memo: (dbRow.memo as string) || undefined,
  };
}

// ===== CartItemWithId -> DB 저장용 레코드 변환 =====

function cartItemToDbRecord(
  item: CartItemWithId,
  screenId: string,
  selectedColumns?: string[],
): Record<string, unknown> {
  const rowData =
    selectedColumns && selectedColumns.length > 0
      ? Object.fromEntries(
          Object.entries(item.row).filter(([k]) => selectedColumns.includes(k)),
        )
      : item.row;

  return {
    cart_type: "pop",
    screen_id: screenId,
    source_table: item.sourceTable,
    row_key: item.rowKey,
    row_data: JSON.stringify(rowData),
    quantity: String(item.quantity),
    unit: "",
    package_unit: item.packageUnit || "",
    package_entries: item.packageEntries ? JSON.stringify(item.packageEntries) : "",
    status: item.status,
    memo: item.memo || "",
  };
}

// ===== dirty check: 두 배열의 내용이 동일한지 비교 =====

function areItemsEqual(a: CartItemWithId[], b: CartItemWithId[]): boolean {
  if (a.length !== b.length) return false;

  const serialize = (items: CartItemWithId[]) =>
    items
      .map((item) => `${item.rowKey}:${item.quantity}:${item.packageUnit || ""}:${item.status}`)
      .sort()
      .join("|");

  return serialize(a) === serialize(b);
}

// ===== 훅 본체 =====

export function useCartSync(
  screenId: string,
  sourceTable: string,
): UseCartSyncReturn {
  const [cartItems, setCartItems] = useState<CartItemWithId[]>([]);
  const [savedItems, setSavedItems] = useState<CartItemWithId[]>([]);
  const [syncStatus, setSyncStatus] = useState<CartSyncStatus>("clean");
  const [loading, setLoading] = useState(false);

  const screenIdRef = useRef(screenId);
  const sourceTableRef = useRef(sourceTable);
  screenIdRef.current = screenId;
  sourceTableRef.current = sourceTable;

  // ----- DB에서 장바구니 로드 -----
  const loadFromDb = useCallback(async () => {
    if (!screenId || !sourceTable) return;
    setLoading(true);
    try {
      const result = await dataApi.getTableData("cart_items", {
        size: 500,
        filters: {
          screen_id: screenId,
          cart_type: "pop",
          status: "in_cart",
        },
      });

      const items = (result.data || []).map(dbRowToCartItem);
      setSavedItems(items);
      setCartItems(items);
      setSyncStatus("clean");
    } catch (err) {
      console.error("[useCartSync] DB 로드 실패:", err);
    } finally {
      setLoading(false);
    }
  }, [screenId, sourceTable]);

  // 마운트 시 자동 로드
  useEffect(() => {
    loadFromDb();
  }, [loadFromDb]);

  // ----- dirty 상태 계산 -----
  const isDirty = !areItemsEqual(cartItems, savedItems);

  // isDirty 변경 시 syncStatus 자동 갱신
  useEffect(() => {
    if (syncStatus !== "saving") {
      setSyncStatus(isDirty ? "dirty" : "clean");
    }
  }, [isDirty, syncStatus]);

  // ----- 로컬 조작 (DB 미반영) -----

  const addItem = useCallback(
    (item: CartItem, rowKey: string) => {
      setCartItems((prev) => {
        const exists = prev.find((i) => i.rowKey === rowKey);
        if (exists) {
          return prev.map((i) =>
            i.rowKey === rowKey
              ? { ...i, quantity: item.quantity, packageUnit: item.packageUnit, packageEntries: item.packageEntries, row: item.row }
              : i,
          );
        }
        const newItem: CartItemWithId = {
          ...item,
          cartId: undefined,
          sourceTable: sourceTableRef.current,
          rowKey,
          status: "in_cart",
          _origin: "local",
        };
        return [...prev, newItem];
      });
    },
    [],
  );

  const removeItem = useCallback((rowKey: string) => {
    setCartItems((prev) => prev.filter((i) => i.rowKey !== rowKey));
  }, []);

  const updateItemQuantity = useCallback(
    (rowKey: string, quantity: number, packageUnit?: string, packageEntries?: CartItem["packageEntries"]) => {
      setCartItems((prev) =>
        prev.map((i) =>
          i.rowKey === rowKey
            ? {
                ...i,
                quantity,
                ...(packageUnit !== undefined && { packageUnit }),
                ...(packageEntries !== undefined && { packageEntries }),
              }
            : i,
        ),
      );
    },
    [],
  );

  const isItemInCart = useCallback(
    (rowKey: string) => cartItems.some((i) => i.rowKey === rowKey),
    [cartItems],
  );

  const getCartItem = useCallback(
    (rowKey: string) => cartItems.find((i) => i.rowKey === rowKey),
    [cartItems],
  );

  // ----- diff 계산 (백엔드 전송용) -----
  const getChanges = useCallback((selectedColumns?: string[]): CartChanges => {
    const currentScreenId = screenIdRef.current;

    const cartRowKeys = new Set(cartItems.map((i) => i.rowKey));
    const toDeleteItems = savedItems.filter((s) => s.cartId && !cartRowKeys.has(s.rowKey));
    const toCreateItems = cartItems.filter((c) => !c.cartId);

    const savedMap = new Map(savedItems.map((s) => [s.rowKey, s]));
    const toUpdateItems = cartItems.filter((c) => {
      if (!c.cartId) return false;
      const saved = savedMap.get(c.rowKey);
      if (!saved) return false;
      return c.quantity !== saved.quantity || c.packageUnit !== saved.packageUnit || c.status !== saved.status;
    });

    return {
      toCreate: toCreateItems.map((item) => cartItemToDbRecord(item, currentScreenId, selectedColumns)),
      toUpdate: toUpdateItems.map((item) => ({ id: item.cartId, ...cartItemToDbRecord(item, currentScreenId, selectedColumns) })),
      toDelete: toDeleteItems.map((item) => item.cartId!),
    };
  }, [cartItems, savedItems]);

  // ----- DB 저장 (일괄) -----
  const saveToDb = useCallback(async (selectedColumns?: string[]): Promise<boolean> => {
    setSyncStatus("saving");
    try {
      const currentScreenId = screenIdRef.current;

      // 삭제 대상: savedItems에 있지만 cartItems에 없는 것
      const cartRowKeys = new Set(cartItems.map((i) => i.rowKey));
      const toDelete = savedItems.filter((s) => s.cartId && !cartRowKeys.has(s.rowKey));

      // 추가 대상: cartItems에 있지만 cartId가 없는 것 (로컬에서 추가됨)
      const toCreate = cartItems.filter((c) => !c.cartId);

      // 수정 대상: 양쪽 다 존재하고 cartId 있으면서 내용이 다른 것
      const savedMap = new Map(savedItems.map((s) => [s.rowKey, s]));
      const toUpdate = cartItems.filter((c) => {
        if (!c.cartId) return false;
        const saved = savedMap.get(c.rowKey);
        if (!saved) return false;
        return (
          c.quantity !== saved.quantity ||
          c.packageUnit !== saved.packageUnit ||
          c.status !== saved.status
        );
      });

      const promises: Promise<unknown>[] = [];

      for (const item of toDelete) {
        promises.push(dataApi.updateRecord("cart_items", item.cartId!, { status: "cancelled" }));
      }

      for (const item of toCreate) {
        const record = cartItemToDbRecord(item, currentScreenId, selectedColumns);
        promises.push(dataApi.createRecord("cart_items", record));
      }

      for (const item of toUpdate) {
        const record = cartItemToDbRecord(item, currentScreenId, selectedColumns);
        promises.push(dataApi.updateRecord("cart_items", item.cartId!, record));
      }

      await Promise.all(promises);

      // 저장 후 DB에서 다시 로드하여 cartId 등을 최신화
      await loadFromDb();
      return true;
    } catch (err) {
      console.error("[useCartSync] DB 저장 실패:", err);
      setSyncStatus("dirty");
      return false;
    }
  }, [cartItems, savedItems, loadFromDb]);

  // ----- 로컬 변경 취소 -----
  const resetToSaved = useCallback(() => {
    setCartItems(savedItems);
    setSyncStatus("clean");
  }, [savedItems]);

  return {
    cartItems,
    savedItems,
    syncStatus,
    cartCount: cartItems.length,
    isDirty,
    loading,
    addItem,
    removeItem,
    updateItemQuantity,
    isItemInCart,
    getCartItem,
    getChanges,
    saveToDb,
    loadFromDb,
    resetToSaved,
  };
}
