import { useEffect, useMemo, useRef, useState, useTransition, useCallback, memo } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Code } from "@/components/ui/code";
import * as XLSX from "xlsx";
import { toast } from "sonner";
import { X as XIcon } from "lucide-react";
import { useQuery } from "@tanstack/react-query";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Checkbox } from "@/components/ui/checkbox";

export interface CategorySummary {
  category: string;
  categoryPath: string;
  avgTotalSold: number;
  minSold: number;
  maxSold: number;
}

type ParsedRow = {
  product: string;
  sku?: string;
  categoryHint?: string;
  moq?: number;
  upc?: string;
};

type OrderRow = ParsedRow & {
  matchedCategoryPath?: string;
  matchedCategoryName?: string;
  baseSuggestion?: number; // from category avg
  finalQty: number; // adjusted for MOQ
};

type HeaderMap = {
  // Stores generated column ids like "col-0" instead of raw header text
  product?: string;
  sku?: string;
  categoryHint?: string;
  moq?: string;
  upc?: string;
};

const PRODUCT_HEADER_SYNONYMS = [
  "product",
  "name",
  "title",
  "description",
  "item",
  "item name",
  "sku description",
  "product name",
];

const SKU_HEADER_SYNONYMS = [
  "sku",
  "item#",
  "item number",
  "supplier #",
  "supplier no",
  "supplier_no",
  "product code",
];

const CATEGORY_HEADER_SYNONYMS = [
  "category",
  "categories",
  "line",
  "collection",
  "type",
];

const MOQ_HEADER_SYNONYMS = [
  "moq",
  "min qty",
  "min. order qty",
  "min order qty",
  "qty per unit",
  "unit qty",
  "inner pack",
  "case pack",
  "pack",
];

const UPC_HEADER_SYNONYMS = [
  "upc",
  "barcode",
  "bar code",
  "ean",
  "jan",
  "upc code",
];

function normalizeHeader(h: string) {
  return h.trim().toLowerCase();
}

function autoMapHeaderNames(headers: string[]): { product?: string; sku?: string; categoryHint?: string; moq?: string; upc?: string } {
  const norm = headers.map((h) => normalizeHeader(h));
  const findFirst = (syns: string[]) => {
    for (const s of syns) {
      const idx = norm.findIndex((h) => h === s || h.includes(s));
      if (idx >= 0) return headers[idx];
    }
    return undefined;
  };
  return {
    product: findFirst(PRODUCT_HEADER_SYNONYMS) || headers[0],
    sku: findFirst(SKU_HEADER_SYNONYMS),
    categoryHint: findFirst(CATEGORY_HEADER_SYNONYMS),
    moq: findFirst(MOQ_HEADER_SYNONYMS),
    upc: findFirst(UPC_HEADER_SYNONYMS),
  };
}

function detectDelimiter(text: string): string {
  // Very simple heuristic: prefer tab, then comma, then semicolon
  const lines = text.split(/\r?\n/).slice(0, 5);
  const counts = { "\t": 0, ",": 0, ";": 0 } as Record<string, number>;
  for (const line of lines) {
    counts["\t"] += (line.match(/\t/g) || []).length;
    counts[","] += (line.match(/,/g) || []).length;
    counts[";"] += (line.match(/;/g) || []).length;
  }
  return Object.entries(counts).sort((a, b) => b[1] - a[1])[0][0];
}

function parsePasted(text: string): { headers: string[]; rows: string[][] } {
  const delimiter = detectDelimiter(text);
  const lines = text
    .split(/\r?\n/)
    .map((l) => l.trim())
    .filter(Boolean);
  if (lines.length === 0) return { headers: [], rows: [] };
  const headers = lines[0].split(delimiter).map((s) => s.trim());
  const rows = lines.slice(1).map((l) => {
    const parts = l.split(delimiter).map((s) => s.trim());
    // Preserve empty trailing columns by padding to headers length
    while (parts.length < headers.length) parts.push("");
    return parts;
  });
  return { headers, rows };
}

function toIntOrUndefined(v: any): number | undefined {
  if (v === null || v === undefined) return undefined;
  const n = Number(String(v).replace(/[^0-9.-]/g, ""));
  return Number.isFinite(n) && n > 0 ? Math.round(n) : undefined;
}

function scoreCategoryMatch(catText: string, name: string, hint?: string): number {
  const base = catText.toLowerCase();
  const tokens = (name || "")
    .toLowerCase()
    .split(/[^a-z0-9]+/)
    .filter((t) => t.length >= 3);
  let score = 0;
  for (const t of tokens) {
    if (base.includes(t)) score += 2;
  }
  if (hint) {
    const h = hint.toLowerCase();
    if (base.includes(h)) score += 5;
  }
  return score;
}

function suggestFromCategory(avgTotalSold?: number, scalePct: number = 100): number {
  const scaled = (avgTotalSold || 0) * (isFinite(scalePct) ? scalePct : 100) / 100;
  const base = Math.max(1, Math.round(scaled));
  return base;
}

function applyMOQ(qty: number, moq?: number): number {
  if (!moq || moq <= 1) return Math.max(0, qty);
  if (qty <= 0) return 0;
  const mult = Math.ceil(qty / moq);
  return mult * moq;
}

export function QuickOrderBuilder({
  categories,
  brand,
}: {
  categories: CategorySummary[];
  brand?: string;
}) {
  const fileInputRef = useRef<HTMLInputElement | null>(null);

  const [pasted, setPasted] = useState("");
  const [headers, setHeaders] = useState<string[]>([]);
  const [rawRows, setRawRows] = useState<string[][]>([]);
  const [headerMap, setHeaderMap] = useState<HeaderMap>({});
  const [orderRows, setOrderRows] = useState<OrderRow[]>([]);
  const [showJson, setShowJson] = useState(false);
  const [selectedSupplierId, setSelectedSupplierId] = useState<string | undefined>(undefined);
  const [scalePct, setScalePct] = useState<number>(100);
  const [scaleInput, setScaleInput] = useState<string>("100");
  const [showExcludedOnly, setShowExcludedOnly] = useState<boolean>(false);
  const [parsed, setParsed] = useState<boolean>(false);
  const [showMapping, setShowMapping] = useState<boolean>(false);
  const [isPending, startTransition] = useTransition();
  const [initialCategories, setInitialCategories] = useState<CategorySummary[] | null>(null);

  // Local storage draft persistence
  const DRAFT_KEY = "quickOrderBuilderDraft";
  const restoringRef = useRef(false);

  // Load suppliers from existing endpoint used elsewhere in the app
  const { data: fieldOptions } = useQuery({
    queryKey: ["field-options"],
    queryFn: async () => {
      const res = await fetch("/api/import/field-options");
      if (!res.ok) throw new Error("Failed to load field options");
      return res.json();
    },
  });
  const supplierOptions: { label: string; value: string }[] = fieldOptions?.suppliers || [];

  // Default supplier to the brand name if an exact label match exists
  useEffect(() => {
    if (!supplierOptions?.length) return;
    if (selectedSupplierId) return;
    if (brand) {
      const match = supplierOptions.find((s) => s.label?.toLowerCase?.() === brand.toLowerCase());
      if (match) setSelectedSupplierId(String(match.value));
    }
  }, [supplierOptions, brand, selectedSupplierId]);

  // Restore draft on mount
  useEffect(() => {
    try {
      const raw = localStorage.getItem(DRAFT_KEY);
      if (!raw) return;
      const draft = JSON.parse(raw);
      restoringRef.current = true;
      setPasted(draft.pasted ?? "");
      setHeaders(Array.isArray(draft.headers) ? draft.headers : []);
      setRawRows(Array.isArray(draft.rawRows) ? draft.rawRows : []);
      setHeaderMap(draft.headerMap ?? {});
      setOrderRows(Array.isArray(draft.orderRows) ? draft.orderRows : []);
      setSelectedSupplierId(draft.selectedSupplierId ?? undefined);
      const restoredScale = typeof draft.scalePct === 'number' ? draft.scalePct : 100;
      setScalePct(restoredScale);
      setScaleInput(String(restoredScale));
      setParsed(Array.isArray(draft.headers) && draft.headers.length > 0);
      setShowMapping(!(Array.isArray(draft.orderRows) && draft.orderRows.length > 0));
      if (Array.isArray(draft.categoriesSnapshot)) {
        setInitialCategories(draft.categoriesSnapshot);
      }
      // brand is passed via props; we don't override it here
    } catch (e) {
      console.warn("Failed to restore draft", e);
    } finally {
      // Defer toggling off to next tick to allow state batching
      setTimeout(() => { restoringRef.current = false; }, 0);
    }
  }, []);

  // Save draft on changes
  useEffect(() => {
    if (restoringRef.current) return;
    const draft = {
      pasted,
      headers,
      rawRows,
      headerMap,
      orderRows,
      selectedSupplierId,
      scalePct,
      brand,
      categoriesSnapshot: categories,
    };
    try {
      localStorage.setItem(DRAFT_KEY, JSON.stringify(draft));
    } catch (e) {
      // ignore storage quota errors silently
    }
  }, [pasted, headers, rawRows, headerMap, orderRows, selectedSupplierId, scalePct, brand]);

  // Debounce scale input -> numeric scalePct
  useEffect(() => {
    const handle = setTimeout(() => {
      const v = Math.max(1, Math.min(500, Math.round(Number(scaleInput) || 0)));
      setScalePct(v);
    }, 500);
    return () => clearTimeout(handle);
  }, [scaleInput]);

  const effectiveCategories = (categories && categories.length > 0) ? categories : (initialCategories || []);

  const categoryOptions = useMemo(() => {
    const arr = (effectiveCategories || [])
      .map((c) => ({
        value: c.categoryPath || c.category,
        label: c.categoryPath ? `${c.category} — ${c.categoryPath}` : c.category,
      }))
      .filter((o) => !!o.value && String(o.value).trim() !== "");
    // dedupe by value to avoid duplicate Select values
    const dedup = new Map<string, string>();
    for (const o of arr) {
      if (!dedup.has(o.value)) dedup.set(o.value, o.label);
    }
    return Array.from(dedup.entries()).map(([value, label]) => ({ value, label }));
  }, [effectiveCategories]);

  const categoryByKey = useMemo(() => {
    const map = new Map<string, CategorySummary>();
    for (const c of effectiveCategories || []) {
      map.set(c.categoryPath || c.category, c);
    }
    return map;
  }, [effectiveCategories]);

  // Build header option list with generated ids so values are never empty and keys are unique
  const headerOptions = useMemo(
    () => headers.map((h, i) => ({ id: `col-${i}`, index: i, label: h && h.trim() ? h : `Column ${i + 1}` })),
    [headers]
  );
  const idToIndex = useMemo(() => new Map(headerOptions.map((o) => [o.id, o.index])), [headerOptions]);

  function headerNameToId(name?: string): string | undefined {
    if (!name) return undefined;
    const idx = headers.findIndex((h) => h === name);
    return idx >= 0 ? `col-${idx}` : undefined;
  }

  function handleFileChange(e: React.ChangeEvent<HTMLInputElement>) {
    const f = e.target.files?.[0];
    if (!f) return;
    const reader = new FileReader();
    const ext = f.name.split(".").pop()?.toLowerCase();

    reader.onload = () => {
      try {
        let wb: XLSX.WorkBook | null = null;
        if (ext === "xlsx" || ext === "xls") {
          const data = new Uint8Array(reader.result as ArrayBuffer);
          wb = XLSX.read(data, { type: "array" });
        } else if (ext === "csv" || ext === "tsv") {
          const text = reader.result as string;
          wb = XLSX.read(text, { type: "string" });
        } else {
          // Try naive string read
          const text = reader.result as string;
          wb = XLSX.read(text, { type: "string" });
        }
        if (!wb) throw new Error("Unable to parse file");
        const sheet = wb.Sheets[wb.SheetNames[0]];
        const rows: any[][] = XLSX.utils.sheet_to_json(sheet, { header: 1, raw: false, defval: "" });
        if (!rows.length) throw new Error("Empty file");
        const hdrs = (rows[0] as string[]).map((h) => String(h || "").trim());
        const body = rows.slice(1).map((r) => (r as any[]).map((v) => String(v ?? "").trim()));
        // Build mapping based on detected names -> ids
        const mappedNames = autoMapHeaderNames(hdrs);
        const mappedIds: HeaderMap = {
          product: headerNameToId(mappedNames.product) ?? (hdrs.length > 0 ? `col-0` : undefined),
          sku: headerNameToId(mappedNames.sku),
          categoryHint: headerNameToId(mappedNames.categoryHint),
          moq: headerNameToId(mappedNames.moq),
          upc: headerNameToId(mappedNames.upc),
        };
        setHeaders(hdrs);
        setRawRows(body);
        setHeaderMap(mappedIds);
        setPasted("");
        setParsed(true);
        setShowMapping(true);
        toast.success("File parsed");
      } catch (err) {
        console.error(err);
        toast.error("Could not parse file");
      }
    };

    if (ext === "xlsx" || ext === "xls") {
      reader.readAsArrayBuffer(f);
    } else {
      reader.readAsText(f);
    }
  }

  function handlePasteParse() {
    try {
      const { headers: hdrs, rows } = parsePasted(pasted);
      if (!hdrs.length || !rows.length) {
        toast.error("No data detected");
        return;
      }
      const mappedNames = autoMapHeaderNames(hdrs);
      const mappedIds: HeaderMap = {
        product: headerNameToId(mappedNames.product) ?? (hdrs.length > 0 ? `col-0` : undefined),
        sku: headerNameToId(mappedNames.sku),
        categoryHint: headerNameToId(mappedNames.categoryHint),
        moq: headerNameToId(mappedNames.moq),
        upc: headerNameToId(mappedNames.upc),
      };
      setHeaders(hdrs);
      setRawRows(rows);
      setHeaderMap(mappedIds);
      setParsed(true);
      setShowMapping(true);
      toast.success("Pasted data parsed");
    } catch (e) {
      console.error(e);
      toast.error("Paste parse failed");
    }
  }

  function buildParsedRows(): ParsedRow[] {
    if (!headers.length || !rawRows.length) return [];
    const idx = (id?: string) => (id ? idToIndex.get(id) ?? -1 : -1);
    const iProduct = idx(headerMap.product);
    const iSku = idx(headerMap.sku);
    const iCat = idx(headerMap.categoryHint);
    const iMoq = idx(headerMap.moq);
    const iUpc = idx(headerMap.upc);
    const out: ParsedRow[] = [];
    for (const r of rawRows) {
      const product = String(iProduct >= 0 ? r[iProduct] ?? "" : "").trim();
      const upc = iUpc >= 0 ? String(r[iUpc] ?? "") : undefined;
      if (!product && !(upc && upc.trim())) continue;
      const sku = iSku >= 0 ? String(r[iSku] ?? "") : undefined;
      const categoryHint = iCat >= 0 ? String(r[iCat] ?? "") : undefined;
      const moq = iMoq >= 0 ? toIntOrUndefined(r[iMoq]) : undefined;
      out.push({ product, sku, categoryHint, moq, upc });
    }
    return out;
  }

  function matchCategory(row: ParsedRow): { key?: string; name?: string } {
    if (!categories?.length) return {};
    let bestKey: string | undefined;
    let bestName: string | undefined;
    let bestScore = -1;
    for (const c of categories) {
      const key = c.categoryPath || c.category;
      const text = `${c.category} ${c.categoryPath || ""}`;
      const s = scoreCategoryMatch(text, row.product, row.categoryHint);
      if (s > bestScore) {
        bestScore = s;
        bestKey = key;
        bestName = c.category;
      }
    }
    return bestScore > 0 ? { key: bestKey, name: bestName } : {};
  }

  function buildOrderRows() {
    const parsed = buildParsedRows();
    if (!parsed.length) {
      toast.error("Nothing to process");
      return;
    }
    const next: OrderRow[] = parsed.map((r) => {
      const m = matchCategory(r);
      const cat = m.key ? categoryByKey.get(m.key) : undefined;
      const base = suggestFromCategory(cat?.avgTotalSold, scalePct);
      const finalQty = applyMOQ(base, r.moq);
      return {
        ...r,
        matchedCategoryPath: m.key,
        matchedCategoryName: m.name,
        baseSuggestion: base,
        finalQty,
      };
    });
    setOrderRows(next);
    setShowMapping(false);
  }

  // Re-apply scaling dynamically to suggested rows
  useEffect(() => {
    if (!orderRows.length) return;
    startTransition(() => {
      setOrderRows((rows) =>
        rows.map((row) => {
          const cat = row.matchedCategoryPath ? categoryByKey.get(row.matchedCategoryPath) : undefined;
          if (!cat) return row; // nothing to scale when no category
          const prevAuto = applyMOQ(row.baseSuggestion || 0, row.moq);
          const nextBase = suggestFromCategory(cat.avgTotalSold, scalePct);
          const nextAuto = applyMOQ(nextBase, row.moq);
          const isAuto = row.finalQty === prevAuto;
          return {
            ...row,
            baseSuggestion: nextBase,
            finalQty: isAuto ? nextAuto : row.finalQty,
          };
        })
      );
    });
  }, [scalePct, categoryByKey]);

  // After categories load (e.g. after refresh), recompute base suggestions
  useEffect(() => {
    if (!orderRows.length) return;
    startTransition(() => {
      setOrderRows((rows) =>
        rows.map((row) => {
          const cat = row.matchedCategoryPath ? categoryByKey.get(row.matchedCategoryPath) : undefined;
          if (!cat) return row;
          const nextBase = suggestFromCategory(cat.avgTotalSold, scalePct);
          const nextAuto = applyMOQ(nextBase, row.moq);
          const prevAuto = applyMOQ(row.baseSuggestion || 0, row.moq);
          const isAuto = row.finalQty === prevAuto || !row.baseSuggestion; // treat empty base as auto
          return {
            ...row,
            baseSuggestion: nextBase,
            finalQty: isAuto ? nextAuto : row.finalQty,
          };
        })
      );
    });
  }, [categoryByKey]);

  const changeCategory = useCallback((idx: number, newKey?: string) => {
    setOrderRows((rows) => {
      const copy = [...rows];
      const row = { ...copy[idx] };
      row.matchedCategoryPath = newKey;
      if (newKey) {
        const cat = categoryByKey.get(newKey);
        row.matchedCategoryName = cat?.category;
        row.baseSuggestion = suggestFromCategory(cat?.avgTotalSold, scalePct);
        row.finalQty = applyMOQ(row.baseSuggestion || 0, row.moq);
      } else {
        row.matchedCategoryName = undefined;
        row.baseSuggestion = undefined;
        row.finalQty = row.moq ? row.moq : 0;
      }
      copy[idx] = row;
      return copy;
    });
  }, [categoryByKey, scalePct]);

  const changeQty = useCallback((idx: number, value: string) => {
    const n = Number(value);
    startTransition(() => setOrderRows((rows) => {
      const copy = [...rows];
      const row = { ...copy[idx] };
      const raw = Number.isFinite(n) ? Math.round(n) : 0;
      row.finalQty = raw; // do not enforce MOQ on manual edits
      copy[idx] = row;
      return copy;
    }));
  }, []);

  const removeRow = useCallback((idx: number) => {
    setOrderRows((rows) => rows.filter((_, i) => i !== idx));
  }, []);

  const visibleRows = useMemo(() => (
    showExcludedOnly
      ? orderRows.filter((r) => !(r.finalQty > 0 && r.upc && r.upc.trim()))
      : orderRows
  ), [orderRows, showExcludedOnly]);

  const OrderRowsTable = useMemo(() => memo(function OrderRowsTableInner({
    rows,
  }: { rows: OrderRow[] }) {
    return (
      <div className="rounded-md border overflow-x-auto">
        <Table>
          <TableHeader>
            <TableRow>
              <TableHead>Product</TableHead>
              <TableHead>SKU</TableHead>
              <TableHead>UPC</TableHead>
              <TableHead>Category</TableHead>
              <TableHead className="text-right">Avg Sold</TableHead>
              <TableHead className="text-right">MOQ</TableHead>
              <TableHead className="text-right">Order Qty</TableHead>
              <TableHead className="text-right">Actions</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {rows.map((r, idx) => {
              const cat = r.matchedCategoryPath ? categoryByKey.get(r.matchedCategoryPath) : undefined;
              const isExcluded = !(r.finalQty > 0 && r.upc && r.upc.trim());
              return (
                <TableRow key={`${r.product || r.upc || 'row'}-${idx}`} className={isExcluded ? 'bg-destructive/10' : undefined}>
                  <TableCell>
                    <div className="font-medium">{r.product}</div>
                  </TableCell>
                  <TableCell className="whitespace-nowrap">{r.sku || ""}</TableCell>
                  <TableCell className="whitespace-nowrap">{r.upc || ""}</TableCell>
                  <TableCell className="min-w-[280px]">
                    <Select
                      value={r.matchedCategoryPath ?? "__none"}
                      onValueChange={(v) => changeCategory(idx, v === "__none" ? undefined : v)}
                    >
                      <SelectTrigger>
                        <SelectValue placeholder="Select category" />
                      </SelectTrigger>
                      <SelectContent className="max-h-[320px]">
                        <SelectItem value="__none">Unmatched</SelectItem>
                        {categoryOptions.map((c) => (
                          <SelectItem key={c.value} value={c.value}>{c.label}</SelectItem>
                        ))}
                      </SelectContent>
                    </Select>
                  </TableCell>
                  <TableCell className="text-right">{cat?.avgTotalSold?.toFixed?.(2) ?? "-"}</TableCell>
                  <TableCell className="text-right">{r.moq ?? "-"}</TableCell>
                  <TableCell className="text-right">
                    <Input
                      className="w-24 text-right"
                      value={Number.isFinite(r.finalQty) ? r.finalQty : 0}
                      onChange={(e) => changeQty(idx, e.target.value)}
                      inputMode="numeric"
                    />
                  </TableCell>
                  <TableCell className="text-right">
                    <Button variant="ghost" size="sm" onClick={() => removeRow(idx)} aria-label="Remove row">
                      <XIcon className="h-4 w-4" />
                    </Button>
                  </TableCell>
                </TableRow>
              );
            })}
          </TableBody>
        </Table>
      </div>
    );
  }), [categoryByKey, categoryOptions, changeCategory, changeQty, removeRow]);

  const exportJson = useMemo(() => {
    const items = orderRows
      .filter((r) => (r.finalQty || 0) > 0 && !!(r.upc && r.upc.trim()))
      .map((r) => ({ upc: r.upc!, quantity: r.finalQty }));
    return {
      supplierId: selectedSupplierId ?? null,
      generatedAt: new Date().toISOString(),
      itemCount: items.length,
      items,
    };
  }, [orderRows, selectedSupplierId]);

  const canProcess = headers.length > 0 && rawRows.length > 0;

  return (
    <Card>
      <CardHeader>
        <CardTitle>Quick Order Builder</CardTitle>
      </CardHeader>
      <CardContent className="space-y-4">
        {/* Supplier + Clear */}
        <div className="flex flex-wrap items-end gap-3">
          <div className="max-w-sm">
            <div className="text-sm font-medium mb-1">Supplier</div>
            <Select
              value={selectedSupplierId ?? "__none"}
              onValueChange={(v) => setSelectedSupplierId(v === "__none" ? undefined : v)}
            >
              <SelectTrigger>
                <SelectValue placeholder="Select supplier" />
              </SelectTrigger>
              <SelectContent className="max-h-[320px]">
                <SelectItem value="__none">Select supplier…</SelectItem>
                {supplierOptions.map((s) => (
                  <SelectItem key={String(s.value)} value={String(s.value)}>
                    {s.label}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          </div>
          <Button
            variant="destructive"
            onClick={() => {
              setPasted("");
              setHeaders([]);
              setRawRows([]);
              setHeaderMap({});
              setOrderRows([]);
              setShowJson(false);
              setSelectedSupplierId(undefined);
              setScalePct(100);
              setScaleInput("100");
              setParsed(false);
              setShowMapping(false);
              try { localStorage.removeItem(DRAFT_KEY); } catch {}
              toast.message("Draft cleared");
            }}
          >
            Clear Draft
          </Button>
        </div>

        {!parsed && (
          <>
            <div className="flex flex-wrap items-center gap-2">
              <Input
                ref={fileInputRef}
                type="file"
                accept=".xlsx,.xls,.csv,.tsv,.txt"
                onChange={handleFileChange}
                className="max-w-sm"
              />
              <span className="text-muted-foreground text-sm">or paste below</span>
            </div>

            <Textarea
              placeholder="Paste rows (with a header): Product, SKU, Category, MOQ..."
              value={pasted}
              onChange={(e) => setPasted(e.target.value)}
              className="min-h-[120px]"
            />
            <div className="flex gap-2">
              <Button variant="outline" onClick={handlePasteParse} disabled={!pasted.trim()}>
                Parse Pasted Data
              </Button>
            </div>
          </>
        )}

        {headers.length > 0 && showMapping && (
          <div className="space-y-3">
            <div className="text-sm font-medium">Map Columns</div>
            <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-2">
              <div>
                <div className="text-xs text-muted-foreground mb-1">Product (recommended)</div>
                <Select
                  value={headerMap.product}
                  onValueChange={(v) => setHeaderMap((m) => ({ ...m, product: v }))}
                >
                  <SelectTrigger><SelectValue placeholder="Select column" /></SelectTrigger>
                  <SelectContent>
                    {headerOptions.map((o) => {
                      const ci = idToIndex.get(o.id)!;
                      const samples = rawRows
                        .map((r) => String((r && r[ci]) ?? "").trim())
                        .filter((v) => v.length > 0)
                        .slice(0, 3);
                      return (
                        <SelectItem key={o.id} value={o.id}>
                          <div className="flex flex-col text-left">
                            <span>{o.label}</span>
                            {samples.length > 0 && (
                              <span className="text-xs text-muted-foreground truncate">{samples.join(" • ")}</span>
                            )}
                          </div>
                        </SelectItem>
                      );
                    })}
                  </SelectContent>
                </Select>
              </div>
              <div>
                <div className="text-xs text-muted-foreground mb-1">UPC / Barcode (recommended)</div>
                <Select
                  value={headerMap.upc ?? "__none"}
                  onValueChange={(v) => setHeaderMap((m) => ({ ...m, upc: v === "__none" ? undefined : v }))}
                >
                  <SelectTrigger><SelectValue placeholder="Select column" /></SelectTrigger>
                  <SelectContent>
                    <SelectItem value="__none">None</SelectItem>
                    {headerOptions.map((o) => {
                      const ci = idToIndex.get(o.id)!;
                      const samples = rawRows
                        .map((r) => String((r && r[ci]) ?? "").trim())
                        .filter((v) => v.length > 0)
                        .slice(0, 3);
                      return (
                        <SelectItem key={o.id} value={o.id}>
                          <div className="flex flex-col text-left">
                            <span>{o.label}</span>
                            {samples.length > 0 && (
                              <span className="text-xs text-muted-foreground truncate">{samples.join(" • ")}</span>
                            )}
                          </div>
                        </SelectItem>
                      );
                    })}
                  </SelectContent>
                </Select>
              </div>
              <div>
                <div className="text-xs text-muted-foreground mb-1">SKU (optional)</div>
                <Select
                  value={headerMap.sku ?? "__none"}
                  onValueChange={(v) => setHeaderMap((m) => ({ ...m, sku: v === "__none" ? undefined : v }))}
                >
                  <SelectTrigger><SelectValue placeholder="Select column" /></SelectTrigger>
                  <SelectContent>
                    <SelectItem value="__none">None</SelectItem>
                    {headerOptions.map((o) => {
                      const ci = idToIndex.get(o.id)!;
                      const samples = rawRows
                        .map((r) => String((r && r[ci]) ?? "").trim())
                        .filter((v) => v.length > 0)
                        .slice(0, 3);
                      return (
                        <SelectItem key={o.id} value={o.id}>
                          <div className="flex flex-col text-left">
                            <span>{o.label}</span>
                            {samples.length > 0 && (
                              <span className="text-xs text-muted-foreground truncate">{samples.join(" • ")}</span>
                            )}
                          </div>
                        </SelectItem>
                      );
                    })}
                  </SelectContent>
                </Select>
              </div>
              <div>
                <div className="text-xs text-muted-foreground mb-1">Category Hint (optional)</div>
                <Select
                  value={headerMap.categoryHint ?? "__none"}
                  onValueChange={(v) => setHeaderMap((m) => ({ ...m, categoryHint: v === "__none" ? undefined : v }))}
                >
                  <SelectTrigger><SelectValue placeholder="Select column" /></SelectTrigger>
                  <SelectContent>
                    <SelectItem value="__none">None</SelectItem>
                    {headerOptions.map((o) => {
                      const ci = idToIndex.get(o.id)!;
                      const samples = rawRows
                        .map((r) => String((r && r[ci]) ?? "").trim())
                        .filter((v) => v.length > 0)
                        .slice(0, 3);
                      return (
                        <SelectItem key={o.id} value={o.id}>
                          <div className="flex flex-col text-left">
                            <span>{o.label}</span>
                            {samples.length > 0 && (
                              <span className="text-xs text-muted-foreground truncate">{samples.join(" • ")}</span>
                            )}
                          </div>
                        </SelectItem>
                      );
                    })}
                  </SelectContent>
                </Select>
              </div>
              <div>
                <div className="text-xs text-muted-foreground mb-1">MOQ (optional)</div>
                <Select
                  value={headerMap.moq ?? "__none"}
                  onValueChange={(v) => setHeaderMap((m) => ({ ...m, moq: v === "__none" ? undefined : v }))}
                >
                  <SelectTrigger><SelectValue placeholder="Select column" /></SelectTrigger>
                  <SelectContent>
                    <SelectItem value="__none">None</SelectItem>
                    {headerOptions.map((o) => {
                      const ci = idToIndex.get(o.id)!;
                      const samples = rawRows
                        .map((r) => String((r && r[ci]) ?? "").trim())
                        .filter((v) => v.length > 0)
                        .slice(0, 3);
                      return (
                        <SelectItem key={o.id} value={o.id}>
                          <div className="flex flex-col text-left">
                            <span>{o.label}</span>
                            {samples.length > 0 && (
                              <span className="text-xs text-muted-foreground truncate">{samples.join(" • ")}</span>
                            )}
                          </div>
                        </SelectItem>
                      );
                    })}
                  </SelectContent>
                </Select>
              </div>
            </div>

            <div className="flex flex-wrap items-end gap-3">
              <Button onClick={buildOrderRows} disabled={!canProcess || (!headerMap.product && !headerMap.upc)}>
                Build Suggestions
              </Button>
            </div>
          </div>
        )}

        {orderRows.length > 0 && (
          <div className="space-y-3">
            {/* Controls for existing suggestions */}
            <div className="flex flex-wrap items-end justify-between gap-3">
              <div className="flex items-end gap-3">
                <div>
                  <div className="text-xs text-muted-foreground mb-1">Scale suggestions (%)</div>
                  <Input
                    type="number"
                    className="w-28"
                    value={scaleInput}
                    onChange={(e) => setScaleInput(e.target.value)}
                  />
                </div>
                <div className="flex items-center gap-2 pb-2">
                  <Checkbox id="excludedOnly" checked={showExcludedOnly} onCheckedChange={(v) => setShowExcludedOnly(!!v)} />
                  <label htmlFor="excludedOnly" className="text-sm">Show excluded only</label>
                </div>
              </div>
              <div className="pb-2">
                <Button variant="outline" size="sm" onClick={() => setShowMapping((v) => !v)}>
                  {showMapping ? 'Hide Mapping' : 'Edit Mapping'}
                </Button>
              </div>
            </div>
            <OrderRowsTable rows={visibleRows} />

            {/* Exclusion alert if some rows won't be exported */}
            {(() => {
              const excluded = orderRows.filter((r) => !(r.finalQty > 0 && r.upc && r.upc.trim()));
              if (excluded.length === 0) return null;
              const missingUpc = excluded.filter((r) => !r.upc || !r.upc.trim()).length;
              const zeroQty = excluded.filter((r) => !(r.finalQty > 0)).length;
              return (
                <Alert variant="destructive">
                  <AlertTitle>Some rows will not be included</AlertTitle>
                  <AlertDescription>
                    <div className="text-sm">
                      {excluded.length} row{excluded.length !== 1 ? "s" : ""} excluded from JSON
                      <ul className="list-disc ml-5">
                        {missingUpc > 0 && <li>{missingUpc} missing UPC</li>}
                        {zeroQty > 0 && <li>{zeroQty} with zero quantity</li>}
                      </ul>
                    </div>
                  </AlertDescription>
                </Alert>
              );
            })()}
            <div className="flex gap-2">
              <Button variant="outline" onClick={() => setShowJson((s) => !s)}>
                {showJson ? "Hide" : "Preview"} JSON
              </Button>
              <Button
                onClick={() => {
                  setShowJson(true);
                  navigator.clipboard?.writeText(JSON.stringify(exportJson, null, 2)).then(
                    () => toast.success("JSON copied"),
                    () => toast.message("JSON ready (copy failed)")
                  ).finally(() => {
                    try { localStorage.removeItem(DRAFT_KEY); } catch {}
                  });
                }}
              >
                Copy JSON
              </Button>
            </div>

            {showJson && (
              <Code className="p-4 w-full rounded-md border whitespace-pre-wrap">
                {JSON.stringify(exportJson, null, 2)}
              </Code>
            )}
          </div>
        )}
      </CardContent>
    </Card>
  );
}
