"use client";

import * as React from "react";
import type { Sheet } from "@fortune-sheet/core";
import { Workbook, type WorkbookInstance } from "@fortune-sheet/react";
import "@fortune-sheet/react/dist/index.css";
import { useSession } from "next-auth/react";
import { toast } from "sonner";

import { useGetProfileQuery } from "@/api/endpoints/auth-api";
import { useAuthToken } from "@/hooks/use-auth-token";
import { useRevenueSheetLiveData } from "@/hooks/use-revenue-sheet-live-data";
import { canViewSalesTargets } from "@/lib/permissions";
import { cn } from "@/lib/utils";
import {
  cloneFortuneSheets,
  createBlankGridData,
  createDefaultFortuneSheets,
  ensureProtectedSheets,
  ensureWorkbookColumnCapacity,
  exportPayloadsToFortuneSheetsMutable,
  fortuneSheetsToExportPayloads,
  getWorkbookColumnCount,
  gridDataToFortuneSheets,
  sanitizeSheetsForWorkbookInput,
} from "./excel-grid-adapters";
import {
  EXCEL_EDITOR_DEFAULT_ROWS,
  EXCEL_EDITOR_MAX_COLS,
  REVENUE_SHEET_ID,
  REVENUE_SHEET_NAME,
} from "./excel-editor.constants";
import { applyRevenueCellUpdatesToSheet } from "./revenue-sheet-live-map";
import { cloneRevenueFortuneSheet } from "./revenue-sheet";
import type { ImportSampleSheetPayload } from "@/workers/import-sample-export.worker";
import "./excel-spreadsheet.css";

export type ExcelSpreadsheetHandle = {
  getExportSheets: () => ImportSampleSheetPayload[];
  setData: (data: string[][]) => void;
  reset: (data?: string[][]) => void;
  addSheet: () => void;
  undo: () => void;
  redo: () => void;
  insertRow: () => void;
  insertColumn: () => void;
};

type ExcelSpreadsheetProps = {
  initialSheets?: ImportSampleSheetPayload[];
  initialData?: string[][];
  className?: string;
  onDirty?: () => void;
};

export const ExcelSpreadsheet = React.forwardRef<
  ExcelSpreadsheetHandle,
  ExcelSpreadsheetProps
>(function ExcelSpreadsheet(
  { initialSheets, initialData, className, onDirty },
  ref,
) {
  const { data: session } = useSession();
  const { token } = useAuthToken();
  const { data: profile } = useGetProfileQuery(undefined, { skip: !token });
  const hostRef = React.useRef<HTMLDivElement>(null);
  const workbookRef = React.useRef<WorkbookInstance>(null);
  const onDirtyRef = React.useRef(onDirty);
  const restoreToastShownRef = React.useRef(false);
  const revenueSnapshotRef = React.useRef<Sheet | null>(null);
  const isApplyingLiveDataRef = React.useRef(false);
  const appliedLiveFingerprintRef = React.useRef("");

  const permissionSource = React.useMemo(
    () =>
      (session as { backendUser?: unknown } | null)?.backendUser ?? profile,
    [session, profile],
  );
  const revenueLiveEnabled = React.useMemo(
    () => canViewSalesTargets(permissionSource),
    [permissionSource],
  );
  const { cellUpdates, fingerprint, isSyncing, lastSyncedAt } =
    useRevenueSheetLiveData(revenueLiveEnabled);

  const workbookBootstrapRef = React.useRef<Sheet[]>(
    sanitizeSheetsForWorkbookInput(
      (() => {
        let initial: Sheet[];
        if (initialSheets?.length) {
          initial = exportPayloadsToFortuneSheetsMutable(initialSheets);
        } else if (initialData) {
          const [revenue, blankSheet] = createDefaultFortuneSheets();
          const userSheet = gridDataToFortuneSheets(initialData, "Sheet1")[0];
          if (revenue && userSheet) {
            userSheet.order = 1;
            userSheet.status = 1;
            revenue.status = 0;
            initial = cloneFortuneSheets([revenue, userSheet]);
          } else if (blankSheet) {
            initial = cloneFortuneSheets([revenue!, blankSheet]);
          } else {
            initial = createDefaultFortuneSheets();
          }
        } else {
          initial = createDefaultFortuneSheets();
        }
        return initial;
      })(),
    ),
  );

  const sheetsRef = React.useRef<Sheet[]>(workbookBootstrapRef.current);

  React.useEffect(() => {
    onDirtyRef.current = onDirty;
  }, [onDirty]);

  const applyLiveRevenueUpdates = React.useCallback(() => {
    if (
      !revenueLiveEnabled ||
      !fingerprint ||
      fingerprint === appliedLiveFingerprintRef.current ||
      !cellUpdates.length
    ) {
      return false;
    }

    const workbook = workbookRef.current;
    if (!workbook) return false;

    const currentSheets = sheetsRef.current;
    const revenueIndex = currentSheets.findIndex(
      (sheet) => sheet.id === REVENUE_SHEET_ID,
    );
    if (revenueIndex < 0) return false;

    const nextSheets = cloneFortuneSheets(currentSheets);
    nextSheets[revenueIndex] = applyRevenueCellUpdatesToSheet(
      nextSheets[revenueIndex],
      cellUpdates,
      { allMetricColumns: true },
    );
    const mutableSheets = sanitizeSheetsForWorkbookInput(nextSheets);

    isApplyingLiveDataRef.current = true;
    try {
      appliedLiveFingerprintRef.current = fingerprint;
      const updatedRevenue = mutableSheets[revenueIndex];
      revenueSnapshotRef.current = cloneRevenueFortuneSheet(updatedRevenue);
      sheetsRef.current = mutableSheets;
      workbook.updateSheet(sanitizeSheetsForWorkbookInput(mutableSheets));
      workbook.activateSheet?.({ id: REVENUE_SHEET_ID });
      workbook.calculateFormula(REVENUE_SHEET_ID);
    } finally {
      window.setTimeout(() => {
        isApplyingLiveDataRef.current = false;
      }, 100);
    }

    return true;
  }, [cellUpdates, fingerprint, revenueLiveEnabled]);

  React.useEffect(() => {
    if (!applyLiveRevenueUpdates()) {
      const timer = window.setInterval(() => {
        if (applyLiveRevenueUpdates()) {
          window.clearInterval(timer);
        }
      }, 200);
      return () => window.clearInterval(timer);
    }
  }, [applyLiveRevenueUpdates]);

  const handleChange = React.useCallback((nextSheets: Sheet[]) => {
    if (isApplyingLiveDataRef.current) return;

    let expandedSheets = ensureWorkbookColumnCapacity(cloneFortuneSheets(nextSheets));

    const revenueSheet = expandedSheets.find(
      (sheet) => sheet.id === REVENUE_SHEET_ID,
    );
    if (revenueSheet) {
      revenueSnapshotRef.current = cloneRevenueFortuneSheet(revenueSheet);
    }

    const protectedResult = ensureProtectedSheets(
      expandedSheets,
      revenueSnapshotRef.current,
    );
    if (protectedResult.restored) {
      expandedSheets = ensureWorkbookColumnCapacity(
        cloneFortuneSheets(protectedResult.sheets),
      );
      if (!restoreToastShownRef.current) {
        restoreToastShownRef.current = true;
        toast.error(`"${REVENUE_SHEET_NAME}" sheet cannot be deleted.`);
        window.setTimeout(() => {
          restoreToastShownRef.current = false;
        }, 1500);
      }
    }

    sheetsRef.current = expandedSheets;
    if (!isApplyingLiveDataRef.current) {
      onDirtyRef.current?.();
    }
  }, []);

  const handleSelectionChange = React.useCallback(
    (_sheetId: string, selection: { column?: number[] }) => {
      const activeCol =
        selection.column?.[1] ?? selection.column?.[0] ?? undefined;
      if (activeCol == null) return;

      const prevSheets = sheetsRef.current;
      const prevCols = getWorkbookColumnCount(prevSheets);
      const expandedSheets = ensureWorkbookColumnCapacity(
        cloneFortuneSheets(prevSheets),
        activeCol,
      );
      if (getWorkbookColumnCount(expandedSheets) === prevCols) return;

      const mutableSheets = sanitizeSheetsForWorkbookInput(expandedSheets);
      sheetsRef.current = mutableSheets;
      workbookRef.current?.updateSheet(
        sanitizeSheetsForWorkbookInput(mutableSheets),
      );
    },
    [],
  );

  const workbookHooks = React.useMemo(
    () => ({ afterSelectionChange: handleSelectionChange }),
    [handleSelectionChange],
  );

  const resetHorizontalScroll = React.useCallback(() => {
    workbookRef.current?.scroll?.({ scrollLeft: 0 });
    const scrollbar = hostRef.current?.querySelector<HTMLDivElement>(
      ".luckysheet-scrollbar-x",
    );
    if (scrollbar) scrollbar.scrollLeft = 0;
  }, []);

  React.useEffect(() => {
    const frame = window.requestAnimationFrame(() => {
      resetHorizontalScroll();
    });
    return () => window.cancelAnimationFrame(frame);
    // Only normalize scroll once when the editor mounts.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [resetHorizontalScroll]);

  const applySheets = React.useCallback((nextSheets: Sheet[]) => {
    const mutableSheets = sanitizeSheetsForWorkbookInput(nextSheets);
    sheetsRef.current = mutableSheets;
    workbookRef.current?.updateSheet(sanitizeSheetsForWorkbookInput(mutableSheets));
  }, []);

  React.useImperativeHandle(
    ref,
    () => ({
      getExportSheets: () => {
        const allSheets =
          workbookRef.current?.getAllSheets() ?? sheetsRef.current;
        return fortuneSheetsToExportPayloads(allSheets, workbookRef.current);
      },
      setData: (data) => {
        applySheets(gridDataToFortuneSheets(data));
      },
      reset: (data) => {
        applySheets(gridDataToFortuneSheets(data ?? createBlankGridData()));
      },
      addSheet: () => {
        workbookRef.current?.addSheet();
        onDirtyRef.current?.();
      },
      undo: () => {
        workbookRef.current?.handleUndo();
      },
      redo: () => {
        workbookRef.current?.handleRedo();
      },
      insertRow: () => {
        const selection = workbookRef.current?.getSelection()?.[0];
        const row = selection?.row?.[0] ?? 0;
        workbookRef.current?.insertRowOrColumn("row", row, 1);
      },
      insertColumn: () => {
        const selection = workbookRef.current?.getSelection()?.[0];
        const column = selection?.column?.[0] ?? 0;
        workbookRef.current?.insertRowOrColumn("column", column, 1);
      },
    }),
    [applySheets],
  );

  return (
    <div
      ref={hostRef}
      className={cn(
        "excel-spreadsheet-host relative h-full min-h-0 min-w-0 flex-1 overflow-hidden",
        className,
      )}
    >
      {revenueLiveEnabled ? (
        <div className="pointer-events-none absolute right-3 top-2 z-10 flex items-center gap-1.5 rounded-md border border-emerald-200/80 bg-emerald-50/95 px-2 py-1 text-[11px] font-medium text-emerald-800 shadow-sm">
          <span
            className={cn(
              "size-1.5 rounded-full bg-emerald-500",
              isSyncing && "animate-pulse",
            )}
          />
          Revenue live
          {lastSyncedAt
            ? ` · ${new Date(lastSyncedAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}`
            : null}
        </div>
      ) : null}
      <Workbook
        ref={workbookRef}
        data={workbookBootstrapRef.current}
        onChange={handleChange}
        hooks={workbookHooks}
        showToolbar
        showFormulaBar
        showSheetTabs
        allowEdit
        row={EXCEL_EDITOR_DEFAULT_ROWS}
        column={EXCEL_EDITOR_MAX_COLS}
        lang="en"
      />
    </div>
  );
});
