import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { ArrowLeft, Plus, Trash2 } from 'lucide-react';
import { toast } from 'sonner';
import { Button } from '../ui/button';
import { Input } from '../ui/input';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '../ui/select';
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from '../ui/table';
import { SearchableSelect } from '../ui/searchable-select';
import { ImageUrlUpload } from '../ui/image-url-upload';
import { getLabelTemplate, updateLabelTemplate } from '../../services/labelTemplateService';
import { getProducts } from '../../services/productService';
import { getLabelTypes } from '../../services/labelTypeService';
import { skipCountForPage } from '../../lib/paginationQuery';
import type { LabelElement, LabelTemplateDto, LabelType, Unit } from '../../types/labelTemplate';
import {
  appliedLocationToEditor,
  canonicalElementType,
  dataEntryColumnLabel,
  isDataEntryTableColumnElement,
  isDateTimeDataEntryField,
  labelElementsToApiPayload,
  sortTemplateElementsForDisplay,
} from '../../types/labelTemplate';
import {
  LABEL_FORM_OFFSET_UNITS,
  offsetFieldUiStateFromStored,
  serializePrintInputOffset,
  tryParsePrintInputOffsetStored,
} from '../../lib/labelFormDatePreview';
import {
  foldNutritionCompositeKeysIntoDefaults,
  hydrateRowFieldValuesWithNutritionColumns,
  listNutritionManualFieldSpecs,
  nutritionCompositeFieldKey,
  type NutritionManualFieldSpec,
} from '../../lib/nutritionManualEntry';
import type { ProductDto } from '../../types/product';
import type { LabelTypeDto } from '../../types/labelType';
import {
  buildTemplateBarcodeQrDefaultsFromCodeValue,
  buildTemplateLabelBindingDefaults,
  isTemplateSectionBarcodeOrQrElement,
} from '../../lib/productCodeValueTemplate';

export type TemplateDataEntryRow = {
  id: string;
  productId: string;
  labelTypeId: string;
  /** elementId -> 管理端录入的关联/默认值（真正打印时输入仍在 App） */
  fieldValues: Record<string, string>;
};

function newRowId(): string {
  try {
    return crypto.randomUUID();
  } catch {
    return `row-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
  }
}

/** 保存前：日期类已是 JSON 则保留；纯数字等转为 JSON；其它旧文案原样保留 */
function normalizeDateTimeFieldForSave(el: LabelElement, raw: string): string {
  if (!isDateTimeDataEntryField(el)) return raw ?? '';
  const t = String(raw ?? '').trim();
  if (!t) return '';
  if (tryParsePrintInputOffsetStored(t)) return t;
  const { unit, value } = offsetFieldUiStateFromStored(t);
  if (!String(value).trim()) return t;
  const amount = Number(String(value).trim());
  if (!Number.isFinite(amount)) return t;
  return serializePrintInputOffset(unit, String(value).trim());
}

/** 模板录入表：图片与二维码（及名称含 qrcode 的控件）用上传组件，预览区固定 100×100 */
const DATA_ENTRY_IMAGE_BOX =
  'h-[100px] w-[100px] min-h-[100px] min-w-[100px] max-h-[100px] max-w-[100px] shrink-0 aspect-auto';

function dataEntryUsesImageUpload(element: LabelElement): boolean {
  const type = canonicalElementType(element.type);
  if (type === 'IMAGE' || type === 'QRCODE') return true;
  const n = (element.elementName ?? '').trim().toLowerCase();
  return n.includes('qrcode');
}

function DataEntryOffsetCell({
  value,
  onValueChange,
}: {
  value: string;
  onValueChange: (next: string) => void;
}) {
  const { unit, value: num } = offsetFieldUiStateFromStored(value);
  return (
    <div className="flex items-center gap-1.5 min-w-0 max-w-[168px]">
      <Select
        value={unit}
        onValueChange={(u) => onValueChange(serializePrintInputOffset(u, num))}
      >
        <SelectTrigger className="h-9 w-[118px] shrink-0 bg-white text-xs border-gray-300 px-2 [&>span]:line-clamp-1">
          <SelectValue />
        </SelectTrigger>
        <SelectContent>
          {LABEL_FORM_OFFSET_UNITS.map((u) => (
            <SelectItem key={u} value={u} className="text-xs">
              {u}
            </SelectItem>
          ))}
        </SelectContent>
      </Select>
      <Input
        type="number"
        className="h-9 w-14 shrink-0 border-gray-300 bg-white px-2 text-sm"
        value={num}
        onChange={(e) => onValueChange(serializePrintInputOffset(unit, e.target.value))}
        placeholder="Val"
      />
    </div>
  );
}

function DataEntryValueCell({
  element,
  value,
  onValueChange,
  readOnly,
}: {
  element: LabelElement;
  value: string;
  onValueChange: (next: string) => void;
  readOnly?: boolean;
}) {
  if (readOnly || isTemplateSectionBarcodeOrQrElement(element)) {
    return (
      <Input
        value={value}
        readOnly
        disabled
        className="h-10 border-gray-300 max-w-[220px] bg-gray-50"
        placeholder="From product Code Value"
      />
    );
  }
  if (dataEntryUsesImageUpload(element)) {
    return (
      <ImageUrlUpload
        value={value}
        onChange={onValueChange}
        uploadSubDir="label-template-data"
        boxClassName={DATA_ENTRY_IMAGE_BOX}
      />
    );
  }
  if (isDateTimeDataEntryField(element)) {
    return <DataEntryOffsetCell value={value} onValueChange={onValueChange} />;
  }
  return (
    <Input
      value={value}
      onChange={(e) => onValueChange(e.target.value)}
      placeholder="—"
      className="h-10 border-gray-300 max-w-[220px]"
    />
  );
}

export function LabelTemplateDataEntryView({
  templateCode,
  onBack,
  contextHint,
}: {
  templateCode: string;
  onBack: () => void;
  /** 从 Labels 进入时展示：当前编辑的是哪条标签绑定的模板 */
  contextHint?: string;
}) {
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [templateTitle, setTemplateTitle] = useState('');
  /** 详情接口完整模板，保存时随 templateProductDefaults 一并 PUT（接口 4.4） */
  const [templateDto, setTemplateDto] = useState<LabelTemplateDto | null>(null);
  const [products, setProducts] = useState<ProductDto[]>([]);
  const [types, setTypes] = useState<LabelTypeDto[]>([]);
  const [rows, setRows] = useState<TemplateDataEntryRow[]>([]);

  const sortedTemplateElements = useMemo(
    () => sortTemplateElementsForDisplay((templateDto?.elements ?? []) as LabelElement[]),
    [templateDto],
  );

  const dataColumns = useMemo(() => {
    const cols: Array<
      | { kind: "element"; el: LabelElement }
      | { kind: "nutrition"; parent: LabelElement; spec: NutritionManualFieldSpec }
    > = [];
    for (const el of sortedTemplateElements) {
      if (isDataEntryTableColumnElement(el)) cols.push({ kind: "element", el });
      if (canonicalElementType(el.type) === "NUTRITION") {
        for (const spec of listNutritionManualFieldSpecs(el)) {
          cols.push({ kind: "nutrition", parent: el, spec });
        }
      }
    }
    return cols;
  }, [sortedTemplateElements]);

  const productOptions = useMemo(
    () =>
      products.map((p) => {
        const label =
          (p.productName ?? p.productCode ?? '').trim() || p.id;
        return { value: p.id, label };
      }),
    [products],
  );

  const labelTypeOptions = useMemo(
    () =>
      types.map((t) => {
        const label =
          (t.typeName ?? t.typeCode ?? '').trim() || t.id;
        return { value: t.id, label };
      }),
    [types],
  );

  useEffect(() => {
    let cancelled = false;
    (async () => {
      setLoading(true);
      try {
        const [tpl, prodRes, typeRes] = await Promise.all([
          getLabelTemplate(templateCode),
          getProducts({ skipCount: skipCountForPage(1), maxResultCount: 500 }),
          getLabelTypes({ skipCount: skipCountForPage(1), maxResultCount: 500 }),
        ]);
        if (cancelled) return;
        const title =
          (tpl.templateName ?? tpl.name ?? '').trim() ||
          (tpl.templateCode ?? tpl.id ?? '').trim() ||
          templateCode;
        setTemplateTitle(title);
        setProducts(prodRes.items ?? []);
        setTypes(typeRes.items ?? []);
        setTemplateDto(tpl);

        const defaults = tpl.templateProductDefaults ?? [];
        const fromApi =
          defaults.length > 0
            ? [...defaults].sort((a, b) => (a.orderNum ?? 0) - (b.orderNum ?? 0))
            : [];

        if (fromApi.length > 0) {
          setRows(
            fromApi.map((d) => ({
              id: newRowId(),
              productId: d.productId,
              labelTypeId: d.labelTypeId,
              fieldValues: hydrateRowFieldValuesWithNutritionColumns(
                { ...d.defaultValues },
                (tpl.elements ?? []) as LabelElement[],
              ),
            })),
          );
        } else {
          setRows([
            {
              id: newRowId(),
              productId: '',
              labelTypeId: '',
              fieldValues: {},
            },
          ]);
        }
      } catch (e: unknown) {
        if (!cancelled) {
          toast.error('Failed to load template or options.', {
            description: e instanceof Error ? e.message : 'Please try again.',
          });
          setTemplateTitle(templateCode);
          setRows([]);
          setTemplateDto(null);
        }
      } finally {
        if (!cancelled) setLoading(false);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [templateCode]);

  const addRow = useCallback(() => {
    setRows((prev) => [
      ...prev,
      { id: newRowId(), productId: '', labelTypeId: '', fieldValues: {} },
    ]);
  }, []);

  const removeRow = useCallback((id: string) => {
    setRows((prev) => (prev.length <= 1 ? prev : prev.filter((r) => r.id !== id)));
  }, []);

  const updateRow = useCallback((id: string, patch: Partial<TemplateDataEntryRow>) => {
    setRows((prev) =>
      prev.map((r) => (r.id === id ? { ...r, ...patch } : r)),
    );
  }, []);

  const applyProductCodeValueToRow = useCallback(
    (rowId: string, productId: string) => {
      const pid = productId.trim();
      const product = products.find((p) => p.id === pid);
      const cv = (product?.codeValue ?? '').trim();
      const elements = (templateDto?.elements ?? []) as LabelElement[];
      const scanDefaults = buildTemplateBarcodeQrDefaultsFromCodeValue(elements, cv);
      setRows((prev) =>
        prev.map((r) => {
          if (r.id !== rowId) return r;
          return {
            ...r,
            productId: pid,
            fieldValues: { ...r.fieldValues, ...scanDefaults },
          };
        }),
      );
    },
    [products, templateDto],
  );

  const applyLabelTypeToRow = useCallback(
    (rowId: string, labelTypeId: string) => {
      const ltid = labelTypeId.trim();
      const labelType = types.find((t) => t.id === ltid);
      const labelTypeName = (labelType?.typeName ?? labelType?.typeCode ?? '').trim();
      const labelDefaults = buildTemplateLabelBindingDefaults(
        (templateDto?.elements ?? []) as LabelElement[],
        { labelTypeName },
      );
      setRows((prev) =>
        prev.map((r) => {
          if (r.id !== rowId) return r;
          return {
            ...r,
            labelTypeId: ltid,
            fieldValues: { ...r.fieldValues, ...labelDefaults },
          };
        }),
      );
    },
    [types, templateDto],
  );

  const setFieldValue = useCallback(
    (rowId: string, elementId: string, value: string) => {
      setRows((prev) =>
        prev.map((r) => {
          if (r.id !== rowId) return r;
          return {
            ...r,
            fieldValues: { ...r.fieldValues, [elementId]: value },
          };
        }),
      );
    },
    [],
  );

  const handleSave = useCallback(async () => {
    if (!templateDto) {
      toast.error('Template not loaded', { description: 'Please reload the page and try again.' });
      return;
    }
    const touched = rows.filter((r) => r.productId.trim() || r.labelTypeId.trim());
    const incomplete = touched.some(
      (r) => !r.productId.trim() || !r.labelTypeId.trim(),
    );
    if (incomplete) {
      toast.error('Product and label type required', {
        description:
          'Each row that you started must have both Product and Label type selected.',
      });
      return;
    }

    const validRows = rows.filter((r) => r.productId.trim() && r.labelTypeId.trim());
    const fullElements = sortTemplateElementsForDisplay(
      (templateDto.elements ?? []) as LabelElement[],
    );
    const templateProductDefaults = validRows.map((r, i) => {
      const folded = foldNutritionCompositeKeysIntoDefaults(r.fieldValues, fullElements);
      const defaultValues: Record<string, string> = {};
      for (const col of dataColumns) {
        if (col.kind === "element") {
          defaultValues[col.el.id] = normalizeDateTimeFieldForSave(col.el, folded[col.el.id] ?? "");
        }
      }
      for (const el of fullElements) {
        if (canonicalElementType(el.type) === "NUTRITION") {
          const j = folded[el.id];
          if (j) defaultValues[el.id] = j;
        }
      }
      const product = products.find((p) => p.id === r.productId.trim());
      const labelType = types.find((t) => t.id === r.labelTypeId.trim());
      Object.assign(
        defaultValues,
        buildTemplateBarcodeQrDefaultsFromCodeValue(fullElements, product?.codeValue),
      );
      Object.assign(
        defaultValues,
        buildTemplateLabelBindingDefaults(fullElements, {
          labelTypeName: labelType?.typeName ?? labelType?.typeCode,
        }),
      );
      return {
        productId: r.productId.trim(),
        labelTypeId: r.labelTypeId.trim(),
        defaultValues,
        orderNum: i + 1,
      };
    });

    if (fullElements.length === 0) {
      toast.error('Template has no elements', { description: 'Cannot save this template.' });
      return;
    }

    const loc = appliedLocationToEditor(templateDto);
    setSaving(true);
    try {
      const updated = await updateLabelTemplate(templateCode, {
        id: templateDto.id,
        name: (templateDto.name ?? templateDto.templateName ?? '').trim() || templateCode,
        labelType: (templateDto.labelType ?? 'PRICE') as LabelType,
        unit: (templateDto.unit ?? 'inch') as Unit,
        width: Number(templateDto.width ?? 2),
        height: Number(templateDto.height ?? 2),
        appliedLocation: loc,
        showRuler: templateDto.showRuler ?? true,
        showGrid: templateDto.showGrid ?? true,
        state: templateDto.state ?? true,
        elements: labelElementsToApiPayload(fullElements),
        appliedLocationIds: loc === 'ALL' ? [] : (templateDto.appliedLocationIds ?? []),
        templateProductDefaults,
      });
      setTemplateDto(updated);
      toast.success('Saved', {
        description: 'Template product defaults were updated on the server.',
      });
    } catch (e: unknown) {
      toast.error('Save failed', {
        description: e instanceof Error ? e.message : 'Please try again.',
      });
    } finally {
      setSaving(false);
    }
  }, [templateCode, templateDto, rows, dataColumns, products]);

  return (
    <div className="h-full flex flex-col min-h-0">
      <div className="flex flex-wrap items-center gap-3 pb-4 border-b border-gray-200 shrink-0">
        <Button
          type="button"
          variant="outline"
          className="h-10 gap-2"
          onClick={onBack}
        >
          <ArrowLeft className="h-4 w-4" />
          Back
        </Button>
        <div className="flex-1 min-w-[200px]">
          <div className="text-xs font-medium text-gray-500 uppercase tracking-wide">
            Label template
          </div>
          <h2 className="text-lg font-semibold text-gray-900 truncate" title={templateTitle}>
            {templateTitle}
          </h2>
          {contextHint ? (
            <p className="text-sm text-gray-600 truncate mt-0.5" title={contextHint}>
              {contextHint}
            </p>
          ) : null}
        </div>
        <div className="flex items-center gap-2">
          <Button type="button" variant="outline" className="h-10 gap-1" onClick={addRow}>
            <Plus className="h-4 w-4" />
            Add row
          </Button>
          <Button
            type="button"
            className="h-10 bg-blue-600 hover:bg-blue-700"
            onClick={() => void handleSave()}
            disabled={saving || loading || !templateDto}
          >
            {saving ? 'Saving…' : 'Save'}
          </Button>
        </div>
      </div>

      <p className="text-sm text-gray-600 py-3 shrink-0">
        Bind product and label type per row. Values are saved with the template (edit API) as{' '}
        <span className="font-medium">templateProductDefaults</span> (interface doc section 4.4). Columns cover{' '}
        <span className="font-medium">Label</span> group defaults, <span className="font-medium">PRINT_INPUT</span> /
        Duration fields, and (when present) <span className="font-medium">Nutrition Facts</span> manual cells.{' '}
        <span className="font-medium">Template</span> panel elements are edited only in the label template editor
        (not here). Date / time / duration columns use <span className="font-medium">unit + value</span>; stored as
        JSON with <span className="font-medium">unit</span> and <span className="font-medium">value</span> keys.
        Nutrition values are stored as JSON under the nutrition element id for App print preview.
      </p>

      <div className="flex-1 min-h-0 overflow-auto rounded-md border bg-white shadow-sm">
        {loading ? (
          <div className="p-10 text-center text-sm text-gray-500">Loading…</div>
        ) : dataColumns.length === 0 ? (
          <div className="p-10 text-center text-sm text-gray-600">
            No manual input or nutrition columns in this template.
          </div>
        ) : (
          <Table>
            <TableHeader>
              <TableRow className="bg-gray-50 hover:bg-gray-50">
                <TableHead className="font-bold text-gray-900 w-[200px] min-w-[160px]">
                  Product
                </TableHead>
                <TableHead className="font-bold text-gray-900 w-[180px] min-w-[140px]">
                  Label type
                </TableHead>
                {dataColumns.map((col) => (
                  <TableHead
                    key={
                      col.kind === 'element'
                        ? col.el.id
                        : nutritionCompositeFieldKey(col.parent.id, col.spec.subKey)
                    }
                    className="font-bold text-gray-900 min-w-[120px] whitespace-nowrap"
                    title={col.kind === 'element' ? col.el.id : `${col.parent.id} · ${col.spec.subKey}`}
                  >
                    {col.kind === 'element' ? dataEntryColumnLabel(col.el) : col.spec.columnLabel}
                  </TableHead>
                ))}
                <TableHead className="w-[72px] text-center font-bold text-gray-900"> </TableHead>
              </TableRow>
            </TableHeader>
            <TableBody>
              {rows.map((row) => (
                <TableRow key={row.id} className="hover:bg-gray-50">
                  <TableCell className="align-top py-2">
                    <SearchableSelect
                      value={row.productId}
                      onValueChange={(v) => applyProductCodeValueToRow(row.id, v)}
                      options={productOptions}
                      placeholder="Select product"
                      searchPlaceholder="Search product…"
                    />
                  </TableCell>
                  <TableCell className="align-top py-2">
                    <SearchableSelect
                      value={row.labelTypeId}
                      onValueChange={(v) => applyLabelTypeToRow(row.id, v)}
                      options={labelTypeOptions}
                      placeholder="Select label type"
                      searchPlaceholder="Search type…"
                    />
                  </TableCell>
                  {dataColumns.map((col) => (
                    <TableCell
                      key={
                        col.kind === 'element'
                          ? col.el.id
                          : nutritionCompositeFieldKey(col.parent.id, col.spec.subKey)
                      }
                      className="align-top py-2"
                    >
                      {col.kind === 'element' ? (
                        <DataEntryValueCell
                          element={col.el}
                          value={row.fieldValues[col.el.id] ?? ''}
                          onValueChange={(v) => setFieldValue(row.id, col.el.id, v)}
                        />
                      ) : (
                        <Input
                          value={
                            row.fieldValues[
                              nutritionCompositeFieldKey(col.parent.id, col.spec.subKey)
                            ] ?? ''
                          }
                          onChange={(e) =>
                            setFieldValue(
                              row.id,
                              nutritionCompositeFieldKey(col.parent.id, col.spec.subKey),
                              e.target.value,
                            )
                          }
                          placeholder="—"
                          className="h-10 border-gray-300 max-w-[220px]"
                        />
                      )}
                    </TableCell>
                  ))}
                  <TableCell className="text-center align-top py-2">
                    <Button
                      type="button"
                      variant="ghost"
                      size="icon"
                      className="h-9 w-9 text-red-600 hover:text-red-700 hover:bg-red-50"
                      aria-label="Remove row"
                      onClick={() => removeRow(row.id)}
                      disabled={rows.length <= 1}
                    >
                      <Trash2 className="h-4 w-4" />
                    </Button>
                  </TableCell>
                </TableRow>
              ))}
            </TableBody>
          </Table>
        )}
      </div>
    </div>
  );
}
