import Button from '@mui/material/Button';
import { CustomBreadcrumbs } from 'src/components/custom-breadcrumbs';
import { DashboardContent } from 'src/layouts/dashboard';
import { RouterLink } from 'src/routes/components';
import { paths } from 'src/routes/paths';
import React, { useCallback, useEffect, useMemo, useRef, useState, startTransition } from 'react';
import { useNavigate } from 'react-router';
import type {
  GridColDef,
  GridFilterModel,
  GridRowId,
  GridRowSelectionModel,
  GridSortModel,
  GridToolbarProps,
  GridPaginationModel} from '@mui/x-data-grid-premium';
import {
  DataGridPremium,
  useGridApiRef
} from '@mui/x-data-grid-premium';
import { unstable_batchedUpdates } from 'react-dom';

import {
  ArticleTwoTone,
  AutorenewTwoTone,
  DeleteSweepTwoTone,
  EditNoteTwoTone,
  FileOpenTwoTone,
  HighlightOffTwoTone,
  UploadFileTwoTone,
} from '@mui/icons-material';
import { CustomToolbar } from '../components/CustomToolbar';
import { formatRupiah } from 'src/shared/FormatRupiah/FormatRupiah';
import { FG_STATUS_DN } from '../constant';
import ModalDeleteDn from '../components/dialog/ModalDeleteDn';
import ModalUploadDn from '../components/dialog/ModalUploadDn';
import ModalCancelDn from '../components/dialog/ModalCancelDn';
import ModalCetakPdfDn from '../components/dialog/ModalCetakPdfDn';
import useGetDn from '../hooks/useGetDn';
import { enqueueSnackbar } from 'notistack';
import { usePaginationStore } from '../store/paginationStore';
import StatusChip from '../components/StatusChip';
import { useDebounce, useThrottle } from 'src/shared/hooks/useDebounceThrottle';
import useGetKodeObjekPajak from '../hooks/useGetKodeObjekPajak';
import useAdvancedFilter from '../hooks/useAdvancedFilterDn';

export type IColumnGrid = GridColDef & {
  field:
    | 'fgStatus'
    | 'noBupot'
    | 'masaPajak'
    | 'tahunPajak'
    | 'kdObjPjk'
    | 'pasalPPh'
    | 'npwp'
    | 'nama'
    | 'dpp'
    | 'pphDipotong'
    | 'idTku'
    | 'dokReferensi'
    | 'nomorDokumen'
    | 'created_by'
    | 'created_at'
    | 'updated_by'
    | 'updated_at'
    | 'internal_id'
    | 'keterangan1'
    | 'keterangan2'
    | 'keterangan3'
    | 'keterangan4'
    | 'keterangan5';
  valueOptions?: string[];
};

type TKodeObjekPajak = {
  kode: string;
  nama: string;
  pasal: string;
  statuspph: string;
};

export function DnListView() {
  const apiRef = useGridApiRef();
  const navigate = useNavigate();

  const tableKey = 'dn';

  const page = usePaginationStore((s) => s.tables[tableKey]?.page ?? 0);
  const pageSize = usePaginationStore((s) => s.tables[tableKey]?.pageSize ?? 10);
  const setPagination = usePaginationStore((s) => s.setPagination);
  const resetPagination = usePaginationStore((s) => s.resetPagination);

  const [filterModel, setFilterModel] = useState<GridFilterModel>({ items: [] });
  const [sortModel, setSortModel] = useState<GridSortModel>([]);
  const [rowSelectionModel, setRowSelectionModel] = useState<GridRowSelectionModel | undefined>(
    undefined
  );

  const [isDeleteModalOpen, setIsDeleteModalOpen] = useState<boolean>(false);
  const [isUploadModalOpen, setIsUploadModalOpen] = useState<boolean>(false);
  const [isCancelModalOpen, setIsCancelModalOpen] = useState<boolean>(false);
  const [isPreviewOpen, setIsPreviewOpen] = useState<boolean>(false);
  const [previewPayload, setPreviewPayload] = useState<Record<string, any> | undefined>(undefined);

  const dataSelectedRef = useRef<any[]>([]);
  const [selectionVersion, setSelectionVersion] = useState(0);
  const [kodeObjekPajaks, setKodeObjekPajaks] = useState<TKodeObjekPajak[]>([]);

  const { data: kodeObjekPajak, isLoading: isLoadingKop } = useGetKodeObjekPajak();

  const { buildAdvancedFilter, buildRequestParams } = useAdvancedFilter();

  // const buildAdvancedFilter = useCallback((filters?: GridFilterModel['items']) => {
  //   if (!filters || filters.length === 0) return '';

  //   return filters
  //     .map((f) => {
  //       if (!f.value || !f.field) return null;
  //       const field = `LOWER("${f.field}")`;
  //       const val = String(f.value).toLowerCase();
  //       switch (f.operator) {
  //         case 'contains':
  //           return `${field} LIKE '%${val}%'`;
  //         case 'equals':
  //         case 'is':
  //           return `${field} = '${val}'`;
  //         case 'isNot':
  //           return `${field} <> '${val}'`;
  //         default:
  //           return null;
  //       }
  //     })
  //     .filter(Boolean)
  //     .join(' AND ');
  // }, []);

  // ---------- params memo (uses pagination from Zustand) ----------
  // const params = useMemo(() => {
  //   const advanced = buildAdvancedFilter(filterModel.items);

  //   return {
  //     page,
  //     limit: pageSize,
  //     noBupot: '',
  //     idDipotong: '',
  //     namaDipotong: '',
  //     msPajak: '',
  //     thnPajak: '',
  //     advanced,
  //     sortingMode: sortModel[0]?.field ?? '',
  //     sortingMethod: sortModel[0]?.sort ?? '',
  //   };
  // }, [page, pageSize, sortModel, filterModel.items, buildAdvancedFilter]);
  const params = useMemo(() => {
    const advanced = buildAdvancedFilter(filterModel.items);

    const baseParams = {
      page,
      limit: pageSize,
      noBupot: '', // tetap pakai noBupot di UI; buildRequestParams akan rename jadi nomorBupot
      idDipotong: '',
      namaDipotong: '',
      msPajak: '',
      thnPajak: '',
      sortingMode: sortModel[0]?.field ?? '',
      sortingMethod: sortModel[0]?.sort ?? '',
    };

    // buildRequestParams akan menambahkan field advanced dan mengganti noBupot -> nomorBupot
    return buildRequestParams(baseParams, advanced);
  }, [page, pageSize, sortModel, filterModel.items, buildAdvancedFilter, buildRequestParams]);

  const { data, isFetching, refetch } = useGetDn({
    params,
  });
  const idStatusMapRef = useRef<Map<string | number, string>>(new Map());
  const rows = useMemo(() => (data?.data || []).filter(Boolean), [data?.data]);
  const totalRows = Number(data?.total ?? 0);

  useEffect(() => {
    try {
      const m = new Map<string | number, string>();
      (rows || []).forEach((r: any) => {
        const id = String(r.id ?? r.internal_id ?? '');
        m.set(id, r?.fgStatus ?? '');
      });
      // swap reference (cheap)
      idStatusMapRef.current = m;
    } catch {
      idStatusMapRef.current = new Map();
    }
  }, [rows]);

  const throttledPaginationChange = useThrottle((model: GridPaginationModel) => {
    if (model.pageSize !== pageSize) {
      setPagination(tableKey, { page: 0, pageSize: model.pageSize });
    } else {
      setPagination(tableKey, { page: model.page });
    }
  }, 250);

  const debouncedFilterChange = useDebounce((model: GridFilterModel) => {
    setFilterModel(model);
    resetPagination(tableKey);
  }, 400);

  const debouncedSortChange = useDebounce((model: GridSortModel) => {
    setSortModel(model);
    resetPagination(tableKey);
  }, 400);

  const paginationModel: GridPaginationModel = {
    page,
    pageSize,
  };

  // ---------- status options and columns (kept identical to your original) ----------
  type Status = 'draft' | 'normal' | 'cancelled' | 'amended';
  type StatusOption = { value: Status; label: string };
  const statusOptions: StatusOption[] = [
    { value: 'draft', label: 'Draft' },
    { value: 'normal', label: 'Normal' },
    { value: 'cancelled', label: 'Dibatalkan' },
    { value: 'amended', label: 'Normal Pengganti' },
  ];

  const columns = useMemo<IColumnGrid[]>(
    () => [
      {
        field: 'fgStatus',
        headerName: 'Status',
        width: 200,
        type: 'singleSelect',
        valueOptions: statusOptions.map((opt) => opt.value),
        // valueFormatter: (params: any) => {
        //   const option = statusOptions.find((opt) => opt.value === params.value);
        //   return option ? option.label : (params.value as string);
        // },
        valueFormatter: ({ value }: { value: string }) => {
          const option = statusOptions.find((opt) => opt.value === value);
          return option ? option.label : value;
        },
        renderCell: ({ value, row }) => <StatusChip value={value} revNo={row.revNo} />,
      },
      { field: 'noBupot', headerName: 'Nomor Bukti Pemotongan', width: 200 },
      { field: 'masaPajak', headerName: 'Masa Pajak', width: 150 },
      { field: 'tahunPajak', headerName: 'Tahun Pajak', width: 150 },
      { field: 'kdObjPjk', headerName: 'Kode Objek Pajak', width: 150 },
      { field: 'npwp', headerName: 'Identitas', width: 150 },
      { field: 'nama', headerName: 'Nama', width: 150 },
      {
        field: 'dpp',
        headerName: 'Jumlah Penghasilan Bruto (Rp)',
        width: 150,
        renderCell: ({ row }) => formatRupiah(row.dpp),
      },
      {
        field: 'pphDipotong',
        headerName: 'Jumlah PPh Terutang (Rp)',
        width: 200,
        renderCell: ({ row }) => formatRupiah(row.pphDipotong),
      },
      { field: 'idTku', headerName: 'NITKU Pemotong', width: 150 },
      { field: 'dokReferensi', headerName: 'Nama dokumen', width: 150 },
      { field: 'nomorDokumen', headerName: 'Nomor dokumen', width: 150 },
      { field: 'created_by', headerName: 'Created', width: 150 },
      { field: 'created_at', headerName: 'Created At', width: 200 },
      { field: 'updated_by', headerName: 'Updated', width: 150 },
      { field: 'updated_at', headerName: 'Update At', width: 150 },
      { field: 'internal_id', headerName: 'Referensi', width: 150 },
      { field: 'keterangan1', headerName: 'Keterangan 1', width: 150 },
      { field: 'keterangan2', headerName: 'Keterangan 2', width: 200 },
      { field: 'keterangan3', headerName: 'Keterangan 3', width: 150 },
      { field: 'keterangan4', headerName: 'Keterangan 4', width: 150 },
      { field: 'keterangan5', headerName: 'Keterangan 5', width: 150 },
    ],
    [statusOptions]
  );

  // --- selection helpers (kept same)
  const normalizeSelectionToArray = (raw: unknown): GridRowId[] => {
    if (!raw) return [];
    if (typeof raw === 'object' && raw !== null && 'ids' in (raw as any)) {
      const ids = (raw as any).ids;
      if (ids instanceof Set) return Array.from(ids) as GridRowId[];
      if (Array.isArray(ids)) return ids as GridRowId[];
      if (ids instanceof Map) return Array.from((ids as Map<any, any>).keys()) as GridRowId[];
      if (typeof ids === 'object' && ids !== null) return Object.keys(ids) as GridRowId[];
    }
    if (Array.isArray(raw)) return raw as GridRowId[];
    if (raw instanceof Set) return Array.from(raw) as GridRowId[];
    if (raw instanceof Map) return Array.from((raw as Map<any, any>).keys()) as GridRowId[];

    if (typeof raw === 'object' && raw !== null) {
      const obj = raw as Record<string, any>;
      const keys = Object.keys(obj).filter((k) => !!obj[k]);
      if (keys.length) return keys as GridRowId[];
    }

    try {
      if ((raw as any)[Symbol.iterator]) {
        return Array.from(raw as Iterable<unknown>) as GridRowId[];
      }
    } catch {
      /* ignore */
    }

    return [];
  };

  const getSelectedRowByKey = (key?: GridRowId | 'all') => {
    const api = apiRef.current;
    if (!api) return null;

    const selectedRows = Array.from(api.getSelectedRows().values());
    if (key === 'all') return selectedRows;
    return selectedRows[0] ?? null;
  };

  const handleEditData = useCallback(
    (type = 'ubah') => {
      const selectedRow = dataSelectedRef.current[0];
      if (!selectedRow) return;
      navigate(`/unifikasi/dn/${selectedRow.id}/${type}`);
    },
    [navigate]
  );

  const throttledSelectionChange = useThrottle((newSelection: any) => {
    if (!apiRef.current) return;
    const ids =
      newSelection?.ids instanceof Set ? Array.from(newSelection.ids) : newSelection || [];
    const selectedData = ids.map((id: any) => apiRef.current!.getRow(id)).filter(Boolean);

    unstable_batchedUpdates(() => {
      dataSelectedRef.current = selectedData;
      setRowSelectionModel(newSelection);
      setSelectionVersion((v) => v + 1);
    });
  }, 150);

  // auto sync selected after rows set (schedule as idle to avoid heavy main thread work)
  useEffect(() => {
    const api = apiRef.current;
    if (!api) return;
    const unsubscribe = api.subscribeEvent('rowsSet', () => {
      const exec = () => {
        const ids =
          api.state?.rowSelection?.ids instanceof Set ? Array.from(api.state.rowSelection.ids) : [];
        const updatedSelected = ids.map((id) => api.getRow(id)).filter(Boolean);
        dataSelectedRef.current = updatedSelected;
        setSelectionVersion((v) => v + 1);
      };
      if ((window as any).requestIdleCallback) (window as any).requestIdleCallback(exec);
      else setTimeout(exec, 0);
    });
    // eslint-disable-next-line consistent-return
    return () => unsubscribe();
  }, [apiRef]);

  // ---------- memoized toolbar validation (avoid recompute heavy every click) ----------
  const validatedActions = useMemo(() => {
    const dataSelected = dataSelectedRef.current;
    const count = dataSelected.length;
    const hasSelection = count > 0;
    if (!hasSelection) {
      return {
        canDetail: false,
        canEdit: false,
        canDelete: false,
        canUpload: false,
        canReplacement: false,
        canCancel: false,
      };
    }
    const allDraft = dataSelected.every((d) => d.fgStatus === FG_STATUS_DN.DRAFT);
    const allNormal = dataSelected.every((d) => d.fgStatus === FG_STATUS_DN.NORMAL_DONE);
    return {
      canDetail: count === 1,
      canEdit: count === 1 && allDraft,
      canDelete: hasSelection && allDraft,
      canUpload: hasSelection && allDraft,
      canReplacement: count === 1 && dataSelected[0].fgStatus === FG_STATUS_DN.NORMAL_DONE,
      canCancel: hasSelection && allNormal,
    };
  }, [selectionVersion]);

  useEffect(() => {
    if (kodeObjekPajak?.data && Array.isArray(kodeObjekPajak.data)) {
      setKodeObjekPajaks(kodeObjekPajak.data);
    }
  }, [kodeObjekPajak]);

  // ✅ Handler tombol “Detail”
  const handleOpenPreview = () => {
    const selectedRow = dataSelectedRef.current?.[0];
    if (!selectedRow) {
      enqueueSnackbar('Pilih 1 baris untuk melihat detail', { variant: 'warning' });
      return;
    }

    const kode = selectedRow.kodeObjekPajak || selectedRow.kdObjPjk;
    const detailKop = kodeObjekPajaks.find((item) => item.kode === kode);

    const mergedRow = {
      ...selectedRow,
      ...(detailKop
        ? {
            namaObjekPajak: detailKop.nama,
            pasalPPh: detailKop.pasal,
            statusPPh: detailKop.statuspph,
          }
        : {}),
    };

    setPreviewPayload(mergedRow);
    setIsPreviewOpen(true);
  };

  const actions = useMemo(
    () => [
      [
        {
          title: 'Refresh List',
          icon: <AutorenewTwoTone sx={{ width: 26, height: 26 }} />,
          func: () =>
            startTransition(() => {
              void refetch();
            }),
        },
        {
          title: 'Edit',
          icon: <EditNoteTwoTone sx={{ width: 26, height: 26 }} />,
          func: () => handleEditData('ubah'),
          disabled: !validatedActions.canEdit,
        },
        {
          title: 'Detail',
          icon: <ArticleTwoTone sx={{ width: 26, height: 26 }} />,
          func: handleOpenPreview,

          disabled: !validatedActions.canDetail,
        },
        {
          title: 'Hapus',
          icon: <DeleteSweepTwoTone sx={{ width: 26, height: 26 }} />,
          func: () => setIsDeleteModalOpen(true),
          disabled: !validatedActions.canDelete,
        },
      ],
      [
        {
          title: 'Upload',
          icon: <UploadFileTwoTone sx={{ width: 26, height: 26 }} />,
          func: () => setIsUploadModalOpen(true),
          disabled: !validatedActions.canUpload,
        },
        {
          title: 'Pengganti',
          icon: <FileOpenTwoTone sx={{ width: 26, height: 26 }} />,
          func: () => handleEditData('pengganti'),
          disabled: !validatedActions.canReplacement,
        },
        {
          title: 'Batal',
          icon: <HighlightOffTwoTone sx={{ width: 26, height: 26 }} />,
          func: () => setIsCancelModalOpen(true),
          disabled: !validatedActions.canCancel,
        },
      ],
    ],
    [validatedActions, refetch, handleEditData]
  );

  const pinnedColumns = useMemo(() => ({ left: ['__check__', 'fgStatus', 'noBupot'] }), []);

  // Wrap CustomToolbar so types match GridToolbarProps and we can pass extra props
  const ToolbarWrapper: React.FC<GridToolbarProps> = useCallback(
    (gridToolbarProps) => (
      <CustomToolbar
        actions={actions}
        columns={columns}
        filterModel={filterModel}
        setFilterModel={setFilterModel}
        statusOptions={statusOptions}
        {...gridToolbarProps}
      />
    ),
    [actions, columns, filterModel, setFilterModel, statusOptions]
  );

  // schedule a lightweight debug (avoid console.debug in fast path)
  useEffect(() => {
    const api = apiRef.current;
    if (!api) return;
    const id = window.setTimeout(() => {
      const selected = getSelectedRowByKey('all');
    }, 100);
    // eslint-disable-next-line consistent-return
    return () => clearTimeout(id);
  }, [apiRef, selectionVersion]);

  return (
    <>
      <DashboardContent>
        <CustomBreadcrumbs
          heading="Bupot Unifikasi"
          links={[{ name: 'Dashboard', href: paths.dashboard.root }, { name: 'e-Bupot Unifikasi' }]}
          action={
            <Button component={RouterLink} href={paths.unifikasi.dnNew} variant="contained">
              Rekam Data
            </Button>
          }
        />

        <DataGridPremium
          apiRef={apiRef}
          checkboxSelection
          rows={rows || []}
          getRowId={(row: any) => {
            if (!row) return '';
            return String(row.id ?? row.internal_id ?? '');
          }}
          columns={columns}
          loading={isFetching}
          rowCount={totalRows}
          pagination
          paginationMode="server"
          paginationModel={paginationModel}
          initialState={{
            pagination: {
              paginationModel: {
                page,
                pageSize,
              },
            },
          }}
          // onPaginationModelChange={handlePaginationChange}
          onPaginationModelChange={throttledPaginationChange}
          pageSizeOptions={[5, 10, 15, 25, 50, 100]}
          filterMode="server"
          onFilterModelChange={debouncedFilterChange}
          sortingMode="server"
          onSortModelChange={debouncedSortChange}
          rowSelectionModel={rowSelectionModel}
          // onRowSelectionModelChange={handleRowSelectionChange}
          onRowSelectionModelChange={throttledSelectionChange}
          pinnedColumns={pinnedColumns}
          cellSelection
          showToolbar
          slots={{ toolbar: ToolbarWrapper }}
          sx={{
            border: 1,
            borderColor: 'divider',
            borderRadius: 2,
            mt: 3,
            '& .MuiDataGrid-cell': {
              borderColor: 'divider',
              userSelect: 'text',
              cursor: 'text',
            },
            '& .MuiDataGrid-columnHeaders': { borderColor: 'divider' },
          }}
        />
      </DashboardContent>

      {isDeleteModalOpen && (
        <ModalDeleteDn
          dataSelected={rowSelectionModel}
          setSelectionModel={setRowSelectionModel}
          tableApiRef={apiRef}
          isOpenDialogDelete={isDeleteModalOpen}
          setIsOpenDialogDelete={setIsDeleteModalOpen}
          successMessage="Data berhasil dihapus"
        />
      )}

      {isUploadModalOpen && (
        <ModalUploadDn
          dataSelected={rowSelectionModel}
          setSelectionModel={setRowSelectionModel}
          tableApiRef={apiRef}
          isOpenDialogUpload={isUploadModalOpen}
          setIsOpenDialogUpload={setIsUploadModalOpen}
          successMessage="Data berhasil diupload"
        />
      )}

      {isCancelModalOpen && (
        <ModalCancelDn
          dataSelected={dataSelectedRef.current}
          setSelectionModel={setRowSelectionModel}
          tableApiRef={apiRef}
          isOpenDialogCancel={isCancelModalOpen}
          setIsOpenDialogCancel={setIsCancelModalOpen}
          successMessage="Data berhasil diupload"
        />
      )}

      {isPreviewOpen && (
        <ModalCetakPdfDn
          payload={previewPayload}
          isOpen={isPreviewOpen}
          onClose={() => {
            setIsPreviewOpen(false);
            setPreviewPayload(undefined);
          }}
        />
      )}
    </>
  );
}
