"use client";

import dynamic from "next/dynamic";
import { useMemo, useState, type ReactElement } from "react";

import Box from "@mui/material/Box";
import CircularProgress from "@mui/material/CircularProgress";
import Paper from "@mui/material/Paper";
import Typography from "@mui/material/Typography";
import { alpha, useTheme } from "@mui/material/styles";
import type { SxProps, Theme } from "@mui/material/styles";
import type {
  DataGridProps,
  GridColDef,
  GridFilterModel,
  GridPaginationModel,
  GridRenderCellParams,
  GridSortModel,
  GridValidRowModel,
} from "@mui/x-data-grid";

import { DataTablePagination } from "@/components/ui/data-table/data-table-pagination";
import { DataTableCellTooltip } from "@/components/ui/data-table/data-table-tooltip-cell";
import type {
  DataTableColDef,
  DataTableProps,
} from "@/components/ui/data-table/data-table-types";

const DEFAULT_PAGE_SIZE = 10;
const DEFAULT_TABLE_HEIGHT = 650;

const MuiDataGrid = dynamic(
  () => import("@mui/x-data-grid").then((mod) => mod.DataGrid),
  { ssr: false },
) as <Row extends GridValidRowModel>(props: DataGridProps<Row>) => ReactElement;

function mergeSx(
  base: SxProps<Theme>,
  custom?: SxProps<Theme>,
  final?: SxProps<Theme>,
): SxProps<Theme> {
  return [
    ...(Array.isArray(base) ? base : [base]),
    ...(Array.isArray(custom) ? custom : [custom]),
    ...(Array.isArray(final) ? final : [final]),
  ].filter(Boolean) as SxProps<Theme>;
}

function centerColumnTitles<Row extends GridValidRowModel>(
  columns: GridColDef<Row>[],
) {
  return columns.map((column) => ({
    ...column,
    headerAlign: column.headerAlign ?? ("center" as const),
  }));
}

function getCellTooltipTitle<Row extends GridValidRowModel>(
  params: GridRenderCellParams<Row>,
  showTooltip: NonNullable<DataTableColDef<Row>["showTooltip"]>,
) {
  if (typeof showTooltip === "function") {
    return showTooltip(params) ?? null;
  }

  const value = params.formattedValue ?? params.value;

  if (value == null || value === "") {
    return null;
  }

  return String(value);
}

function applyDataTableTooltips<Row extends GridValidRowModel>(
  columns: DataTableProps<Row>["columns"],
): GridColDef<Row>[] {
  return columns.map((column) => {
    const { showTooltip, headerTooltip, ...gridColumn } = column;
    let nextColumn: GridColDef<Row> = { ...gridColumn };

    if (showTooltip) {
      const originalRenderCell = column.renderCell;

      nextColumn = {
        ...nextColumn,
        renderCell: (params) => {
          const content = originalRenderCell
            ? originalRenderCell(params)
            : (params.formattedValue ?? params.value);

          const title = getCellTooltipTitle(params, showTooltip);

          return (
            <DataTableCellTooltip title={title ?? undefined}>
              {content}
            </DataTableCellTooltip>
          );
        },
      };
    }

    if (headerTooltip) {
      const originalRenderHeader = column.renderHeader;

      nextColumn = {
        ...nextColumn,
        renderHeader: (params) => {
          const content = originalRenderHeader
            ? originalRenderHeader(params)
            : params.colDef.headerName;

          return (
            <DataTableCellTooltip title={headerTooltip}>
              <Box
                component="span"
                sx={{ display: "inline-block", width: "100%" }}
              >
                {content}
              </Box>
            </DataTableCellTooltip>
          );
        },
      };
    }

    return nextColumn;
  });
}

export function DataTable<Row extends GridValidRowModel>({
  rows,
  columns,
  getRowId,
  loading = false,
  error = null,
  emptyMessage = "No data found.",
  emptyContent,
  emptyOverlayInsetTop = 0,
  fillAvailableHeight = false,
  height = DEFAULT_TABLE_HEIGHT,
  pageSize = DEFAULT_PAGE_SIZE,
  rowHeight = 60,
  columnHeaderHeight = 50,
  paperSx,
  dataGridSx,
  enableColumnMenu = false,
  checkboxSelection = false,
  hidePagination = false,
  paginationMode = "client",
  rowCount,
  paginationModel: controlledPaginationModel,
  onPaginationModelChange,
  sortingMode = "client",
  sortModel: controlledSortModel,
  onSortModelChange,
  filterMode = "client",
  filterModel: controlledFilterModel,
  onFilterModelChange,
}: DataTableProps<Row>) {
  const theme = useTheme();
  const isDark = theme.palette.mode === "dark";

  const [internalPaginationModel, setInternalPaginationModel] =
    useState<GridPaginationModel>({
      page: 0,
      pageSize,
    });
  const [internalSortModel, setInternalSortModel] = useState<GridSortModel>([]);
  const [internalFilterModel, setInternalFilterModel] =
    useState<GridFilterModel>({
      items: [],
    });

  const paginationModel = controlledPaginationModel ?? internalPaginationModel;
  const handlePaginationModelChange =
    onPaginationModelChange ?? setInternalPaginationModel;
  const sortModel = controlledSortModel ?? internalSortModel;
  const handleSortModelChange = onSortModelChange ?? setInternalSortModel;
  const filterModel = controlledFilterModel ?? internalFilterModel;
  const handleFilterModelChange = onFilterModelChange ?? setInternalFilterModel;
  const isServerPagination = paginationMode === "server";
  const isServerSorting = sortingMode === "server";
  const isServerFiltering = filterMode === "server";
  const totalRowCount = isServerPagination
    ? (rowCount ?? rows.length)
    : rows.length;

  const effectivePageSize = hidePagination
    ? Math.max(rows.length, 1)
    : (paginationModel.pageSize ?? pageSize);
  const pageCount = hidePagination
    ? 1
    : Math.max(1, Math.ceil(totalRowCount / effectivePageSize));
  const page = hidePagination
    ? 0
    : Math.min(paginationModel.page, pageCount - 1);

  const mergePaginationModel = (model: GridPaginationModel) => {
    handlePaginationModelChange({
      page: model.page,
      pageSize: model.pageSize ?? effectivePageSize,
    });
  };
  const showError = !loading && Boolean(error);
  const showEmpty = !loading && !error && rows.length === 0;
  const processedColumns = useMemo(
    () => centerColumnTitles(applyDataTableTooltips(columns)),
    [columns],
  );

  const baseDataGridSx = useMemo<SxProps<Theme>>(
    () => ({
      border: "none",
      color: isDark ? alpha("#ffffff", 0.86) : theme.palette.text.primary,
      backgroundColor: isDark ? "#101828" : "#ffffff",

      "& .MuiDataGrid-columnHeaders": {
        backgroundColor: isDark ? alpha("#ffffff", 0.06) : "#ddedfb",
      },

      "& .MuiDataGrid-columnHeader": {
        px: 2,
        backgroundColor: isDark ? alpha("#ffffff", 0.06) : "#ddedfb",
      },

      "& .MuiDataGrid-columnHeaderTitle": {
        fontSize: 12,
        fontWeight: 500,
        color: isDark ? alpha("#ffffff", 0.72) : "#717680",
      },

      "& .MuiDataGrid-cell": {
        px: 2,
        py: 0,
        display: "flex",
        alignItems: "center",
        backgroundColor: isDark ? "#101828" : "#ffffff",
        color: isDark ? alpha("#ffffff", 0.86) : theme.palette.text.primary,
      },

      "& .MuiDataGrid-row": {
        backgroundColor: isDark ? "#101828" : "#ffffff",
      },

      "& .MuiDataGrid-row:hover": {
        backgroundColor: isDark ? alpha("#ffffff", 0.04) : "#f9fafb",
      },

      "& .MuiDataGrid-withBorderColor": {
        borderColor: isDark ? alpha("#ffffff", 0.08) : "#f5f5f5",
      },

      "& .MuiDataGrid-cell:focus, & .MuiDataGrid-cell:focus-within": {
        outline: "none",
      },

      "& .MuiDataGrid-columnHeader:focus, & .MuiDataGrid-columnHeader:focus-within":
        {
          outline: "none",
        },

      "& .MuiDataGrid-main": {
        overflow: "hidden",
      },

      "& .MuiDataGrid-virtualScroller": {
        overflowX: "auto !important",
        overflowY: "auto !important",
        backgroundColor: isDark ? "#101828" : "#ffffff",
      },

      "& .MuiDataGrid-overlay": {
        display: "none",
      },
    }),
    [isDark, theme.palette.text.primary],
  );

  return (
    <Paper
      elevation={0}
      sx={mergeSx(
        {
          borderRadius: "12px",
          border: `1px solid ${isDark ? alpha("#ffffff", 0.1) : "#f5f5f5"}`,
          overflow: "hidden",
          backgroundColor: isDark ? "#101828" : "#ffffff",
        },
        paperSx,
        fillAvailableHeight
          ? {
              flex: 1,
              minHeight: 428,
              display: "flex",
              flexDirection: "column",
              overflow: "hidden",
            }
          : undefined,
      )}
    >
      <Box
        sx={
          fillAvailableHeight
            ? {
                flex: 1,
                minHeight: 360,
                width: "100%",
                position: "relative",
              }
            : { height, width: "100%", position: "relative" }
        }
      >
        {loading ? (
          <Box
            sx={{
              position: "absolute",
              inset: 0,
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              zIndex: 2,
              backgroundColor: isDark
                ? alpha("#101828", 0.6)
                : alpha("#ffffff", 0.6),
            }}
          >
            <CircularProgress size={28} />
          </Box>
        ) : null}

        {showError ? (
          <Box
            sx={{
              position: "absolute",
              inset: 0,
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              px: 3,
              textAlign: "center",
              zIndex: 1,
            }}
          >
            <Typography
              sx={{
                color: "#f04438",
                fontSize: 13,
                fontWeight: 500,
              }}
            >
              {error}
            </Typography>
          </Box>
        ) : null}

        {showEmpty ? (
          <Box
            sx={{
              position: "absolute",
              top: emptyOverlayInsetTop,
              right: 0,
              bottom: 0,
              left: 0,
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              px: 3,
              textAlign: "center",
              zIndex: 1,
              pointerEvents: "none",
            }}
          >
            {emptyContent ?? (
              <Typography
                sx={{
                  color: "text.secondary",
                  fontSize: 13,
                  fontWeight: 500,
                }}
              >
                {emptyMessage}
              </Typography>
            )}
          </Box>
        ) : null}

        <MuiDataGrid
          rows={rows}
          columns={processedColumns}
          getRowId={getRowId}
          loading={loading}
          pagination
          paginationMode={isServerPagination ? "server" : "client"}
          sortingMode={isServerSorting ? "server" : "client"}
          filterMode={isServerFiltering ? "server" : "client"}
          rowCount={isServerPagination ? totalRowCount : undefined}
          hideFooter
          disableRowSelectionOnClick
          disableColumnFilter={!enableColumnMenu}
          disableColumnSelector={!enableColumnMenu}
          disableDensitySelector
          disableColumnMenu={!enableColumnMenu}
          checkboxSelection={checkboxSelection}
          paginationModel={{ page, pageSize: effectivePageSize }}
          onPaginationModelChange={
            hidePagination ? undefined : mergePaginationModel
          }
          sortModel={sortModel}
          onSortModelChange={handleSortModelChange}
          filterModel={filterModel}
          onFilterModelChange={handleFilterModelChange}
          pageSizeOptions={[effectivePageSize]}
          showCellVerticalBorder
          showColumnVerticalBorder
          rowHeight={rowHeight}
          columnHeaderHeight={columnHeaderHeight}
          sx={mergeSx(
            baseDataGridSx,
            dataGridSx,
            fillAvailableHeight ? { height: "100%" } : undefined,
          )}
        />
      </Box>

      {!hidePagination ? (
        <DataTablePagination
          page={page}
          pageCount={pageCount}
          isEmpty={showEmpty}
          onPrevious={() =>
            mergePaginationModel({
              page: Math.max(0, paginationModel.page - 1),
              pageSize: effectivePageSize,
            })
          }
          onNext={() =>
            mergePaginationModel({
              page: Math.min(pageCount - 1, paginationModel.page + 1),
              pageSize: effectivePageSize,
            })
          }
          onSelectPage={(pageIndex) =>
            mergePaginationModel({
              page: pageIndex,
              pageSize: effectivePageSize,
            })
          }
        />
      ) : null}
    </Paper>
  );
}
