"use client";

import * as React from "react";
import dynamic from "next/dynamic";
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogFooter,
  DialogDescription,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { GenericData } from "./export-excel-form";
import * as GC from "@mescius/spread-sheets";
import { toast } from "sonner";
import { updateFormDataInDB } from "@/lib/forms/services";
import { Loader, Save } from "lucide-react";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import "@mescius/spread-sheets/styles/gc.spread.sheets.excel2016colorful.css";

// Dynamically load the SpreadSheets component (disable SSR)
const SpreadSheets = dynamic(
  () => import("@mescius/spread-sheets-react").then((mod) => mod.SpreadSheets),
  {
    ssr: false,
    loading: () => (
      <div className="flex items-center justify-center h-full">
        <Loader className="mr-2 h-4 w-4 animate-spin" />
        Loading SpreadSheets...
      </div>
    ),
  }
);

// Apply license key on the client only
if (typeof window !== "undefined" && process.env.NEXT_PUBLIC_SPREAD_LICENSE) {
  GC.Spread.Sheets.LicenseKey = process.env.NEXT_PUBLIC_SPREAD_LICENSE;
}

interface TemplateItem {
  TMPL_ID: string;
  NAME: string;
  TMPL_TYPE: string;
  SPR_LST_SETUP: {
    ACT_SHEET: string;
    HIDN_SHEETS: Array<string>;
    CONTENT?: string;
    DATA_SHEETS: Array<{
      SHEET_NAME: string;
      REG_TYPE_ID: string;
      MAP_CELL_ATT: Array<{
        ATT_ID: string;
        IN: string;
      }>;
    }>;
  };
  GRD_LST_SETUP: {
    REG_TYPE_ID: string;
    SPR_ITM_IDS: Array<string>;
    ATTS: Array<{}>;
  };
  SPR_ITM_LST_SETUP: {
    ACT_SHEET: string;
    HIDN_SHEETS: Array<string>;
    CONTENT?: string;
    DATA_SHEETS: Array<{
      SHEET_NAME: string;
      REG_TYPE_ID: string;
      MAP_CELL_ATT: Array<{
        ATT_ID: string;
        IN: string;
      }>;
    }>;
  };
}

interface TemplateViewDialogProps {
  isOpen: boolean;
  onClose: () => void;
  templateData: TemplateItem[] | any;
  selectedRow: GenericData;
  formCode: string;
  contractItemId: number;
  editableFieldsMap?: Map<string, string[]>; // editable field info per tag
  onUpdateSuccess?: (updatedValues: Record<string, any>) => void;
}

export function TemplateViewDialog({
  isOpen,
  onClose,
  templateData,
  selectedRow,
  formCode,
  contractItemId,
  editableFieldsMap = new Map(),
  onUpdateSuccess,
}: TemplateViewDialogProps) {
  /* ------------------------- local state ------------------------- */
  const [hostStyle] = React.useState({ width: "100%", height: "100%" });
  const [isPending, setIsPending] = React.useState(false);
  const [hasChanges, setHasChanges] = React.useState(false);
  const [currentSpread, setCurrentSpread] = React.useState<GC.Spread.Sheets.Workbook | null>(
    null
  );
  const [selectedTemplateId, setSelectedTemplateId] = React.useState<string>("");
  const [cellMappings, setCellMappings] = React.useState<
    Array<{ attId: string; cellAddress: string; isEditable: boolean }>
  >([]);
  const [isClient, setIsClient] = React.useState(false);

  // Render only on client side
  React.useEffect(() => {
    setIsClient(true);
  }, []);

  /* ------------------------- helpers ------------------------- */
  // Normalize template list and keep only those with CONTENT
  const normalizedTemplates = React.useMemo((): TemplateItem[] => {
    if (!templateData) return [];

    const list = Array.isArray(templateData)
      ? (templateData as TemplateItem[])
      : ([templateData] as TemplateItem[]);

    return list.filter(
      (t) => t.SPR_LST_SETUP?.CONTENT || t.SPR_ITM_LST_SETUP?.CONTENT
    );
  }, [templateData]);

  // Choose currently selected template
  const selectedTemplate = React.useMemo(() => {
    if (!selectedTemplateId) return normalizedTemplates[0];
    return (
      normalizedTemplates.find((t) => t.TMPL_ID === selectedTemplateId) ||
      normalizedTemplates[0]
    );
  }, [normalizedTemplates, selectedTemplateId]);

  // Editable fields for the current TAG_NO
  const editableFields = React.useMemo(() => {
    if (!selectedRow?.TAG_NO) return [];
    return editableFieldsMap.get(selectedRow.TAG_NO) || [];
  }, [selectedRow?.TAG_NO, editableFieldsMap]);

  const isFieldEditable = React.useCallback(
    (attId: string) => {
      // TAG_NO and TAG_DESC are always editable
      if (attId === "TAG_NO" || attId === "TAG_DESC") return true;
      if (!selectedRow?.TAG_NO) return false;
      return editableFields.includes(attId);
    },
    [selectedRow?.TAG_NO, editableFields]
  );

  /** Convert a cell address like "M1" into {row:0,col:12}. */
  const parseCellAddress = (addr: string): { row: number; col: number } | null => {
    if (!addr) return null;
    const match = addr.match(/^([A-Z]+)(\d+)$/);
    if (!match) return null;
    const [, colStr, rowStr] = match;
    let col = 0;
    for (let i = 0; i < colStr.length; i++) {
      col = col * 26 + (colStr.charCodeAt(i) - 65 + 1);
    }
    col -= 1;
    const row = parseInt(rowStr, 10) - 1;
    return { row, col };
  };

  // Auto‑select first template
  React.useEffect(() => {
    if (normalizedTemplates.length && !selectedTemplateId) {
      setSelectedTemplateId(normalizedTemplates[0].TMPL_ID);
    }
  }, [normalizedTemplates, selectedTemplateId]);

  /* ------------------------- init spread ------------------------- */
  const initSpread = React.useCallback(
    (spread: GC.Spread.Sheets.Workbook | undefined) => {
      if (!spread || !selectedTemplate || !selectedRow) return;

      setCurrentSpread(spread);
      setHasChanges(false);

      // Pick content JSON and data‑sheet mapping
      const contentJson =
        selectedTemplate.SPR_LST_SETUP?.CONTENT ??
        selectedTemplate.SPR_ITM_LST_SETUP?.CONTENT;
      const dataSheets =
        selectedTemplate.SPR_LST_SETUP?.DATA_SHEETS ??
        selectedTemplate.SPR_ITM_LST_SETUP?.DATA_SHEETS;
      if (!contentJson) return;

      // Prepare shared styles once
      const editableStyle = new GC.Spread.Sheets.Style();
      editableStyle.backColor = "#f0fdf4";
      editableStyle.locked = false;

      const readOnlyStyle = new GC.Spread.Sheets.Style();
      readOnlyStyle.backColor = "#f9fafb";
      readOnlyStyle.foreColor = "#6b7280";
      readOnlyStyle.locked = true;

      const jsonObj = typeof contentJson === "string" ? JSON.parse(contentJson) : contentJson;

      const sheet = spread.getActiveSheet();

      /* -------- batch load + style -------- */
      sheet.suspendPaint();
      sheet.suspendCalcService(true);
      try {
        spread.fromJSON(jsonObj);
        sheet.options.isProtected = false;

        const mappings: Array<{ attId: string; cellAddress: string; isEditable: boolean }> = [];

        if (dataSheets?.length) {
          dataSheets.forEach((ds) => {
            ds.MAP_CELL_ATT?.forEach(({ ATT_ID, IN }) => {
              if (!IN) return;
              const pos = parseCellAddress(IN);
              if (!pos) return;
              const editable = isFieldEditable(ATT_ID);
              mappings.push({ attId: ATT_ID, cellAddress: IN, isEditable: editable });
            });
          });
        }

        // Apply values + style in chunks for large templates
        const CHUNK = 500;
        let idx = 0;
        const applyChunk = () => {
          const end = Math.min(idx + CHUNK, mappings.length);
          for (; idx < end; idx++) {
            const { attId, cellAddress, isEditable } = mappings[idx];
            const pos = parseCellAddress(cellAddress)!;
            if (selectedRow[attId] !== undefined && selectedRow[attId] !== null) {
              sheet.setValue(pos.row, pos.col, selectedRow[attId]);
            }
            sheet.setStyle(pos.row, pos.col, isEditable ? editableStyle : readOnlyStyle);
          }
          if (idx < mappings.length) {
            requestAnimationFrame(applyChunk);
          } else {
            // enable protection & events after styling done
            sheet.options.isProtected = true;
            sheet.options.protectionOptions = {
              allowSelectLockedCells: true,
              allowSelectUnlockedCells: true,
            } as any;

            // Cell/value change events
            sheet.bind(GC.Spread.Sheets.Events.ValueChanged, () => setHasChanges(true));
            sheet.bind(GC.Spread.Sheets.Events.CellChanged, () => setHasChanges(true));

            // Prevent editing read‑only fields
            sheet.bind(
              GC.Spread.Sheets.Events.EditStarting,
              (event: any, info: any) => {
                const map = mappings.find((m) => {
                  const pos = parseCellAddress(m.cellAddress);
                  return pos && pos.row === info.row && pos.col === info.col;
                });
                if (map && !map.isEditable) {
                  toast.warning(`${map.attId} field is read‑only`);
                  info.cancel = true;
                }
              }
            );

            setCellMappings(mappings);
            sheet.resumeCalcService(false);
            sheet.resumePaint();
          }
        };
        applyChunk();
      } catch (err) {
        console.error(err);
        toast.error("Failed to load template");
        sheet.resumeCalcService(false);
        sheet.resumePaint();
      }
    },
    [selectedTemplate, selectedRow, isFieldEditable]
  );

  /* ------------------------- handlers ------------------------- */
  const handleTemplateChange = (id: string) => {
    setSelectedTemplateId(id);
    setHasChanges(false);
    if (currentSpread) {
      // re‑init after a short tick so component remounts SpreadSheets
      setTimeout(() => initSpread(currentSpread), 50);
    }
  };

  const handleSaveChanges = React.useCallback(async () => {
    if (!currentSpread || !hasChanges || !selectedRow) {
      toast.info("No changes to save");
      return;
    }

    setIsPending(true);

    try {
      const sheet = currentSpread.getActiveSheet();
      const payload: Record<string, any> = { ...selectedRow };

      cellMappings.forEach((m) => {
        if (m.isEditable) {
          const pos = parseCellAddress(m.cellAddress);
          if (pos) payload[m.attId] = sheet.getValue(pos.row, pos.col);
        }
      });

      payload.TAG_NO = selectedRow.TAG_NO; // never change TAG_NO

      const { success, message } = await updateFormDataInDB(
        formCode,
        contractItemId,
        payload
      );

      if (!success) {
        toast.error(message);
        return;
      }

      toast.success("Changes saved successfully!");
      onUpdateSuccess?.({ ...selectedRow, ...payload });
      setHasChanges(false);
    } catch (err) {
      console.error(err);
      toast.error("An unexpected error occurred while saving");
    } finally {
      setIsPending(false);
    }
  }, [currentSpread, hasChanges, selectedRow, cellMappings, formCode, contractItemId, onUpdateSuccess]);

  /* ------------------------- render ------------------------- */
  if (!isOpen) return null;

  return (
    <Dialog open={isOpen} onOpenChange={onClose}>
      <DialogContent className="w-[80%] max-w-none h-[80vh] flex flex-col" style={{ maxWidth: "80vw" }}>
        <DialogHeader className="flex-shrink-0">
          <DialogTitle>SEDP Template – {formCode}</DialogTitle>
          <DialogDescription>
            {selectedRow && `Selected TAG_NO: ${selectedRow.TAG_NO || "N/A"}`}
            {hasChanges && <span className="ml-2 text-orange-600 font-medium">• Unsaved changes</span>}
            <br />
            <div className="flex items-center gap-4 mt-2">
              <span className="text-xs text-muted-foreground">
                <span className="inline-block w-3 h-3 bg-green-100 border border-green-400 mr-1" />
                Editable fields
              </span>
              <span className="text-xs text-muted-foreground">
                <span className="inline-block w-3 h-3 bg-gray-100 border border-gray-300 mr-1" />
                Read‑only fields
              </span>
              {!!cellMappings.length && (
                <span className="text-xs text-blue-600">
                  {cellMappings.filter((m) => m.isEditable).length} of {cellMappings.length} fields editable
                </span>
              )}
            </div>
          </DialogDescription>
        </DialogHeader>

        {/* Template selector */}
        {normalizedTemplates.length > 1 && (
          <div className="flex-shrink-0 px-4 py-2 border-b">
            <div className="flex items-center gap-2">
              <label className="text-sm font-medium">Template:</label>
              <Select value={selectedTemplateId} onValueChange={handleTemplateChange}>
                <SelectTrigger className="w-64">
                  <SelectValue placeholder="Select a template" />
                </SelectTrigger>
                <SelectContent>
                  {normalizedTemplates.map((t) => (
                    <SelectItem key={t.TMPL_ID} value={t.TMPL_ID}>
                      <div className="flex flex-col">
                        <span>{t.NAME || `Template ${t.TMPL_ID.slice(0, 8)}`}</span>
                        <span className="text-xs text-muted-foreground">{t.TMPL_TYPE}</span>
                      </div>
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
              <span className="text-xs text-muted-foreground">({normalizedTemplates.length} templates available)</span>
            </div>
          </div>
        )}

        {/* Spreadsheet */}
        <div className="flex-1 overflow-hidden">
          {selectedTemplate && isClient ? (
            <SpreadSheets key={selectedTemplateId} workbookInitialized={initSpread} hostStyle={hostStyle} />
          ) : (
            <div className="flex items-center justify-center h-full text-muted-foreground">
              {!isClient ? (
                <>
                  <Loader className="mr-2 h-4 w-4 animate-spin" /> Loading...
                </>
              ) : (
                "No template available"
              )}
            </div>
          )}
        </div>

        {/* footer */}
        <DialogFooter className="flex-shrink-0">
          <Button variant="outline" onClick={onClose}>
            Close
          </Button>
          {hasChanges && (
            <Button variant="default" onClick={handleSaveChanges} disabled={isPending}>
              {isPending ? (
                <>
                  <Loader className="mr-2 h-4 w-4 animate-spin" /> Saving...
                </>
              ) : (
                <>
                  <Save className="mr-2 h-4 w-4" /> Save Changes
                </>
              )}
            </Button>
          )}
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
