import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Button } from '../../ui/button';
import { ArrowLeft, Save, Download } from 'lucide-react';
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
} from '../../ui/dialog';
import { Input } from '../../ui/input';
import { Label } from '../../ui/label';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '../../ui/select';
import type { ElementLibraryCategory, LabelTemplate, LabelElement } from '../../../types/labelTemplate';
import {
  allocateElementName,
  canonicalElementType,
  composeElementTypeForPersist,
  composeLibraryCategoryForPersist,
  createDefaultTemplate,
  createDefaultElement,
  labelElementsToApiPayload,
  PRESET_LABEL_SIZES,
  resolvedLibraryCategoryForPersist,
  resolvedTypeAddForPersist,
  resolvedValueSourceTypeForSave,
  stripLabelConfigPrefixes,
  valueSourceTypeForLibraryCategory,
} from '../../../types/labelTemplate';
import { ElementsPanel } from './ElementsPanel';
import { LabelCanvas, LabelPreviewOnly, clampLabelElementBox } from './LabelCanvas';
import { PropertiesPanel } from './PropertiesPanel';
import { createLabelTemplate, getLabelTemplate, getLabelTemplates, updateLabelTemplate } from '../../../services/labelTemplateService';
import { toast } from 'sonner';

const MIN_SCALE = 0.5;
const MAX_SCALE = 2;
const SCALE_STEP = 0.25;
const DEFAULT_SCALE = 1.0;

function buildCopiedTemplateId(sourceId: string): string {
  const seed = Math.random().toString(36).slice(2, 8);
  return `tpl_${seed}_${Date.now().toString(36)}`;
}

function cloneStarterTemplate(source: LabelTemplate): LabelTemplate {
  return {
    ...source,
    id: buildCopiedTemplateId(source.id),
    name: `${(source.name || "Unnamed template").trim()} Copy`,
    elements: source.elements.map((el) => ({
      ...el,
      config: { ...(el.config ?? {}) },
    })),
  };
}

interface LabelTemplateEditorProps {
  /** null = 新建，string = 编辑该 id */
  templateId: string | null;
  initialTemplate: LabelTemplate | null;
  onClose: () => void;
  onSaved: () => void;
}

export function LabelTemplateEditor({
  templateId,
  initialTemplate,
  onClose,
  onSaved,
}: LabelTemplateEditorProps) {
  const [template, setTemplate] = useState<LabelTemplate>(() => {
    if (initialTemplate) return { ...initialTemplate };
    const next = createDefaultTemplate(templateId ?? undefined);
    return {
      ...next,
      id: buildCopiedTemplateId(next.id || "template"),
    };
  });
  const [selectedId, setSelectedId] = useState<string | null>(null);
  const [scale, setScale] = useState(DEFAULT_SCALE);
  const [previewOpen, setPreviewOpen] = useState(false);
  const [canvasBorder, setCanvasBorder] = useState<'none' | 'line' | 'dotted'>('none');
  const [canvasRotation, setCanvasRotation] = useState<'horizontal' | 'vertical'>('horizontal');
  const [starterOptions, setStarterOptions] = useState<Array<{ code: string; name: string }>>([]);
  const [selectedStarterCode, setSelectedStarterCode] = useState<string>('');
  const [loadingStarter, setLoadingStarter] = useState(false);

  const selectedElement = template.elements.find((el) => el.id === selectedId) ?? null;

  useEffect(() => {
    if (templateId) return;
    let cancelled = false;
    (async () => {
      try {
        const res = await getLabelTemplates({ skipCount: 1, maxResultCount: 200 });
        if (cancelled) return;
        const options = (res.items ?? [])
          .map((x) => {
            const code = (x.templateCode ?? x.id ?? '').trim();
            const name = (x.templateName ?? x.name ?? code).trim();
            return code ? { code, name } : null;
          })
          .filter((x): x is { code: string; name: string } => !!x);
        setStarterOptions(options);
        if (!selectedStarterCode && options.length > 0) {
          setSelectedStarterCode(options[0].code);
        }
      } catch {
        if (!cancelled) setStarterOptions([]);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [templateId, selectedStarterCode]);

  useEffect(() => {
    if (templateId || !selectedStarterCode) return;
    let cancelled = false;
    (async () => {
      setLoadingStarter(true);
      try {
        const apiTemplate = await getLabelTemplate(selectedStarterCode);
        if (cancelled) return;
        const copied = cloneStarterTemplate({
          id: apiTemplate.id,
          name: (apiTemplate.name ?? apiTemplate.templateName ?? '').trim() || 'Unnamed template',
          labelType: (apiTemplate.labelType as any) ?? 'PRICE',
          unit: (apiTemplate.unit as any) ?? 'cm',
          width: apiTemplate.width ?? 6,
          height: apiTemplate.height ?? 4,
          appliedLocation: apiTemplate.appliedLocation === 'SPECIFIED' ? 'SPECIFIED' : 'ALL',
          appliedLocationIds: [...(apiTemplate.appliedLocationIds ?? [])],
          showRuler: true,
          showGrid: apiTemplate.showGrid ?? true,
          elements: (apiTemplate.elements ?? []).map((raw, idx) => {
            const el = raw as LabelElement;
            const en = (el.elementName ?? '').trim();
            return { ...el, elementName: en || `element${idx + 1}` };
          }),
        });
        setTemplate(copied);
        setSelectedId(null);
      } catch (e: any) {
        if (cancelled) return;
        toast.error("Failed to copy starter template.", {
          description: e?.message ? String(e.message) : "Please try again.",
        });
      } finally {
        if (!cancelled) setLoadingStarter(false);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [templateId, selectedStarterCode]);

  /** 纸张尺寸或单位变化时，将已有控件限制在安全区内 */
  useEffect(() => {
    setTemplate((prev) => {
      const unitToPx = (value: number, unit: "cm" | "inch"): number =>
        unit === "cm" ? value * 37.8 : value * 96;
      const baseW = unitToPx(prev.width, prev.unit);
      const baseH = unitToPx(prev.height, prev.unit);
      let changed = false;
      const elements = prev.elements.map((el) => {
        const c = clampLabelElementBox(el.x, el.y, el.width, el.height, baseW, baseH);
        if (el.x !== c.x || el.y !== c.y || el.width !== c.w || el.height !== c.h) {
          changed = true;
          return { ...el, x: c.x, y: c.y, width: c.w, height: c.h };
        }
        return el;
      });
      if (!changed) return prev;
      return { ...prev, elements };
    });
  }, [template.width, template.height, template.unit, template.id]);

  const templateBorderValue = canvasBorder;

  /** 顶部表单：紧凑间距（!h-7 覆盖 SelectTrigger / Input 默认高度） */
  const topCellClass = "space-y-1";
  const topFieldClass = "!h-7 px-2 py-1 text-xs leading-tight bg-white";
  const topSelectTriggerClass =
    "!h-7 gap-1 px-2 py-0 text-xs leading-tight bg-white [&_svg]:!size-3 *:data-[slot=select-value]:line-clamp-1";
  const topLabelClass = "text-[11px] font-semibold leading-tight text-[#1d3c8f]";

  const addElement = useCallback((
    type: Parameters<typeof createDefaultElement>[0],
    configOverride: Partial<Record<string, unknown>> | undefined,
    libraryCategory: ElementLibraryCategory,
    paletteItemLabel: string,
  ) => {
    let addedId = "";
    setTemplate((prev) => {
      const unitToPx = (value: number, unit: "cm" | "inch"): number =>
        unit === "cm" ? value * 37.8 : value * 96;

      const canvasWidthPx = unitToPx(prev.width, prev.unit);
      const canvasHeightPx = unitToPx(prev.height, prev.unit);

      let el = createDefaultElement(type, 0, 0);
      const GRID_SIZE = 8;
      const snapToGrid = (value: number): number =>
        Math.round(value / GRID_SIZE) * GRID_SIZE;

      let centerX = (canvasWidthPx - el.width) / 2;
      let centerY = (canvasHeightPx - el.height) / 2;

      const checkOverlap = (x: number, y: number, width: number, height: number): boolean =>
        prev.elements.some((o) => {
          const elRight = o.x + o.width;
          const elBottom = o.y + o.height;
          const newRight = x + width;
          const newBottom = y + height;
          return !(x >= elRight || newRight <= o.x || y >= elBottom || newBottom <= o.y);
        });

      if (checkOverlap(centerX, centerY, el.width, el.height)) {
        const offset = GRID_SIZE * 2;
        let found = false;
        for (let tryY = centerY; tryY < canvasHeightPx - el.height && !found; tryY += offset) {
          for (let tryX = centerX; tryX < canvasWidthPx - el.width && !found; tryX += offset) {
            if (!checkOverlap(tryX, tryY, el.width, el.height)) {
              centerX = tryX;
              centerY = tryY;
              found = true;
            }
          }
        }
        if (!found) {
          for (let tryY = centerY; tryY >= 0 && !found; tryY -= offset) {
            for (let tryX = centerX; tryX >= 0 && !found; tryX -= offset) {
              if (!checkOverlap(tryX, tryY, el.width, el.height)) {
                centerX = tryX;
                centerY = tryY;
                found = true;
              }
            }
          }
        }
      }

      el = {
        ...el,
        x: Math.max(0, snapToGrid(centerX)),
        y: Math.max(0, snapToGrid(centerY)),
      };
      const box = clampLabelElementBox(el.x, el.y, el.width, el.height, canvasWidthPx, canvasHeightPx);
      el = { ...el, x: box.x, y: box.y, width: box.w, height: box.h };
      if (configOverride && Object.keys(configOverride).length > 0) {
        el.config = { ...el.config, ...configOverride };
      }
      const elementName = allocateElementName(paletteItemLabel, prev.elements);
      const vst = valueSourceTypeForLibraryCategory(libraryCategory);
      el = {
        ...el,
        type: el.type,
        typeAdd: composeElementTypeForPersist(libraryCategory, paletteItemLabel),
        libraryCategory: composeLibraryCategoryForPersist(libraryCategory, paletteItemLabel),
        valueSourceType: vst,
        elementName,
      };
      addedId = el.id;
      return { ...prev, elements: [...prev.elements, el] };
    });
    setSelectedId(addedId);
  }, [template.width, template.height, template.unit]);

  const updateElement = useCallback((id: string, patch: Partial<LabelElement>) => {
    setTemplate((prev) => {
      const unitToPx = (value: number, unit: "cm" | "inch"): number =>
        unit === "cm" ? value * 37.8 : value * 96;
      const baseW = unitToPx(prev.width, prev.unit);
      const baseH = unitToPx(prev.height, prev.unit);
      return {
        ...prev,
        elements: prev.elements.map((el) => {
          if (el.id !== id) return el;
          const merged = { ...el, ...patch };
          const geomTouched =
            patch.x !== undefined ||
            patch.y !== undefined ||
            patch.width !== undefined ||
            patch.height !== undefined;
          if (!geomTouched) return merged;
          const c = clampLabelElementBox(merged.x, merged.y, merged.width, merged.height, baseW, baseH);
          return { ...merged, x: c.x, y: c.y, width: c.w, height: c.h };
        }),
      };
    });
  }, []);

  const deleteElement = useCallback((id: string) => {
    setTemplate((prev) => ({
      ...prev,
      elements: prev.elements.filter((el) => el.id !== id),
    }));
    setSelectedId(null);
  }, []);

  const handleTemplateChange = useCallback((patch: Partial<LabelTemplate>) => {
    setTemplate((prev) => ({ ...prev, ...patch }));
  }, []);

  const handleSave = useCallback(async () => {
    try {
      const code = (template.id ?? "").trim();
      if (!code) {
        toast.error("Template code is required.", {
          description: "Please enter a template code (e.g. TPL_TEST_001).",
        });
        return;
      }
      const emptyName = template.elements.find(
        (el) => !(el.elementName ?? "").trim(),
      );
      if (emptyName) {
        toast.error("Component name required.", {
          description: "Each element must have a non-empty element name.",
        });
        return;
      }

      const optionsWithoutDictionary = template.elements.find((el) => {
        if (el.type !== "TEXT_STATIC") return false;
        const cfg = el.config as Record<string, unknown>;
        if (String(cfg?.inputType ?? "").toLowerCase() !== "options") return false;
        const mid = String(cfg?.multipleOptionId ?? cfg?.MultipleOptionId ?? "").trim();
        return !mid;
      });
      if (optionsWithoutDictionary) {
        toast.error("Option dictionary required.", {
          description:
            "Each Multiple Options element must have an Option dictionary selected in the properties panel.",
        });
        return;
      }

      // 转换 LabelTemplate 到 API 需要的格式（对齐 LabelTemplateCreateInputVo）
      const apiInput = {
        id: code,
        name: template.name,
        labelType: template.labelType,
        unit: template.unit,
        width: template.width,
        height: template.height,
        /** 编辑器已移除「适用门店」，保存时统一为全部门店 */
        appliedLocation: "ALL" as const,
        showRuler: true,
        showGrid: template.showGrid ?? true,
        state: true,
        elements: labelElementsToApiPayload(template.elements),
        appliedLocationIds: [] as string[],
      };

      if (templateId) {
        // 编辑模式：使用 TemplateCode 作为 id
        await updateLabelTemplate(code, apiInput);
        toast.success("Template updated.", {
          description: "The template has been updated successfully.",
        });
      } else {
        // 新建模式
        await createLabelTemplate(apiInput);
        toast.success("Template created.", {
          description: "The template has been created successfully.",
        });
      }
      onSaved();
      onClose();
    } catch (e: any) {
      toast.error("Failed to save template.", {
        description: e?.message ? String(e.message) : "Please try again.",
      });
    }
  }, [template, templateId, onSaved, onClose]);

  const handleExport = useCallback(() => {
    const payload: LabelTemplate = {
      ...template,
      elements: template.elements.map((el) => ({
        ...el,
        type: canonicalElementType(el.type),
        typeAdd: resolvedTypeAddForPersist(el),
        elementName: (el.elementName ?? "").trim(),
        valueSourceType: resolvedValueSourceTypeForSave(el),
        libraryCategory: resolvedLibraryCategoryForPersist(el),
        config: stripLabelConfigPrefixes((el.config ?? {}) as Record<string, unknown>),
      })),
    };
    const blob = new Blob([JSON.stringify(payload, null, 2)], {
      type: 'application/json',
    });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `label-template-${template.id}.json`;
    a.click();
    URL.revokeObjectURL(url);
  }, [template]);

  return (
    <div
      className="flex flex-col overflow-hidden"
      style={{
        flex: "1 1 0%",
        minHeight: 0,
        display: "flex",
        flexDirection: "column",
        overflow: "hidden",
      }}
    >
      {/* Toolbar */}
      <div className="flex items-center gap-2 px-4 py-2 border-b border-gray-200 bg-white shrink-0">
        <Button variant="outline" size="sm" onClick={onClose}>
          <ArrowLeft className="w-4 h-4 mr-1" />
          Back
        </Button>
        <span className="text-sm font-medium text-gray-700 truncate flex-1">
          {template.name}
        </span>
        <Button size="sm" onClick={handleExport} variant="outline">
          <Download className="w-4 h-4 mr-1" />
          Export JSON
        </Button>
        <Button
          size="sm"
          className="bg-blue-600 hover:bg-blue-700 text-white"
          onClick={handleSave}
        >
          <Save className="w-4 h-4 mr-1" />
          Save
        </Button>
      </div>

      {/* Template level editor (single block / three areas) */}
      <div className="shrink-0 border-b border-[#cfd9ea] bg-[#dde7f5] px-4 py-2">
        <div className="rounded-lg border border-[#c2d1e8] bg-[#e4ecf8] p-2">
          <div className="grid grid-cols-3 gap-2">
            <div className={topCellClass}>
              <Label className={topLabelClass}>Label Template Name</Label>
              <Input
                value={template.name}
                onChange={(e) => handleTemplateChange({ name: e.target.value })}
                className={topFieldClass}
              />
            </div>
            <div className={`${topCellClass} col-span-2`}>
              <Label className={topLabelClass}>Ruler</Label>
              <div className="flex flex-wrap items-center gap-1.5">
                <Select
                  value={template.unit}
                  onValueChange={(v: "cm" | "inch") => handleTemplateChange({ unit: v, showRuler: true })}
                >
                  <SelectTrigger className={`w-[72px] shrink-0 ${topSelectTriggerClass}`}>
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="inch" className="text-xs">
                      Inch
                    </SelectItem>
                    <SelectItem value="cm" className="text-xs">
                      cm
                    </SelectItem>
                  </SelectContent>
                </Select>
                <span className="shrink-0 text-center text-[11px] font-semibold text-[#1d3c8f]">W</span>
                <Input
                  type="number"
                  value={template.width}
                  onChange={(e) =>
                    handleTemplateChange({ width: Math.max(0.1, Number(e.target.value) || 0), showRuler: true })
                  }
                  className={`w-[72px] shrink-0 ${topFieldClass}`}
                  placeholder="Width"
                />
                <span className="shrink-0 text-center text-[11px] font-semibold text-[#1d3c8f]">H</span>
                <Input
                  type="number"
                  value={template.height}
                  onChange={(e) =>
                    handleTemplateChange({ height: Math.max(0.1, Number(e.target.value) || 0), showRuler: true })
                  }
                  className={`w-[72px] shrink-0 ${topFieldClass}`}
                  placeholder="Height"
                />
                <Select
                  value={(() => {
                    const i = PRESET_LABEL_SIZES.findIndex(
                      (p) =>
                        p.width === template.width && p.height === template.height && p.unit === template.unit,
                    );
                    return i >= 0 ? String(i) : "custom";
                  })()}
                  onValueChange={(v: string) => {
                    if (v === "custom") return;
                    const p = PRESET_LABEL_SIZES[Number(v)];
                    if (p) handleTemplateChange({ width: p.width, height: p.height, unit: p.unit, showRuler: true });
                  }}
                >
                  <SelectTrigger className={`w-[118px] shrink-0 ${topSelectTriggerClass}`}>
                    <SelectValue placeholder="Canvas size" />
                  </SelectTrigger>
                  <SelectContent>
                    {PRESET_LABEL_SIZES.map((p, i) => (
                      <SelectItem key={i} value={String(i)} className="text-xs">
                        {p.name}
                      </SelectItem>
                    ))}
                    <SelectItem value="custom" className="text-xs text-gray-500">
                      Custom
                    </SelectItem>
                  </SelectContent>
                </Select>
              </div>
            </div>

            <div className={topCellClass}>
              <Label className={topLabelClass}>Starter Template</Label>
              {!templateId ? (
                <Select value={selectedStarterCode} onValueChange={setSelectedStarterCode}>
                  <SelectTrigger className={topSelectTriggerClass}>
                    <SelectValue placeholder="Select starter template" />
                  </SelectTrigger>
                  <SelectContent>
                    {starterOptions.map((op) => (
                      <SelectItem key={op.code} value={op.code} className="text-xs">
                        {op.name}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              ) : (
                <Input value="Current template" disabled className={topFieldClass} />
              )}
            </div>
            <div className={topCellClass}>
              <Label className={topLabelClass}>Border</Label>
              <Select
                value={templateBorderValue}
                onValueChange={(v: "none" | "line" | "dotted") => setCanvasBorder(v)}
              >
                <SelectTrigger className={topSelectTriggerClass}>
                  <SelectValue />
                </SelectTrigger>
                <SelectContent>
                  <SelectItem value="none" className="text-xs">
                    none
                  </SelectItem>
                  <SelectItem value="line" className="text-xs">
                    line
                  </SelectItem>
                  <SelectItem value="dotted" className="text-xs">
                    dotted
                  </SelectItem>
                </SelectContent>
              </Select>
            </div>
            <div className={topCellClass}>
              <Label className={topLabelClass}>Rotation</Label>
              <div className="grid grid-cols-2 gap-1.5">
                <div className="col-span-2">
                  <Select
                    value={canvasRotation}
                    onValueChange={(v: "horizontal" | "vertical") => {
                      setCanvasRotation(v);
                      const isCurrentlyVertical = template.height > template.width;
                      const shouldBeVertical = v === "vertical";
                      if (isCurrentlyVertical !== shouldBeVertical) {
                        handleTemplateChange({ width: template.height, height: template.width });
                      }
                    }}
                  >
                    <SelectTrigger className={topSelectTriggerClass}>
                      <SelectValue />
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem value="horizontal" className="text-xs">
                        horizontal
                      </SelectItem>
                      <SelectItem value="vertical" className="text-xs">
                        vertical
                      </SelectItem>
                    </SelectContent>
                  </Select>
                </div>
              </div>
            </div>
            {loadingStarter ? (
              <div className="col-span-3 text-[11px] leading-tight text-[#1d3c8f]">Copying starter template…</div>
            ) : null}
          </div>
        </div>
      </div>

      {/* 三列：行布局内联；左侧整列 overflow-y:auto，保证橙色 Print input 可滚到 */}
      <div
        className="gap-2 bg-[#dde7f5] p-2"
        style={{
          flex: "1 1 0%",
          minHeight: 0,
          display: "flex",
          flexDirection: "row",
          alignItems: "stretch",
          gap: 8,
        }}
      >
        <div
          className="shrink-0 rounded-lg border border-[#c2d1e8] bg-[#e4ecf8] p-1"
          style={{
            width: "15rem",
            flexShrink: 0,
            alignSelf: "stretch",
            minHeight: 0,
            overflowX: "hidden",
            overflowY: "auto",
            WebkitOverflowScrolling: "touch",
            boxSizing: "border-box",
          }}
        >
          <ElementsPanel onAddElement={addElement} />
        </div>
        <div
          className="rounded-lg border border-[#c2d1e8] bg-[#e4ecf8] p-1"
          style={{
            flex: "1 1 0%",
            minHeight: 0,
            minWidth: 0,
            overflow: "hidden",
            display: "flex",
            flexDirection: "column",
          }}
        >
          <LabelCanvas
            template={template}
            canvasBorder={canvasBorder}
            selectedId={selectedId}
            onSelect={setSelectedId}
            onUpdateElement={updateElement}
            onDeleteElement={deleteElement}
            onTemplateChange={handleTemplateChange}
            scale={scale}
            onZoomIn={() => setScale((s) => Math.min(MAX_SCALE, s + SCALE_STEP))}
            onZoomOut={() => setScale((s) => Math.max(MIN_SCALE, s - SCALE_STEP))}
            onResetZoom={() => setScale(DEFAULT_SCALE)}
            onPreview={() => setPreviewOpen(true)}
            hideToolbarPresetSize
          />
        </div>
        <div
          className="rounded-lg border border-[#c2d1e8] bg-[#e4ecf8] p-1"
          style={{
            width: "18rem",
            flexShrink: 0,
            alignSelf: "stretch",
            minHeight: 0,
            overflow: "hidden",
            display: "flex",
            flexDirection: "column",
          }}
        >
          <PropertiesPanel
            template={template}
            selectedElement={selectedElement}
            onTemplateChange={handleTemplateChange}
            onElementChange={updateElement}
            onDeleteElement={deleteElement}
            readOnlyTemplateCode={!!templateId}
          />
        </div>
      </div>
      <Dialog open={previewOpen} onOpenChange={setPreviewOpen}>
        <DialogContent className="max-w-[90vw] max-h-[90vh] p-0 overflow-hidden flex flex-col">
          <DialogHeader className="shrink-0 px-6 py-4 border-b bg-white">
            <DialogTitle>Label preview</DialogTitle>
          </DialogHeader>
          <div className="flex-1 min-h-0 overflow-x-auto overflow-y-auto p-4 bg-gray-50">
            <div className="min-w-max">
              <LabelPreviewOnly template={template} canvasBorder={canvasBorder} maxWidth={0} />
            </div>
          </div>
        </DialogContent>
      </Dialog>
    </div>
  );
}
