"use client";

import { useMemo, useState } from "react";
import Link from "next/link";
import { useRouter, useSearchParams } from "next/navigation";
import Badge from "@/components/ui/Badge";
import Button from "@/components/ui/Button";
import ActionMenu from "@/components/ui/ActionMenu";
import EmptyState from "@/components/ui/EmptyState";
import ErrorState from "@/components/ui/ErrorState";

type ProductRow = {
  id: string;
  shopId: string;
  itemId: string;
  modelId: string;
  itemName: string | null;
  modelName: string | null;
  imageUrl: string | null;
  masterSku: string | null;
  platformSku: string | null;
  stock: number | null;
  reservedStock: number | null;
  availableStock: number | null;
  price: number | null;
  currency: string | null;
  hpp: number | null;
  hppCurrency: string | null;
  hppUpdatedAt: string | null;
  hppAwal: number | null;
  hppAwalCurrency: string | null;
  hppAwalUpdatedAt: string | null;
  hppFinal: number | null;
  hppFinalCurrency: string | null;
  hppFinalUpdatedAt: string | null;
  status: string | null;
  updateTime: string | null;
};

type EditState = {
  row: ProductRow;
  itemName: string;
  platformSku: string;
  masterSku: string;
  price: string;
  stock: string;
  hppAwal: string;
  hppFinal: string;
  saving: boolean;
  feedback: {
    tone: "error" | "info";
    title: string;
    description: string;
  } | null;
};

type ProductPatchResponse = {
  ok: boolean;
  error?: string | null;
  data?: { updates?: string[]; errors?: string[] };
};

type ShopRow = {
  shopId: string;
  shopName: string | null;
  connected: boolean;
};

type ProductFilter = "mapped" | "unmapped" | "stock_empty" | "stock_low" | "hpp_missing";

type MasterSkuGroup = {
  masterSku: string;
  productCount: number;
  shopCount: number;
  shopNames: string[];
  platformSkus: string[];
  productNames: string[];
  hppFinal: number | null;
  hppFinalMixed: boolean;
  missingHppFinal: number;
  imageUrl: string | null;
};

type ManualMappingState = {
  rows: ProductRow[];
  platformSku: string;
  masterSku: string;
  hppAwal: string;
  hppFinal: string;
  syncShopeeSku: boolean;
  saving: boolean;
};

type ImportPreviewRow = {
  line: number;
  status: "matched" | "unmatched" | "ambiguous" | "invalid" | "duplicate";
  sourceSku: string | null;
  sourceName: string | null;
  matchedBy: "sku" | "name" | null;
  reason: string;
  willChange: boolean;
  products: Array<{
    shopId: string;
    shopName: string;
    itemId: string;
    modelId: string;
    itemName: string | null;
    modelName: string | null;
    platformSku: string | null;
    currentMasterSku: string | null;
    nextMasterSku: string | null;
  }>;
  nextMasterSku: string | null;
  hppAwal: number | null;
  hppFinal: number | null;
  targetCount: number;
  shopCount: number;
  changeCount: number;
};

type ImportPreview = {
  summary: {
    total: number;
    matched: number;
    ready: number;
    products: number;
    shops: number;
    unmatched: number;
    ambiguous: number;
    invalid: number;
    duplicate: number;
    applied: number;
  };
  rows: ImportPreviewRow[];
};

type Props = {
  initial: {
    shops: ShopRow[];
    activeShopId: string | null;
    rows: ProductRow[];
    total: number;
    stats: { total: number; normal: number; inactive: number; hppAwal: number; hppFinal: number; mapped: number; unmapped: number; stockEmpty: number; stockLow: number; hppMissing: number; masterGroups: number };
    masterSkuGroups: MasterSkuGroup[];
    q: string | null;
    status: string | null;
    filter: ProductFilter | null;
    profitRepair: boolean;
    repairHppComplete: boolean;
    view: "products" | "master_sku";
    page: number;
    pageSize: number;
  };
};

function formatMoney(value: number | null, currency: string | null) {
  if (value === null || value === undefined) return "-";
  return `${currency || "IDR"} ${value.toLocaleString("id-ID")}`;
}

function formatDate(value: string | null) {
  if (!value) return "-";
  return new Intl.DateTimeFormat("id-ID", { dateStyle: "medium", timeStyle: "short" }).format(new Date(value));
}

function statusTone(status: string | null) {
  if (status === "NORMAL") return "success" as const;
  if (!status) return "neutral" as const;
  return "warning" as const;
}

function productKey(row: Pick<ProductRow, "shopId" | "itemId" | "modelId">) {
  return `${row.shopId}:${row.itemId}:${row.modelId || "0"}`;
}

function numberDraftChanged(draft: string, current: number | null | undefined) {
  const value = draft.trim();
  if (!value) return current !== null && current !== undefined;
  const parsed = Number(value);
  return !Number.isFinite(parsed) || current === null || current === undefined || parsed !== Number(current);
}

function editChangePlan(edit: EditState) {
  const row = edit.row;
  const originalHppAwal = row.hppAwal ?? row.hpp;
  const originalHppFinal = row.hppFinal ?? row.hpp;
  const masterSkuChanged = edit.masterSku.trim() !== (row.masterSku || "").trim();
  const hppChanged = numberDraftChanged(edit.hppAwal, originalHppAwal) || numberDraftChanged(edit.hppFinal, originalHppFinal);
  const itemNameChanged = edit.itemName.trim() !== (row.itemName || "").trim();
  const platformSkuChanged = edit.platformSku.trim() !== (row.platformSku || "").trim();
  const priceChanged = numberDraftChanged(edit.price, row.price);
  const stockChanged = numberDraftChanged(edit.stock, row.availableStock ?? row.stock);
  const localChanges = [
    platformSkuChanged ? "SKU platform" : null,
    masterSkuChanged ? "Master SKU" : null,
    hppChanged ? "HPP" : null,
  ].filter(Boolean) as string[];
  const shopeeChanges = [
    itemNameChanged ? "judul" : null,
    platformSkuChanged ? "SKU platform" : null,
    priceChanged ? "harga" : null,
    stockChanged ? "stok" : null,
  ].filter(Boolean) as string[];
  return {
    masterSkuChanged,
    hppChanged,
    itemNameChanged,
    platformSkuChanged,
    priceChanged,
    stockChanged,
    localChanges,
    shopeeChanges,
    hasChanges: localChanges.length > 0 || shopeeChanges.length > 0,
  };
}

function productSaveError(message: string) {
  if (message.toLowerCase().includes("product.error_busi")) {
    return "Shopee menolak perubahan listing karena aturan bisnis produk (biasanya produk sedang ikut promosi atau perubahan SKU dibatasi). SKU tetap disimpan di Sellfin.";
  }
  if (message.toLowerCase().includes("product.error_param")) {
    return "Shopee menolak parameter SKU (product.error_param). SKU Shopee belum berubah; pastikan SKU unik dan maksimal 100 karakter.";
  }
  return message;
}

async function patchProduct(itemId: string, body: Record<string, unknown>, timeoutMs = 30_000) {
  const controller = new AbortController();
  const timeout = window.setTimeout(() => controller.abort(), timeoutMs);
  try {
    const response = await fetch(`/api/shopee/products/${encodeURIComponent(itemId)}`, {
      method: "PATCH",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(body),
      signal: controller.signal,
    });
    const json = (await response.json().catch(() => ({ ok: false, error: `Server mengembalikan HTTP ${response.status}.` }))) as ProductPatchResponse;
    return { response, json };
  } catch (error) {
    if (error instanceof DOMException && error.name === "AbortError") {
      throw new Error("Waktu tunggu habis. Sellfin menghentikan loading setelah 30 detik; periksa hasil sinkron sebelum mencoba lagi.");
    }
    throw error;
  } finally {
    window.clearTimeout(timeout);
  }
}

export default function ProductsPageClient({ initial }: Props) {
  const router = useRouter();
  const searchParams = useSearchParams();
  const [loading, setLoading] = useState(false);
  const [message, setMessage] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [qInput, setQInput] = useState(initial.q || "");
  const [hppDraft, setHppDraft] = useState<Record<string, string>>({});
  const [hppLoading, setHppLoading] = useState<Record<string, boolean>>({});
  const [edit, setEdit] = useState<EditState | null>(null);
  const [importOpen, setImportOpen] = useState(false);
  const [importFile, setImportFile] = useState<File | null>(null);
  const [importShopId, setImportShopId] = useState("all");
  const [importPreview, setImportPreview] = useState<ImportPreview | null>(null);
  const [importLoading, setImportLoading] = useState(false);
  const [selectedProducts, setSelectedProducts] = useState<Record<string, ProductRow>>({});
  const [manualMapping, setManualMapping] = useState<ManualMappingState | null>(null);

  const pageCount = Math.max(Math.ceil(initial.total / initial.pageSize), 1);
  const selectedRows = Object.values(selectedProducts);
  const selectedOnPage = initial.rows.length > 0 && initial.rows.every((row) => Boolean(selectedProducts[productKey(row)]));
  const visibleMasterSkuGroups = useMemo(() => {
    const query = (initial.q || "").trim().toLocaleLowerCase("id-ID");
    if (!query) return initial.masterSkuGroups;
    return initial.masterSkuGroups.filter((group) => [
      group.masterSku,
      ...group.platformSkus,
      ...group.productNames,
      ...group.shopNames,
    ].some((value) => value.toLocaleLowerCase("id-ID").includes(query)));
  }, [initial.masterSkuGroups, initial.q]);

  function openEditor(row: ProductRow) {
    setEdit({
      row,
      itemName: row.itemName || "",
      platformSku: row.platformSku || "",
      masterSku: row.masterSku || "",
      price: row.price === null || row.price === undefined ? "" : String(row.price),
      stock: row.availableStock === null || row.availableStock === undefined ? "" : String(row.availableStock),
      hppAwal: (row.hppAwal ?? row.hpp) === null || (row.hppAwal ?? row.hpp) === undefined ? "" : String(row.hppAwal ?? row.hpp),
      hppFinal: (row.hppFinal ?? row.hpp) === null || (row.hppFinal ?? row.hpp) === undefined ? "" : String(row.hppFinal ?? row.hpp),
      saving: false,
      feedback: null,
    });
  }

  function productHref(next: Record<string, string | number | null | undefined>) {
    const params = new URLSearchParams(searchParams.toString());
    if (next.filter !== undefined || next.view !== undefined || next.shop_id !== undefined) params.delete("product_keys");
    for (const [key, value] of Object.entries(next)) {
      if (value === null || value === undefined || value === "") params.delete(key);
      else params.set(key, String(value));
    }
    if (next.q !== undefined || next.status !== undefined || next.filter !== undefined || next.shop_id !== undefined || next.view !== undefined) params.delete("page");
    const suffix = params.toString();
    return suffix ? `/products?${suffix}` : "/products";
  }

  function updateQuery(next: Record<string, string | number | null | undefined>) {
    router.push(productHref(next));
  }

  function toggleProduct(row: ProductRow) {
    const key = productKey(row);
    setSelectedProducts((state) => {
      const next = { ...state };
      if (next[key]) delete next[key];
      else next[key] = row;
      return next;
    });
  }

  function toggleCurrentPage() {
    setSelectedProducts((state) => {
      const next = { ...state };
      for (const row of initial.rows) {
        const key = productKey(row);
        if (selectedOnPage) delete next[key];
        else next[key] = row;
      }
      return next;
    });
  }

  function openManualMapping(rows: ProductRow[]) {
    const uniqueRows = Array.from(new Map(rows.map((row) => [productKey(row), row])).values());
    if (uniqueRows.length === 0) return;
    const existingMasterSkus = new Set(uniqueRows.map((row) => row.masterSku?.trim()).filter(Boolean));
    const existingPlatformSkus = new Set(uniqueRows.map((row) => row.platformSku?.trim()).filter(Boolean));
    const awalHppValues = new Set(uniqueRows.map((row) => row.hppAwal).filter((value): value is number => value !== null));
    const finalHppValues = new Set(uniqueRows.map((row) => row.hppFinal ?? row.hpp).filter((value): value is number => value !== null));
    setManualMapping({
      rows: uniqueRows,
      platformSku: existingPlatformSkus.size === 1 ? Array.from(existingPlatformSkus)[0] || "" : "",
      masterSku: existingMasterSkus.size === 1 ? Array.from(existingMasterSkus)[0] || "" : "",
      hppAwal: awalHppValues.size === 1 ? String(Array.from(awalHppValues)[0]) : "",
      hppFinal: finalHppValues.size === 1 ? String(Array.from(finalHppValues)[0]) : "",
      syncShopeeSku: false,
      saving: false,
    });
  }

  async function applyManualMapping() {
    if (!manualMapping) return;
    const masterSku = manualMapping.masterSku.trim();
    const platformSku = manualMapping.platformSku.trim();
    if (!masterSku) {
      setError("Master SKU wajib diisi.");
      return;
    }
    if (platformSku.length > 100) {
      setError("SKU Platform maksimal 100 karakter.");
      return;
    }
    if (manualMapping.syncShopeeSku && !platformSku) {
      setError("Isi SKU Platform sebelum mengirim perubahan ke Shopee.");
      return;
    }
    const hppAwal = manualMapping.hppAwal.trim() === "" ? null : Number(manualMapping.hppAwal);
    const hppFinal = manualMapping.hppFinal.trim() === "" ? null : Number(manualMapping.hppFinal);
    if (hppAwal !== null && (!Number.isFinite(hppAwal) || hppAwal < 0)) {
      setError("HPP Awal harus berupa angka positif atau dikosongkan.");
      return;
    }
    if (hppFinal !== null && (!Number.isFinite(hppFinal) || hppFinal < 0)) {
      setError("HPP Final harus berupa angka positif atau dikosongkan.");
      return;
    }

    setManualMapping((state) => (state ? { ...state, saving: true } : state));
    setError(null);
    setMessage(null);
    try {
      const response = await fetch("/api/shopee/products/master-sku", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          masterSku,
          ...(platformSku ? { platformSku } : {}),
          hppAwal,
          hppFinal,
          syncShopeeSku: manualMapping.syncShopeeSku,
          targets: manualMapping.rows.map((row) => ({ shopId: row.shopId, itemId: row.itemId, modelId: row.modelId })),
        }),
      });
      const json = (await response.json()) as {
        ok: boolean;
        error?: string;
        data?: {
          summary: { selected: number; localUpdated: number; shopeeUpdated: number; shopeeUnchanged: number; shopeeFailed: number; localFailed: number; hppSynced?: number };
          results: Array<{ shopId: string; itemId: string; modelId: string; localUpdated: boolean; shopeeStatus: "not_requested" | "unchanged" | "updated" | "failed"; error: string | null }>;
        };
      };
      if (!response.ok || !json.ok || !json.data) throw new Error(json.error || "Pemetaan Master SKU gagal.");
      const { summary, results } = json.data;
      const hppNote = summary.hppSynced ? ` HPP Awal/Final terselaraskan pada ${summary.hppSynced} produk dengan Master SKU ini.` : "";
      setMessage(
        manualMapping.syncShopeeSku
          ? `${summary.localUpdated} produk dipetakan ke ${masterSku}. SKU Platform ${platformSku} dikirim ke Shopee: ${summary.shopeeUpdated} diperbarui, ${summary.shopeeUnchanged} sudah sama.${hppNote}`
          : `${summary.localUpdated} produk dipetakan ke Master SKU ${masterSku}${platformSku ? ` dengan SKU Platform ${platformSku}` : ""}. SKU live Shopee tidak diubah.${hppNote}`,
      );
      const retryKeys = new Set(results.filter((result) => !result.localUpdated || result.shopeeStatus === "failed").map((result) => productKey(result)));
      setSelectedProducts((state) => Object.fromEntries(Object.entries(state).filter(([key]) => retryKeys.has(key))));
      if (summary.localFailed || summary.shopeeFailed) {
        const firstError = results.find((result) => result.error)?.error;
        setError(`Pemetaan lokal gagal ${summary.localFailed}; update SKU Shopee gagal ${summary.shopeeFailed}.${firstError ? ` ${firstError}` : ""}`);
      }
      setManualMapping(null);
      router.refresh();
    } catch (err) {
      setError(err instanceof Error ? err.message : "Pemetaan Master SKU gagal.");
      setManualMapping((state) => (state ? { ...state, saving: false } : state));
    }
  }

  async function syncProducts() {
    const targets = initial.activeShopId
      ? initial.shops.filter((shop) => shop.shopId === initial.activeShopId && shop.connected)
      : initial.shops.filter((shop) => shop.connected);
    if (targets.length === 0) {
      setError(initial.activeShopId ? "Toko ini disconnected. Reconnect toko sebelum sinkronisasi produk." : "Belum ada toko aktif yang bisa disinkronkan.");
      return;
    }
    setLoading(true);
    setError(null);
    setMessage(null);
    try {
      let synced = 0;
      let itemCount = 0;
      let pages = 0;
      let partialErrors = 0;
      let hasMore = false;
      const failedShops: string[] = [];

      for (const target of targets) {
        const res = await fetch("/api/shopee/products/sync", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ shopId: target.shopId }),
        });
        const json = (await res.json()) as {
          ok: boolean;
          error?: string;
          data?: { synced?: number; itemCount?: number; pages?: number; hasMore?: boolean; errors?: Array<{ error: string }> };
        };
        if (!res.ok || !json.ok) {
          failedShops.push(target.shopName || target.shopId);
          continue;
        }
        synced += json.data?.synced ?? 0;
        itemCount += json.data?.itemCount ?? 0;
        pages += json.data?.pages ?? 0;
        partialErrors += json.data?.errors?.length ?? 0;
        hasMore ||= Boolean(json.data?.hasMore);
      }

      if (failedShops.length === targets.length) throw new Error(`Sinkron produk gagal untuk ${failedShops.join(", ")}.`);
      const warning = hasMore ? " Masih ada halaman berikutnya; jalankan sinkron lagi." : "";
      const partial = partialErrors ? ` ${partialErrors} produk gagal dibaca variasinya.` : "";
      const failed = failedShops.length ? ` Gagal pada: ${failedShops.join(", ")}.` : "";
      setMessage(`${synced} SKU dari ${itemCount} produk tersimpan untuk ${targets.length - failedShops.length} toko (${pages} halaman).${partial}${warning}${failed}`);
      router.refresh();
    } catch (err) {
      setError(err instanceof Error ? err.message : "Gagal sinkron produk.");
    } finally {
      setLoading(false);
    }
  }

  async function saveHpp(row: ProductRow) {
    const key = `${row.shopId}:${row.itemId}:${row.modelId}`;
    const hppAwal = hppDraft[`${key}:awal`] ?? (row.hppAwal ?? row.hpp ?? "");
    const hppFinal = hppDraft[`${key}:final`] ?? (row.hppFinal ?? row.hpp ?? "");
    setHppLoading((state) => ({ ...state, [key]: true }));
    setError(null);
    setMessage(null);
    try {
      const res = await fetch(`/api/shopee/products/${encodeURIComponent(row.itemId)}`, {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          action: "hpp",
          shopId: row.shopId,
          modelId: row.modelId,
          hppAwal: hppAwal === "" ? null : Number(hppAwal),
          hppFinal: hppFinal === "" ? null : Number(hppFinal),
          currency: row.currency || "IDR",
        }),
      });
      const json = (await res.json()) as { ok: boolean; error?: string; data?: { hppSynced?: number } };
      if (!res.ok || !json.ok) throw new Error(json.error || "Gagal menyimpan HPP.");
      const targetName = row.platformSku || row.modelName || row.itemId;
      setMessage(json.data?.hppSynced && json.data.hppSynced > 1
        ? `HPP Awal dan HPP Final ${targetName} tersimpan dan terselaraskan ke ${json.data.hppSynced} produk dengan Master SKU yang sama.`
        : `HPP Awal dan HPP Final ${targetName} tersimpan.`);
      router.refresh();
    } catch (err) {
      setError(err instanceof Error ? err.message : "Gagal menyimpan HPP.");
    } finally {
      setHppLoading((state) => ({ ...state, [key]: false }));
    }
  }

  async function saveShopeeEdit() {
    if (!edit) return;
    const plan = editChangePlan(edit);
    if (!plan.hasChanges) {
      setEdit((state) => (state ? { ...state, feedback: { tone: "info", title: "Tidak ada perubahan", description: "Ubah data terlebih dahulu sebelum menyimpan." } } : state));
      return;
    }

    const parsedPrice = edit.price.trim() === "" ? null : Number(edit.price);
    const parsedStock = edit.stock.trim() === "" ? null : Number(edit.stock);
    const parsedHppAwal = edit.hppAwal.trim() === "" ? null : Number(edit.hppAwal);
    const parsedHppFinal = edit.hppFinal.trim() === "" ? null : Number(edit.hppFinal);
    const validationError =
      (plan.itemNameChanged && !edit.itemName.trim() ? "Judul produk tidak boleh kosong." : null)
      || (edit.platformSku.trim().length > 100 ? "SKU Shopee maksimal 100 karakter." : null)
      || (edit.masterSku.trim().length > 80 ? "Master SKU maksimal 80 karakter." : null)
      || (plan.priceChanged && (parsedPrice === null || !Number.isFinite(parsedPrice) || parsedPrice < 0) ? "Harga Shopee harus berupa angka positif." : null)
      || (plan.stockChanged && (parsedStock === null || !Number.isInteger(parsedStock) || parsedStock < 0) ? "Stok harus berupa angka bulat positif." : null)
      || (plan.hppChanged && parsedHppAwal !== null && (!Number.isFinite(parsedHppAwal) || parsedHppAwal < 0) ? "HPP Awal harus berupa angka positif atau kosong." : null)
      || (plan.hppChanged && parsedHppFinal !== null && (!Number.isFinite(parsedHppFinal) || parsedHppFinal < 0) ? "HPP Final harus berupa angka positif atau kosong." : null);
    if (validationError) {
      setEdit((state) => (state ? { ...state, feedback: { tone: "error", title: "Periksa data", description: validationError } } : state));
      return;
    }

    setEdit((state) => (state ? { ...state, saving: true, feedback: null } : state));
    setError(null);
    setMessage(null);
    const localSaved: string[] = [];
    let persistedRow = { ...edit.row };
    try {
      const masterSkuTrimmed = edit.masterSku.trim();
      if (plan.masterSkuChanged) {
        const { response, json } = await patchProduct(edit.row.itemId, {
            action: "local",
            shopId: edit.row.shopId,
            modelId: edit.row.modelId,
            masterSku: masterSkuTrimmed,
        });
        if (!response.ok || !json.ok) throw new Error(json.error || "Gagal menyimpan SKU lokal.");
        localSaved.push("Master SKU");
        persistedRow = {
          ...persistedRow,
          masterSku: masterSkuTrimmed || null,
        };
      }

      if (plan.hppChanged) {
        const { response, json } = await patchProduct(edit.row.itemId, {
            action: "hpp",
            shopId: edit.row.shopId,
            modelId: edit.row.modelId,
            hppAwal: parsedHppAwal,
            hppFinal: parsedHppFinal,
            currency: edit.row.currency || "IDR",
        });
        if (!response.ok || !json.ok) throw new Error(json.error || "Gagal menyimpan HPP lokal.");
        localSaved.push("HPP");
        persistedRow = { ...persistedRow, hppAwal: parsedHppAwal, hppFinal: parsedHppFinal };
      }

      if (plan.shopeeChanges.length > 0) {
        const { response, json } = await patchProduct(edit.row.itemId, {
          action: "update_shopee",
          shopId: edit.row.shopId,
          modelId: edit.row.modelId,
          ...(plan.itemNameChanged ? { itemName: edit.itemName.trim() } : {}),
          ...(plan.platformSkuChanged ? { platformSku: edit.platformSku.trim() } : {}),
          ...(plan.priceChanged ? { price: parsedPrice } : {}),
          ...(plan.stockChanged ? { stock: parsedStock } : {}),
        }, 30_000);
        const remoteSaved = json.data?.updates || [];
        if (!response.ok || !json.ok) {
          const localPrefix = localSaved.length ? `${localSaved.join(" dan ")} sudah tersimpan di Sellfin. ` : "";
          const remotePrefix = remoteSaved.length ? `${remoteSaved.join(", ")} berhasil diperbarui di Shopee, tetapi ` : "";
          setEdit((state) => (state ? {
            ...state,
            row: persistedRow,
            saving: false,
            feedback: {
              tone: "error",
              title: localSaved.length || remoteSaved.length ? "Sebagian perubahan tersimpan" : "Shopee menolak perubahan",
              description: `${localPrefix}${remotePrefix}${productSaveError(json.error || "Shopee menolak update produk.")}`,
            },
          } : state));
          if (localSaved.length || remoteSaved.length) router.refresh();
          return;
        }
        setMessage(`${localSaved.length ? `${localSaved.join(" dan ")} tersimpan di Sellfin. ` : ""}Shopee berhasil diperbarui: ${remoteSaved.join(", ")}.`);
      } else {
        setMessage(`${localSaved.join(" dan ")} tersimpan di Sellfin. Tidak ada data yang dikirim ke Shopee.`);
      }
      setEdit(null);
      router.refresh();
    } catch (err) {
      const detail = err instanceof Error ? productSaveError(err.message) : "Perubahan gagal disimpan.";
      const localPrefix = localSaved.length ? `${localSaved.join(" dan ")} sudah tersimpan di Sellfin. ` : "";
      setEdit((state) => (state ? {
        ...state,
        row: persistedRow,
        saving: false,
        feedback: {
          tone: "error",
          title: localSaved.length ? "Sebagian perubahan tersimpan" : "Gagal menyimpan",
          description: `${localPrefix}${detail}`,
        },
      } : state));
      if (localSaved.length) router.refresh();
    } finally {
      setEdit((state) => (state ? { ...state, saving: false } : state));
    }
  }

  async function runImport(mode: "preview" | "apply") {
    if (!importFile) {
      setError("Pilih berkas CSV terlebih dahulu.");
      return;
    }
    if (!importShopId) {
      setError("Pilih cakupan pemetaan.");
      return;
    }
    setImportLoading(true);
    setError(null);
    setMessage(null);
    try {
      const body = new FormData();
      body.set("file", importFile);
      body.set("shop_id", importShopId);
      body.set("mode", mode);
      const res = await fetch("/api/shopee/products/master-sku-import", { method: "POST", body });
      const json = (await res.json()) as { ok: boolean; error?: string; data?: ImportPreview };
      if (!res.ok || !json.ok || !json.data) throw new Error(json.error || "CSV gagal diproses.");
      setImportPreview(json.data);
      if (mode === "apply") {
        setMessage(`${json.data.summary.applied} produk diperbarui. Pemetaan CSV mencakup ${json.data.summary.shops} toko; baris yang tidak cocok tidak diubah.`);
        setImportOpen(false);
        setImportFile(null);
        router.refresh();
      }
    } catch (err) {
      setError(err instanceof Error ? err.message : "CSV gagal diproses.");
    } finally {
      setImportLoading(false);
    }
  }

  const exportParams = new URLSearchParams({ shop_id: initial.activeShopId || "all" });
  if (initial.q) exportParams.set("q", initial.q);
  if (initial.status) exportParams.set("status", initial.status);
  if (initial.filter) exportParams.set("filter", initial.filter);
  const exportUrl = `/api/shopee/products/export${exportParams.toString() ? `?${exportParams.toString()}` : ""}`;
  const currentEditPlan = edit ? editChangePlan(edit) : null;

  return (
    <div className="space-y-5">
      <section className="flex flex-wrap items-center justify-between gap-3">
        <div>
          <h1 className="page-title">Produk</h1>
          <p className="mt-1 text-sm text-slate-500">SKU Shopee, Master SKU, stok, harga, dan HPP.</p>
        </div>
        <div className="flex flex-wrap gap-2">
          {selectedRows.length ? <Button variant="primary" onClick={() => openManualMapping(selectedRows)}>Petakan {selectedRows.length} SKU</Button> : null}
          <Button disabled={!initial.shops.length} onClick={() => { setImportShopId("all"); setImportPreview(null); setImportOpen(true); }}>Import Master SKU</Button>
          <Button variant="primary" disabled={!initial.shops.some((shop) => shop.connected)} loading={loading} onClick={syncProducts}>Sinkron Shopee</Button>
          <a className="ui-button ui-button-secondary" href={exportUrl}>Ekspor CSV</a>
        </div>
      </section>

      {message ? <div className="rounded-xl border border-emerald-200 bg-emerald-50 p-3 text-sm font-semibold text-emerald-800">{message}</div> : null}
      {error ? <ErrorState description={error} /> : null}
      {initial.profitRepair ? <div className="rounded-xl border border-amber-200 bg-amber-50 p-3 text-sm font-semibold text-amber-900">{initial.repairHppComplete
        ? `HPP Final pada ${initial.total.toLocaleString("id-ID")} SKU terkait sudah terisi. Buka kembali Profit untuk melihat hasil hitung terbaru.`
        : `Menampilkan ${initial.total.toLocaleString("id-ID")} SKU yang terkait dengan order profit belum lengkap. SKU yang sudah terisi tetap terlihat agar bisa diperiksa.`}</div> : null}

      <section className="flex flex-wrap items-center gap-1 rounded-xl border border-slate-200 bg-white p-1.5 shadow-sm dark:border-slate-800 dark:bg-slate-900" aria-label="Workspace Produk dan Master SKU">
        <CatalogWorkspaceTab active={initial.view === "products" && initial.filter !== "unmapped"} href={productHref({ view: null, filter: null })} label="Produk toko" value={initial.stats.total} />
        <CatalogWorkspaceTab active={initial.view === "master_sku"} href={productHref({ view: "master_sku", shop_id: initial.activeShopId || "all", q: null, status: null, filter: null })} label="Master SKU" value={initial.stats.masterGroups} />
        <CatalogWorkspaceTab active={initial.view === "products" && initial.filter === "unmapped"} href={productHref({ view: null, shop_id: initial.activeShopId || "all", filter: "unmapped" })} label="Belum dipetakan" value={initial.stats.unmapped} tone="warning" />
      </section>

      <section className="flex flex-wrap items-center justify-between gap-3 rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-950">
        <div><b>Profit memakai HPP Final.</b> HPP Awal tetap dicatat untuk memantau selisih biaya. Setiap pesanan menyimpan snapshot HPP sendiri; mengubah HPP produk tidak menulis ulang profit pesanan lama.</div>
        {initial.view === "products" ? <span className="text-xs font-semibold text-emerald-800">Centang produk untuk memetakan Master SKU lintas toko.</span> : null}
      </section>

      {initial.view === "products" ? <>
      <section className="flex flex-wrap gap-2 rounded-xl border border-slate-200 bg-white p-2 shadow-sm dark:border-slate-800 dark:bg-slate-900" aria-label="Filter kesiapan katalog">
        <CatalogFilter active={!initial.filter} href={productHref({ filter: null })} label="Semua" value={initial.stats.total} />
        <CatalogFilter active={initial.filter === "mapped"} href={productHref({ filter: "mapped" })} label="Master SKU dipetakan" value={initial.stats.mapped} tone="success" />
        <CatalogFilter active={initial.filter === "unmapped"} href={productHref({ filter: "unmapped" })} label="Belum dipetakan" value={initial.stats.unmapped} tone="warning" />
        <CatalogFilter active={initial.filter === "stock_empty"} href={productHref({ filter: "stock_empty" })} label="Stok kosong" value={initial.stats.stockEmpty} tone="danger" />
        <CatalogFilter active={initial.filter === "stock_low"} href={productHref({ filter: "stock_low" })} label="Stok menipis" value={initial.stats.stockLow} tone="warning" />
        <CatalogFilter active={initial.filter === "hpp_missing"} href={productHref({ filter: "hpp_missing" })} label="HPP belum lengkap" value={initial.stats.hppMissing} tone="danger" />
      </section>

      <section className="workspace-panel p-3">
        <form action="/products" className="grid gap-2 lg:grid-cols-[210px_1fr_170px_auto_auto]" method="get">
          {initial.filter ? <input name="filter" type="hidden" value={initial.filter} /> : null}
          {initial.view !== "products" ? <input name="view" type="hidden" value={initial.view} /> : null}
          <select
            className="rounded-[10px] border border-slate-200 bg-white px-3 py-2 text-[13px] font-semibold"
            name="shop_id"
            value={initial.activeShopId || "all"}
            onChange={(event) => updateQuery({ shop_id: event.target.value })}
          >
            {initial.shops.length === 0 ? <option value="">Belum terhubung</option> : null}
            {initial.shops.length > 0 ? <option value="all">Semua toko</option> : null}
            {initial.shops.map((shop) => (
              <option key={shop.shopId} value={shop.shopId}>{shop.shopName || shop.shopId}{shop.connected ? "" : " · disconnected"}</option>
            ))}
          </select>
          <input
            className="rounded-[10px] border border-slate-200 bg-white px-3 py-2 text-[13px]"
            name="q"
            value={qInput}
            onChange={(event) => setQInput(event.target.value)}
            placeholder="Cari nama/SKU; pisahkan koma untuk beberapa SKU..."
          />
          <select
            className="rounded-[10px] border border-slate-200 bg-white px-3 py-2 text-[13px] font-semibold"
            name="status"
            value={initial.status || ""}
            onChange={(event) => updateQuery({ status: event.target.value || null })}
          >
            <option value="">Semua status</option>
            <option value="NORMAL">Aktif</option>
            <option value="UNLIST">Nonaktif</option>
            <option value="BANNED">Banned</option>
            <option value="DELETED">Deleted</option>
          </select>
          <Button variant="primary" type="submit">Cari</Button>
          <Link className="ui-button ui-button-secondary" href={productHref({ q: null, status: null, filter: null, page: 1 })}>Reset</Link>
        </form>
      </section>

      {selectedRows.length ? (
        <section className="sticky top-2 z-20 flex flex-wrap items-center justify-between gap-3 rounded-xl border border-slate-300 bg-slate-950 px-4 py-3 text-white shadow-lg">
          <div>
            <div className="text-sm font-bold">{selectedRows.length} SKU dipilih lintas halaman</div>
            <div className="text-xs text-slate-300">Atur SKU Platform dan Master SKU secara terpisah untuk produk yang dipilih.</div>
          </div>
          <div className="flex gap-2">
            <Button className="border-slate-600 bg-slate-900 text-white hover:bg-slate-800" onClick={() => setSelectedProducts({})}>Batalkan pilihan</Button>
            <Button variant="primary" onClick={() => openManualMapping(selectedRows)}>Atur SKU</Button>
          </div>
        </section>
      ) : null}

      {initial.rows.length === 0 ? (
        <EmptyState title="Tidak ada SKU pada filter ini." description="Ubah filter atau sinkronkan katalog dari Shopee." />
      ) : (
        <section className="workspace-panel overflow-hidden">
          <div className="overflow-auto">
            <table className="w-full min-w-[1240px] text-left text-[13px]">
              <thead className="sticky top-0 z-10 bg-slate-50/95 text-[10px] font-black uppercase tracking-[0.1em] text-slate-500 backdrop-blur">
                <tr>
                  <th className="w-10 px-3 py-3">
                    <input aria-label="Pilih semua SKU pada halaman ini" checked={selectedOnPage} onChange={toggleCurrentPage} type="checkbox" />
                  </th>
                  <th className="px-4 py-3">Produk</th>
                  <th className="px-4 py-3">SKU</th>
                  <th className="px-4 py-3">Stok</th>
                  <th className="px-4 py-3">Harga jual</th>
                  <th className="px-4 py-3">HPP &amp; Margin</th>
                  <th className="px-4 py-3">Status</th>
                  <th className="px-4 py-3">Update</th>
                  <th className="px-4 py-3">Tindakan</th>
                </tr>
              </thead>
              <tbody className="divide-y divide-slate-100">
                {initial.rows.map((row) => {
                  const key = `${row.shopId}:${row.itemId}:${row.modelId}`;
                  const shopName = initial.shops.find((shop) => shop.shopId === row.shopId)?.shopName || row.shopId;
                  const draftAwal = hppDraft[`${key}:awal`] ?? (row.hppAwal ?? row.hpp ?? "");
                  const draftFinal = hppDraft[`${key}:final`] ?? (row.hppFinal ?? row.hpp ?? "");
                  const originalAwal = String(row.hppAwal ?? row.hpp ?? "");
                  const originalFinal = String(row.hppFinal ?? row.hpp ?? "");
                  const hppDirty = String(draftAwal) !== originalAwal || String(draftFinal) !== originalFinal;
                  const finalHpp = Number(draftFinal || 0);
                  const unitMargin = row.price !== null && finalHpp > 0 ? row.price - finalHpp : null;
                  const marginRate = unitMargin !== null && row.price ? (unitMargin / row.price) * 100 : null;
                  return (
                    <tr key={row.id} className="align-middle hover:bg-slate-50/80">
                      <td className="px-3 py-3">
                        <input aria-label={`Pilih SKU ${row.platformSku || row.itemId}`} checked={Boolean(selectedProducts[productKey(row)])} onChange={() => toggleProduct(row)} type="checkbox" />
                      </td>
                      <td className="px-4 py-2.5">
                        <div className="flex min-w-[300px] gap-3">
                          {row.imageUrl ? (
                            // eslint-disable-next-line @next/next/no-img-element
                            <img className="h-12 w-12 shrink-0 rounded-[10px] border border-slate-200 object-cover" src={row.imageUrl} alt="" />
                          ) : (
                            <div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-[10px] border border-dashed border-slate-300 bg-slate-50 text-[10px] font-semibold text-slate-400">
                              No img
                            </div>
                          )}
                          <div className="min-w-0">
                            <div className="line-clamp-2 font-bold leading-5 text-slate-900">{row.itemName || "-"}</div>
                            <div className="truncate text-[11px] font-bold text-emerald-700">{row.modelName || "Tanpa variasi"}</div>
                            <div className="truncate text-[10px] font-semibold text-slate-500">{shopName}</div>
                            <div className="truncate text-[10px] text-slate-400">Item {row.itemId} · Model {row.modelId}</div>
                          </div>
                        </div>
                      </td>
                      <td className="px-4 py-3">
                        <div className="font-mono text-xs font-bold text-slate-700">{row.platformSku || "-"}</div>
                        <div className="mt-1 text-[10px] text-slate-400">Master SKU</div>
                        <div className="mt-0.5 text-xs font-semibold text-slate-600">{row.masterSku || "Belum dipetakan"}</div>
                      </td>
                      <td className="px-4 py-3">
                        <div className="font-bold text-slate-950">{row.availableStock ?? row.stock ?? "-"}</div>
                        <div className="text-xs text-slate-500">Reserved {row.reservedStock ?? 0}</div>
                      </td>
                      <td className="px-4 py-3">{formatMoney(row.price, row.currency)}</td>
                      <td className="px-4 py-3">
                        <div className="min-w-[260px] space-y-1.5">
                          <div className="grid grid-cols-[54px_88px_54px_88px] items-center gap-1.5">
                            <span className="text-[10px] font-bold uppercase text-slate-400">Awal</span>
                            <input className="w-full rounded-lg border border-slate-200 bg-white px-2 py-1.5 text-xs" inputMode="decimal" placeholder="0" value={String(draftAwal)} onChange={(event) => setHppDraft((state) => ({ ...state, [`${key}:awal`]: event.target.value }))} />
                            <span className="text-[10px] font-bold uppercase text-slate-400">Final</span>
                            <input className="w-full rounded-lg border border-slate-200 bg-white px-2 py-1.5 text-xs" inputMode="decimal" placeholder="0" value={String(draftFinal)} onChange={(event) => setHppDraft((state) => ({ ...state, [`${key}:final`]: event.target.value }))} />
                          </div>
                          <div className="flex items-center justify-between gap-2 text-[10px]">
                            <span className={unitMargin === null ? "text-slate-400" : unitMargin >= 0 ? "font-bold text-emerald-700" : "font-bold text-rose-700"}>
                              {unitMargin === null ? "Isi HPP final untuk margin" : `Margin/unit ${formatMoney(unitMargin, row.currency)} · ${marginRate?.toFixed(1)}%`}
                            </span>
                            {hppDirty ? <Button className="min-h-7 px-2 py-1 text-[10px]" loading={hppLoading[key]} onClick={() => saveHpp(row)}>Simpan HPP</Button> : null}
                          </div>
                        </div>
                      </td>
                      <td className="px-4 py-3"><Badge tone={statusTone(row.status)}>{row.status || "-"}</Badge></td>
                      <td className="px-4 py-3 text-xs text-slate-500">{formatDate(row.updateTime)}</td>
                      <td className="px-4 py-3">
                        <ActionMenu compact label="•••" items={[
                          { label: "Atur SKU Platform & Master", description: "Simpan SKU Shopee dan identitas internal", onClick: () => openManualMapping([row]) },
                          { label: "Edit produk & SKU", description: "Harga, stok, SKU, dan HPP", onClick: () => openEditor(row) },
                          { label: "Simpan perubahan HPP", description: hppDirty ? "Ada perubahan yang belum disimpan" : "Tidak ada perubahan HPP", disabled: !hppDirty, onClick: () => saveHpp(row) },
                        ]} />
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        </section>
      )}

      <div className="flex flex-wrap items-center justify-between gap-3 text-sm text-slate-500">
        <span>Menampilkan <b className="text-slate-950">{initial.rows.length}</b> dari <b className="text-slate-950">{initial.total}</b> SKU</span>
        <div className="flex items-center gap-2">
          {initial.page <= 1 ? <span className="ui-button ui-button-secondary cursor-not-allowed opacity-50">Sebelumnya</span> : <Link className="ui-button ui-button-secondary" href={productHref({ page: initial.page - 1 })}>Sebelumnya</Link>}
          <span className="font-semibold text-slate-700">Halaman {initial.page} / {pageCount}</span>
          {initial.page >= pageCount ? <span className="ui-button ui-button-secondary cursor-not-allowed opacity-50">Berikutnya</span> : <Link className="ui-button ui-button-secondary" href={productHref({ page: initial.page + 1 })}>Berikutnya</Link>}
        </div>
      </div>
      </> : <>
        <section className="workspace-panel p-3">
          <form
            action="/products"
            className="grid gap-2 sm:grid-cols-[1fr_auto_auto]"
            method="get"
            onSubmit={(event) => {
              event.preventDefault();
              updateQuery({ q: qInput, view: "master_sku", shop_id: initial.activeShopId || "all" });
            }}
          >
            <input name="shop_id" type="hidden" value={initial.activeShopId || "all"} />
            <input name="view" type="hidden" value="master_sku" />
            <input
              className="rounded-[10px] border border-slate-200 bg-white px-3 py-2 text-[13px]"
              name="q"
              value={qInput}
              onChange={(event) => setQInput(event.target.value)}
              placeholder="Cari Master SKU, SKU Shopee, produk, atau toko..."
            />
            <Button variant="primary" type="submit">Cari</Button>
            <Link className="ui-button ui-button-secondary" href={productHref({ q: null, view: "master_sku", shop_id: initial.activeShopId || "all", page: 1 })}>Reset</Link>
          </form>
        </section>
        <MasterSkuWorkspace
          groups={visibleMasterSkuGroups}
          mappedProducts={initial.stats.mapped}
          productMatches={initial.total}
          search={initial.q}
          onInspect={(group) => {
            setQInput(group.masterSku);
            updateQuery({ view: null, shop_id: initial.activeShopId || "all", filter: "mapped", q: group.masterSku, page: 1 });
          }}
          onStartMapping={() => {
            setQInput("");
            updateQuery({ view: null, shop_id: initial.activeShopId || "all", filter: "unmapped", q: null, page: 1 });
          }}
          onSearchProducts={() => updateQuery({ view: null, shop_id: initial.activeShopId || "all", filter: null, q: initial.q, page: 1 })}
        />
      </>}

      {manualMapping ? (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-slate-950/55 p-4">
          <div className="flex max-h-[92vh] w-full max-w-3xl flex-col overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-2xl dark:border-slate-700 dark:bg-slate-900">
            <div className="flex items-start justify-between gap-4 border-b border-slate-200 p-5 dark:border-slate-700">
              <div>
                <div className="text-xs font-bold uppercase tracking-wide text-emerald-700">Pemetaan manual lintas toko</div>
                <h2 className="mt-1 text-xl font-black text-slate-950 dark:text-white">Atur SKU Platform &amp; Master SKU untuk {manualMapping.rows.length} produk</h2>
                <p className="mt-1 text-sm text-slate-500">Master SKU adalah identitas internal. SKU Platform disimpan terpisah dan dapat dikirim ke Shopee bila diperlukan.</p>
              </div>
              <button className="rounded-lg px-3 py-2 text-sm font-bold text-slate-500 hover:bg-slate-100 dark:hover:bg-slate-800" onClick={() => setManualMapping(null)} type="button">Tutup</button>
            </div>

            <div className="overflow-y-auto p-5">
              <div className="grid gap-4 md:grid-cols-2">
                <label>
                  <span className="text-xs font-bold uppercase tracking-wide text-slate-500">SKU Platform</span>
                  <input
                    className="mt-2 w-full rounded-lg border border-slate-200 px-3 py-2.5 font-mono text-sm font-bold outline-none focus:border-emerald-500 dark:border-slate-700 dark:bg-slate-950"
                    maxLength={100}
                    placeholder="Contoh: TPL-MNGG"
                    value={manualMapping.platformSku}
                    onChange={(event) => setManualMapping((state) => (state ? { ...state, platformSku: event.target.value } : state))}
                  />
                  <span className="mt-1 block text-[11px] leading-4 text-slate-500">Kosongkan untuk mempertahankan SKU Platform masing-masing.</span>
                </label>
                <label>
                  <span className="text-xs font-bold uppercase tracking-wide text-slate-500">Master SKU baru</span>
                  <input
                    autoFocus
                    className="mt-2 w-full rounded-lg border border-slate-200 px-3 py-2.5 font-mono text-sm font-bold outline-none focus:border-emerald-500 dark:border-slate-700 dark:bg-slate-950"
                    maxLength={80}
                    placeholder="Contoh: LAMPU-PETROMAK"
                    value={manualMapping.masterSku}
                    onChange={(event) => setManualMapping((state) => (state ? { ...state, masterSku: event.target.value } : state))}
                  />
                </label>
                <label>
                  <span className="text-xs font-bold uppercase tracking-wide text-slate-500">HPP Awal untuk semua pilihan</span>
                  <input
                    className="mt-2 w-full rounded-lg border border-slate-200 px-3 py-2.5 text-sm outline-none focus:border-emerald-500 dark:border-slate-700 dark:bg-slate-950"
                    inputMode="decimal"
                    placeholder="Kosongkan jika tidak ingin mengubah"
                    value={manualMapping.hppAwal}
                    onChange={(event) => setManualMapping((state) => (state ? { ...state, hppAwal: event.target.value } : state))}
                  />
                </label>
                <label>
                  <span className="text-xs font-bold uppercase tracking-wide text-slate-500">HPP Final untuk semua pilihan</span>
                  <input
                    className="mt-2 w-full rounded-lg border border-slate-200 px-3 py-2.5 text-sm outline-none focus:border-emerald-500 dark:border-slate-700 dark:bg-slate-950"
                    inputMode="decimal"
                    placeholder="Kosongkan jika tidak ingin mengubah"
                    value={manualMapping.hppFinal}
                    onChange={(event) => setManualMapping((state) => (state ? { ...state, hppFinal: event.target.value } : state))}
                  />
                </label>
              </div>

              <label className={`mt-4 flex cursor-pointer items-start gap-3 rounded-xl border p-4 ${manualMapping.syncShopeeSku ? "border-amber-300 bg-amber-50" : "border-slate-200 bg-slate-50"}`}>
                <input
                  className="mt-0.5 h-4 w-4"
                  type="checkbox"
                  checked={manualMapping.syncShopeeSku}
                  onChange={(event) => setManualMapping((state) => (state ? { ...state, syncShopeeSku: event.target.checked } : state))}
                />
                <span>
                  <span className="block text-sm font-bold text-slate-950">Kirim SKU Platform ke Shopee</span>
                  <span className="mt-1 block text-xs leading-5 text-slate-600">Jika dicentang, Sellfin mengirim nilai kolom SKU Platform ke setiap produk/variasi Shopee yang dipilih. Master SKU tetap hanya menjadi identitas internal.</span>
                </span>
              </label>

              {manualMapping.syncShopeeSku ? (
                <div className="mt-3 rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-xs leading-5 text-amber-900">
                  Perubahan ini langsung memengaruhi Seller Centre. SKU lama seperti <b>{manualMapping.rows.slice(0, 3).map((row) => row.platformSku || "-").join(", ")}</b> akan diganti menjadi <b>{manualMapping.platformSku.trim() || "SKU Platform baru"}</b> setelah tombol konfirmasi ditekan.
                </div>
              ) : null}

              <div className="mt-5 overflow-hidden rounded-xl border border-slate-200 dark:border-slate-700">
                <div className="flex items-center justify-between bg-slate-50 px-4 py-2.5 text-xs font-bold text-slate-600 dark:bg-slate-950">
                  <span>Produk yang dipetakan</span>
                  <span>{manualMapping.rows.length} SKU · {new Set(manualMapping.rows.map((row) => row.shopId)).size} toko</span>
                </div>
                <div className="max-h-56 divide-y divide-slate-100 overflow-y-auto dark:divide-slate-800">
                  {manualMapping.rows.map((row) => {
                    const shopName = initial.shops.find((shop) => shop.shopId === row.shopId)?.shopName || row.shopId;
                    return <div className="grid grid-cols-[1fr_150px] gap-4 px-4 py-3 text-xs" key={productKey(row)}><div><div className="font-bold text-slate-900 dark:text-white">{row.itemName || row.itemId}</div><div className="mt-0.5 text-slate-500">{shopName} · {row.modelName || "Tanpa variasi"}</div></div><div className="text-right"><div className="font-mono font-bold text-slate-800 dark:text-slate-100">{row.platformSku || "Tanpa SKU"}</div><div className="mt-0.5 text-slate-400">{row.masterSku || "Belum dipetakan"}</div></div></div>;
                  })}
                </div>
              </div>
              {manualMapping.rows.length > 50 ? <div className="mt-3 text-sm font-semibold text-rose-700">Maksimal 50 SKU per proses. Kurangi pilihan terlebih dahulu.</div> : null}
            </div>

            <div className="flex flex-wrap items-center justify-between gap-3 border-t border-slate-200 bg-slate-50 p-4 dark:border-slate-700 dark:bg-slate-950">
              <span className="text-xs text-slate-500">HPP Awal menjadi catatan biaya awal; HPP Final dipakai untuk estimasi dan profit riil.</span>
              <div className="flex gap-2">
                <Button onClick={() => setManualMapping(null)}>Batal</Button>
                <Button variant="primary" loading={manualMapping.saving} disabled={!manualMapping.masterSku.trim() || (manualMapping.syncShopeeSku && !manualMapping.platformSku.trim()) || manualMapping.rows.length > 50} onClick={applyManualMapping}>
                  {manualMapping.syncShopeeSku ? "Petakan & update Shopee" : "Simpan pemetaan"}
                </Button>
              </div>
            </div>
          </div>
        </div>
      ) : null}

      {importOpen ? (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-slate-950/45 p-4">
          <div className="flex max-h-[92vh] w-full max-w-5xl flex-col overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-2xl dark:border-slate-700 dark:bg-slate-900">
            <div className="flex items-start justify-between gap-4 border-b border-slate-200 p-5 dark:border-slate-700">
              <div>
                <h2 className="text-lg font-bold text-slate-950 dark:text-white">Import Master SKU &amp; HPP</h2>
                <p className="mt-1 text-sm text-slate-500">Satu CSV dapat memetakan SKU yang sama di seluruh toko sekaligus.</p>
              </div>
              <button className="rounded-lg px-3 py-2 text-sm font-semibold text-slate-500 hover:bg-slate-100 dark:hover:bg-slate-800" onClick={() => setImportOpen(false)} type="button">Tutup</button>
            </div>

            <div className="overflow-y-auto p-5">
              <div className="grid gap-3 md:grid-cols-[260px_1fr_auto] md:items-end">
                <label>
                  <span className="muted-label">Cakupan pemetaan</span>
                  <select className="mt-2 w-full rounded-lg border border-slate-200 bg-white px-3 py-2.5 text-sm dark:border-slate-700 dark:bg-slate-950" value={importShopId} onChange={(event) => { setImportShopId(event.target.value); setImportPreview(null); }}>
                    <option value="all">Semua toko · otomatis berdasarkan SKU</option>
                    {initial.shops.map((shop) => <option key={shop.shopId} value={shop.shopId}>{shop.shopName || shop.shopId}</option>)}
                  </select>
                </label>
                <label>
                  <span className="muted-label">Berkas CSV</span>
                  <input className="mt-2 block w-full rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm file:mr-3 file:rounded-md file:border-0 file:bg-slate-100 file:px-3 file:py-1 file:font-semibold dark:border-slate-700 dark:bg-slate-950" type="file" accept=".csv,text/csv" onChange={(event) => { setImportFile(event.target.files?.[0] || null); setImportPreview(null); }} />
                </label>
                <Link className="ui-button ui-button-secondary" href="/api/shopee/products/master-sku-import">Unduh template</Link>
              </div>

              <div className="mt-4 rounded-xl border border-slate-200 bg-slate-50 p-3 text-xs leading-5 text-slate-600 dark:border-slate-700 dark:bg-slate-950 dark:text-slate-300">
                SKU Shopee yang sama pada beberapa toko akan mendapat <b>Master SKU dan HPP yang sama</b>. HPP Awal diambil dari harga beli supplier; HPP Final = harga beli + ongkir unit + kemasan unit + biaya admin. Format lama dengan kolom <b>Kode SKU</b> tetap didukung.
              </div>

              {importPreview ? (
                <div className="mt-4 space-y-3">
                  <div className="grid gap-2 sm:grid-cols-3 lg:grid-cols-6">
                    <ImportCount label="Baris CSV" value={importPreview.summary.total} />
                    <ImportCount label="Baris cocok" value={importPreview.summary.matched} tone="success" />
                    <ImportCount label="Produk cocok" value={importPreview.summary.products} tone="success" />
                    <ImportCount label="Toko tercakup" value={importPreview.summary.shops} tone="success" />
                    <ImportCount label="Siap diubah" value={importPreview.summary.ready} tone="success" />
                    <ImportCount label="Tidak cocok" value={importPreview.summary.unmatched} tone="warning" />
                  </div>
                  {importPreview.summary.ambiguous + importPreview.summary.invalid + importPreview.summary.duplicate > 0 ? <div className="text-xs font-semibold text-amber-700">Perlu diperiksa: {importPreview.summary.ambiguous} nama ambigu, {importPreview.summary.invalid} baris tidak valid, dan {importPreview.summary.duplicate} baris duplikat.</div> : null}
                  <div className="overflow-x-auto rounded-xl border border-slate-200 dark:border-slate-700">
                    <table className="w-full min-w-[1040px] text-left text-xs">
                      <thead className="bg-slate-50 text-[10px] uppercase tracking-wide text-slate-500 dark:bg-slate-950"><tr><th className="px-3 py-2.5">Baris</th><th className="px-3 py-2.5">Sumber CSV</th><th className="px-3 py-2.5">Pemetaan lintas toko</th><th className="px-3 py-2.5">Master SKU</th><th className="px-3 py-2.5 text-right">HPP Awal</th><th className="px-3 py-2.5 text-right">HPP Final</th><th className="px-3 py-2.5">Hasil</th></tr></thead>
                      <tbody className="divide-y divide-slate-100 dark:divide-slate-800">
                        {importPreview.rows.map((row) => {
                          const firstProduct = row.products[0];
                          const shopNames = [...new Set(row.products.map((product) => product.shopName))];
                          return <tr key={row.line} className="align-top"><td className="px-3 py-3 font-mono">{row.line}</td><td className="max-w-[220px] px-3 py-3"><div className="font-mono font-semibold">{row.sourceSku || "-"}</div><div className="mt-1 line-clamp-2 text-slate-500">{row.sourceName || "-"}</div></td><td className="max-w-[320px] px-3 py-3"><div className="line-clamp-2 font-semibold text-slate-900 dark:text-white">{firstProduct?.itemName || "-"}</div>{row.targetCount ? <><div className="mt-1 font-semibold text-emerald-700">{row.targetCount} produk · {row.shopCount} toko · {row.changeCount} berubah</div><div className="mt-1 line-clamp-2 text-[10px] text-slate-500">{shopNames.join(", ")}{row.targetCount > row.products.length ? ` +${row.targetCount - row.products.length} produk` : ""}</div></> : null}</td><td className="px-3 py-3 font-mono">{row.nextMasterSku || "-"}</td><td className="px-3 py-3 text-right">{row.hppAwal === null || row.hppAwal === undefined ? "-" : formatMoney(row.hppAwal, "IDR")}</td><td className="px-3 py-3 text-right">{row.hppFinal === null || row.hppFinal === undefined ? "-" : formatMoney(row.hppFinal, "IDR")}</td><td className="px-3 py-3"><Badge tone={importTone(row.status)}>{importStatus(row.status)}</Badge><div className="mt-1 max-w-[220px] leading-4 text-slate-500">{row.reason}</div></td></tr>;
                        })}
                      </tbody>
                    </table>
                  </div>
                  {importPreview.summary.total > importPreview.rows.length ? <div className="text-xs text-slate-500">Menampilkan 100 baris pertama dari {importPreview.summary.total} baris.</div> : null}
                </div>
              ) : null}
            </div>

            <div className="flex flex-wrap justify-end gap-2 border-t border-slate-200 bg-slate-50 p-4 dark:border-slate-700 dark:bg-slate-950">
              <Button onClick={() => setImportOpen(false)}>Batal</Button>
              <Button loading={importLoading} disabled={!importFile || !importShopId} onClick={() => runImport("preview")}>Pratinjau semua toko</Button>
              <Button variant="primary" loading={importLoading} disabled={!importPreview || importPreview.summary.ready === 0} onClick={() => runImport("apply")}>Terapkan {importPreview?.summary.ready || 0} produk</Button>
            </div>
          </div>
        </div>
      ) : null}

      {edit ? (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-slate-950/45 p-4">
          <div className="w-full max-w-2xl rounded-2xl border border-slate-200 bg-white shadow-2xl">
            <div className="flex items-start justify-between gap-4 border-b border-slate-200 p-5">
              <div>
                <div className="text-xs font-bold uppercase tracking-wide text-teal-700">Edit produk</div>
                <h2 className="mt-1 text-xl font-black text-slate-950">{edit.row.platformSku || edit.row.itemId}</h2>
                <p className="mt-1 text-sm text-slate-500">Sellfin menyimpan Master SKU dan HPP. Data live hanya dikirim ke Shopee bila benar-benar berubah.</p>
              </div>
              <button className="rounded-lg px-3 py-2 text-sm font-bold text-slate-500 hover:bg-slate-100" onClick={() => setEdit(null)} type="button">
                Tutup
              </button>
            </div>
            <div className="grid gap-4 p-5 md:grid-cols-2">
              <div className="grid gap-2 rounded-xl border border-slate-200 bg-slate-50 p-3 text-xs md:col-span-2 md:grid-cols-2">
                <div>
                  <div className="font-black uppercase tracking-wide text-slate-500">Disimpan di Sellfin</div>
                  <div className="mt-1 font-semibold text-slate-900">{currentEditPlan?.localChanges.join(", ") || "Tidak ada perubahan lokal"}</div>
                </div>
                <div>
                  <div className="font-black uppercase tracking-wide text-slate-500">Dikirim ke Shopee</div>
                  <div className="mt-1 font-semibold text-slate-900">{currentEditPlan?.shopeeChanges.join(", ") || "Tidak ada perubahan live"}</div>
                </div>
              </div>
              <label className="md:col-span-2">
                <span className="text-xs font-bold uppercase tracking-wide text-slate-500">Judul produk</span>
                <textarea
                  className="mt-2 min-h-24 w-full rounded-lg border border-slate-200 px-3 py-2 text-sm"
                  value={edit.itemName}
                  onChange={(event) => setEdit((state) => (state ? { ...state, itemName: event.target.value, feedback: null } : state))}
                />
              </label>
              <label>
                <span className="text-xs font-bold uppercase tracking-wide text-slate-500">SKU platform</span>
                <input
                  className="mt-2 w-full rounded-lg border border-slate-200 px-3 py-2 text-sm"
                  value={edit.platformSku}
                  onChange={(event) => setEdit((state) => (state ? { ...state, platformSku: event.target.value, feedback: null } : state))}
                />
              </label>
              <label>
                <span className="text-xs font-bold uppercase tracking-wide text-slate-500">Master SKU (lokal)</span>
                <input
                  className="mt-2 w-full rounded-lg border border-slate-200 px-3 py-2 text-sm"
                  value={edit.masterSku}
                  onChange={(event) => setEdit((state) => (state ? { ...state, masterSku: event.target.value, feedback: null } : state))}
                />
              </label>
              <label>
                <span className="text-xs font-bold uppercase tracking-wide text-slate-500">Harga Shopee</span>
                <input
                  className="mt-2 w-full rounded-lg border border-slate-200 px-3 py-2 text-sm"
                  inputMode="decimal"
                  value={edit.price}
                  onChange={(event) => setEdit((state) => (state ? { ...state, price: event.target.value, feedback: null } : state))}
                />
              </label>
              <label>
                <span className="text-xs font-bold uppercase tracking-wide text-slate-500">Stok tersedia</span>
                <input
                  className="mt-2 w-full rounded-lg border border-slate-200 px-3 py-2 text-sm"
                  inputMode="numeric"
                  value={edit.stock}
                  onChange={(event) => setEdit((state) => (state ? { ...state, stock: event.target.value, feedback: null } : state))}
                />
              </label>
              <label>
                <span className="text-xs font-bold uppercase tracking-wide text-slate-500">HPP Awal</span>
                <input
                  className="mt-2 w-full rounded-lg border border-slate-200 px-3 py-2 text-sm"
                  inputMode="decimal"
                  value={edit.hppAwal}
                  onChange={(event) => setEdit((state) => (state ? { ...state, hppAwal: event.target.value, feedback: null } : state))}
                />
              </label>
              <label>
                <span className="text-xs font-bold uppercase tracking-wide text-slate-500">HPP Final + packing</span>
                <input
                  className="mt-2 w-full rounded-lg border border-slate-200 px-3 py-2 text-sm"
                  inputMode="decimal"
                  value={edit.hppFinal}
                  onChange={(event) => setEdit((state) => (state ? { ...state, hppFinal: event.target.value, feedback: null } : state))}
                />
              </label>
              {edit.feedback ? (
                <div className={`rounded-xl border p-3 text-sm md:col-span-2 ${edit.feedback.tone === "error" ? "border-rose-200 bg-rose-50 text-rose-900" : "border-sky-200 bg-sky-50 text-sky-900"}`} role="status">
                  <div className="font-black">{edit.feedback.title}</div>
                  <div className="mt-1 leading-5">{edit.feedback.description}</div>
                </div>
              ) : null}
            </div>
            <div className="flex flex-wrap items-center justify-between gap-3 border-t border-slate-200 bg-slate-50 p-4">
              <div className="text-xs font-semibold text-slate-500">
                {currentEditPlan?.hasChanges ? `${currentEditPlan.localChanges.length + currentEditPlan.shopeeChanges.length} perubahan siap disimpan` : "Data sudah sama dengan catatan Sellfin"}
              </div>
              <div className="flex gap-2">
              <Button onClick={() => setEdit(null)}>Batal</Button>
              <Button variant="primary" loading={edit.saving} disabled={!currentEditPlan?.hasChanges} onClick={saveShopeeEdit}>
                {currentEditPlan?.shopeeChanges.length
                  ? currentEditPlan.localChanges.length ? "Simpan Sellfin & Shopee" : "Simpan ke Shopee"
                  : currentEditPlan?.localChanges.length ? "Simpan ke Sellfin" : "Tidak ada perubahan"}
              </Button>
              </div>
            </div>
          </div>
        </div>
      ) : null}
    </div>
  );
}

function CatalogWorkspaceTab({ label, value, active, href, tone = "neutral" }: { label: string; value: number; active: boolean; href: string; tone?: "neutral" | "warning" }) {
  return (
    <Link
      aria-current={active ? "page" : undefined}
      className={`flex min-h-10 items-center gap-2 rounded-lg px-3.5 text-sm font-bold transition ${active ? "bg-slate-950 text-white shadow-sm dark:bg-white dark:text-slate-950" : "text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800"}`}
      href={href}
    >
      <span>{label}</span>
      <span className={`rounded-md px-1.5 py-0.5 text-xs ${active ? "bg-white/15 text-white dark:bg-slate-950/10 dark:text-slate-950" : tone === "warning" ? "bg-amber-100 text-amber-800" : "bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-200"}`}>{value.toLocaleString("id-ID")}</span>
    </Link>
  );
}

function MasterSkuWorkspace({ groups, mappedProducts, productMatches, search, onInspect, onStartMapping, onSearchProducts }: { groups: MasterSkuGroup[]; mappedProducts: number; productMatches: number; search: string | null; onInspect: (group: MasterSkuGroup) => void; onStartMapping: () => void; onSearchProducts: () => void }) {
  return (
    <section className="workspace-panel overflow-hidden">
      <div className="flex flex-wrap items-center justify-between gap-3 border-b border-slate-200 px-4 py-3 dark:border-slate-800">
        <div>
          <h2 className="text-base font-black text-slate-950 dark:text-white">Daftar Master SKU</h2>
          <p className="mt-0.5 text-xs text-slate-500">{groups.length} Master SKU ditampilkan · {mappedProducts} produk toko sudah dipetakan secara global.</p>
        </div>
        <Button variant="primary" onClick={onStartMapping}>Petakan produk manual</Button>
      </div>
      {groups.length === 0 ? (
        <div className="space-y-3 p-5">
          <EmptyState
            title="Master SKU tidak ditemukan."
            description={search && productMatches ? `${productMatches} produk toko cocok dengan pencarian ini, tetapi belum tergabung dalam daftar Master SKU.` : "Ubah pencarian atau mulai petakan produk secara manual."}
          />
          {search && productMatches ? <div className="flex justify-center"><Button variant="primary" onClick={onSearchProducts}>Buka &amp; petakan {productMatches} produk</Button></div> : null}
        </div>
      ) : (
        <div className="overflow-auto">
          <table className="w-full min-w-[1080px] text-left text-[13px]">
            <thead className="bg-slate-50 text-[10px] font-black uppercase tracking-[0.1em] text-slate-500 dark:bg-slate-950">
              <tr><th className="px-4 py-3">Master SKU</th><th className="px-4 py-3">Produk terhubung</th><th className="px-4 py-3">SKU Shopee saat ini</th><th className="px-4 py-3">Toko</th><th className="px-4 py-3 text-right">HPP Final</th><th className="px-4 py-3">Kesiapan</th><th className="px-4 py-3">Tindakan</th></tr>
            </thead>
            <tbody className="divide-y divide-slate-100 dark:divide-slate-800">
              {groups.map((group) => (
                <tr className="align-middle hover:bg-slate-50/80 dark:hover:bg-slate-800/40" key={group.masterSku}>
                  <td className="px-4 py-3"><div className="font-mono text-sm font-black text-slate-950 dark:text-white">{group.masterSku}</div><div className="mt-1 text-[10px] text-slate-400">Identitas global Sellfin</div></td>
                  <td className="px-4 py-3">
                    <div className="flex min-w-[290px] items-center gap-3">
                      {group.imageUrl ? (
                        // eslint-disable-next-line @next/next/no-img-element
                        <img alt="" className="h-10 w-10 shrink-0 rounded-lg border border-slate-200 object-cover" src={group.imageUrl} />
                      ) : <div className="h-10 w-10 shrink-0 rounded-lg border border-dashed border-slate-300" />}
                      <div><div className="line-clamp-2 font-bold text-slate-900 dark:text-white">{group.productNames[0] || "Produk tanpa nama"}</div><div className="mt-0.5 text-xs text-slate-500">{group.productCount} produk/variasi{group.productNames.length > 1 ? ` · ${group.productNames.length} judul` : ""}</div></div>
                    </div>
                  </td>
                  <td className="px-4 py-3"><div className="flex max-w-[260px] flex-wrap gap-1">{group.platformSkus.slice(0, 4).map((sku) => <span className={`rounded-md px-1.5 py-1 font-mono text-[10px] font-bold ${sku === group.masterSku ? "bg-emerald-50 text-emerald-700" : "bg-amber-50 text-amber-800"}`} key={sku}>{sku}</span>)}{group.platformSkus.length > 4 ? <span className="rounded-md bg-slate-100 px-1.5 py-1 text-[10px] font-bold text-slate-500">+{group.platformSkus.length - 4}</span> : null}</div></td>
                  <td className="px-4 py-3"><div className="font-bold text-slate-800 dark:text-slate-100">{group.shopCount} toko</div><div className="mt-0.5 max-w-[200px] truncate text-xs text-slate-500">{group.shopNames.join(", ")}</div></td>
                  <td className="px-4 py-3 text-right font-bold">{group.hppFinalMixed ? "Beragam" : formatMoney(group.hppFinal, "IDR")}</td>
                  <td className="px-4 py-3"><Badge tone={group.missingHppFinal ? "warning" : "success"}>{group.missingHppFinal ? `${group.missingHppFinal} HPP Final kosong` : "Siap dihitung"}</Badge></td>
                  <td className="px-4 py-3"><Button onClick={() => onInspect(group)}>Lihat produk</Button></td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </section>
  );
}

function CatalogFilter({ label, value, active, href, tone = "neutral" }: { label: string; value: number; active: boolean; href: string; tone?: "neutral" | "success" | "warning" | "danger" }) {
  const valueTone = tone === "success" ? "text-emerald-700" : tone === "warning" ? "text-amber-700" : tone === "danger" ? "text-rose-700" : "text-slate-800";
  return (
    <Link aria-current={active ? "page" : undefined} className={`flex min-h-9 items-center gap-2 rounded-lg px-3 text-xs font-semibold transition ${active ? "bg-slate-900 text-white shadow-sm dark:bg-white dark:text-slate-950" : "text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800"}`} href={href}>
      <span>{label}</span><span className={`rounded-md px-1.5 py-0.5 font-bold ${active ? "bg-white/15 text-white dark:bg-slate-950/10 dark:text-slate-950" : `bg-slate-100 ${valueTone} dark:bg-slate-800`}`}>{value.toLocaleString("id-ID")}</span>
    </Link>
  );
}

function ImportCount({ label, value, tone = "neutral" }: { label: string; value: number; tone?: "neutral" | "success" | "warning" | "danger" }) {
  const color = tone === "success" ? "text-emerald-700" : tone === "warning" ? "text-amber-700" : tone === "danger" ? "text-rose-700" : "text-slate-900";
  return <div className="rounded-lg border border-slate-200 bg-white px-3 py-2 dark:border-slate-700 dark:bg-slate-900"><div className={`text-lg font-bold ${color}`}>{value}</div><div className="text-[10px] font-semibold uppercase tracking-wide text-slate-400">{label}</div></div>;
}

function importTone(status: ImportPreviewRow["status"]) {
  if (status === "matched") return "success" as const;
  if (status === "unmatched" || status === "ambiguous") return "warning" as const;
  return "danger" as const;
}

function importStatus(status: ImportPreviewRow["status"]) {
  if (status === "matched") return "Cocok";
  if (status === "unmatched") return "Tidak cocok";
  if (status === "ambiguous") return "Ganda";
  if (status === "duplicate") return "Duplikat";
  return "Tidak valid";
}
