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

"use client";

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

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

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

export type CartCategory = "inbound" | "outbound";

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

	addItem: (item: CartItem, rowKey: string, sourceTable?: string) => void;
	removeItem: (rowKey: string) => void;
	updateItemQuantity: (
		rowKey: string,
		quantity: number,
		packageUnit?: string,
		packageEntries?: CartItem["packageEntries"],
	) => void;
	updateItemRow: (rowKey: string, partialRow: Record<string, unknown>) => 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,
	cartType: string,
	selectedColumns?: string[],
	screenId?: string,
): Record<string, unknown> {
	const rowData =
		selectedColumns && selectedColumns.length > 0
			? Object.fromEntries(
					Object.entries(item.row).filter(([k]) => selectedColumns.includes(k)),
				)
			: item.row;

	const record: Record<string, unknown> = {
		cart_type: cartType,
		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 || "",
	};
	// 레거시 모드: screen_id 포함
	if (screenId) {
		record.screen_id = screenId;
	}
	return record;
}

// ===== 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}:${JSON.stringify(item.row)}`,
			)
			.sort()
			.join("|");

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

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

// 오버로드: 카테고리 기반 (신규) + 레거시 screen_id 기반 (PopCardListComponent 등)
export function useCartSync(category: CartCategory): UseCartSyncReturn;
export function useCartSync(screenId: string, sourceTable: string): UseCartSyncReturn;
export function useCartSync(
	categoryOrScreenId: string,
	sourceTable?: string,
): UseCartSyncReturn {
	// 레거시 호출 감지: 2번째 인자가 있으면 구 시그니처 (screen_id 기반)
	const isLegacy = sourceTable !== undefined;
	const cartTypeValue = isLegacy ? "pop" : `pop_${categoryOrScreenId}`;
	const screenIdValue = isLegacy ? categoryOrScreenId : undefined;
	const legacySourceTable = isLegacy ? sourceTable : undefined;

	const [cartItems, setCartItems] = useState<CartItemWithId[]>([]);
	const [savedItems, setSavedItems] = useState<CartItemWithId[]>([]);
	const [syncStatus, setSyncStatus] = useState<CartSyncStatus>("clean");
	const [loading, setLoading] = useState(false);

	const categoryRef = useRef(categoryOrScreenId);
	categoryRef.current = categoryOrScreenId;
	const cartTypeRef = useRef(cartTypeValue);
	cartTypeRef.current = cartTypeValue;
	const legacySourceTableRef = useRef(legacySourceTable);
	legacySourceTableRef.current = legacySourceTable;

	// ----- DB에서 장바구니 로드 -----
	const loadFromDb = useCallback(async () => {
		if (!categoryOrScreenId) return;
		setLoading(true);
		try {
			const filters: Record<string, string> = {
				cart_type: cartTypeValue,
				status: "in_cart",
			};
			// 레거시: screen_id로 필터링
			if (screenIdValue) {
				filters.screen_id = screenIdValue;
			}

			const result = await dataApi.getTableData("cart_items", {
				size: 500,
				filters,
			});

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

	// 마운트 시 자동 로드
	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, sourceTable?: 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: sourceTable || legacySourceTableRef.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,
				),
			);
		},
		[],
	);

	// row 객체에 임의 필드를 부분 업데이트 (예: inspectionResult)
	const updateItemRow = useCallback(
		(rowKey: string, partialRow: Record<string, unknown>) => {
			setCartItems((prev) =>
				prev.map((i) =>
					i.rowKey === rowKey ? { ...i, row: { ...i.row, ...partialRow } } : 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 currentCartType = cartTypeRef.current;
			const currentScreenId = isLegacy ? categoryRef.current : undefined;

			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;
				const rowChanged = JSON.stringify(c.row) !== JSON.stringify(saved.row);
				return (
					c.quantity !== saved.quantity ||
					c.packageUnit !== saved.packageUnit ||
					c.status !== saved.status ||
					rowChanged
				);
			});

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

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

				// 삭제 대상: 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;
					const rowChanged =
						JSON.stringify(c.row) !== JSON.stringify(saved.row);
					return (
						c.quantity !== saved.quantity ||
						c.packageUnit !== saved.packageUnit ||
						c.status !== saved.status ||
						rowChanged
					);
				});

				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,
						currentCartType,
						selectedColumns,
						currentScreenId,
					);
					// cart_items.id는 NOT NULL + 자동생성 없음 → UUID 직접 생성
					const recordWithId = { id: crypto.randomUUID(), ...record };
					promises.push(dataApi.createRecord("cart_items", recordWithId));
				}

				for (const item of toUpdate) {
					const record = cartItemToDbRecord(
						item,
						currentCartType,
						selectedColumns,
						currentScreenId,
					);
					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,
		updateItemRow,
		isItemInCart,
		getCartItem,
		getChanges,
		saveToDb,
		loadFromDb,
		resetToSaved,
	};
}
