import RestartAltOutlined from "@mui/icons-material/RestartAltOutlined";
import SearchOutlined from "@mui/icons-material/SearchOutlined";
import MenuItem from "@mui/material/MenuItem";
import MenuList from "@mui/material/MenuList";
import Popover from "@mui/material/Popover";
import TextField from "@mui/material/TextField";
import Tooltip from "@mui/material/Tooltip";
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTimeZone } from "@/app/timezone/TimeZoneProvider.jsx";
import { Button } from "@/shared/ui/Button.jsx";
import { IconButton } from "@/shared/ui/IconButton.jsx";
import { PaginationBar } from "@/shared/ui/PaginationBar.jsx";

const DEFAULT_COLUMN_WIDTH = "minmax(120px, 1fr)";
const DEFAULT_RESIZE_MIN_WIDTH = 72;
const DEFAULT_RESIZE_MAX_WIDTH = 960;
const ACTION_COLUMN_WIDTH = "112px";
const DEFAULT_COMPACT_MIN_WIDTH = 220;
const LOW_PRIORITY_COMPACT_MIN_WIDTH = 180;

export function DataTable({
  className = "",
  columns,
  context,
  emptyLabel = "当前无数据",
  fixedEdges = true,
  getRowProps,
  infiniteScroll,
  items,
  minWidth = "980px",
  onColumnWidthsChange,
  pagination,
  renderRowDetail,
  resizableColumns = true,
  rowKey,
  tableClassName = "",
  title,
  total
}) {
  const { timeZone } = useTimeZone();
  const [columnWidths, setColumnWidths] = useState({});
  const [filterMenu, setFilterMenu] = useState({ anchorEl: null, columnKey: "" });
  const [filterQuery, setFilterQuery] = useState("");
  const [hasHorizontalOverflow, setHasHorizontalOverflow] = useState(false);
  const [paginationLoadingMore, setPaginationLoadingMore] = useState(false);
  const [resizeSession, setResizeSession] = useState(null);
  const loadMoreRef = useRef(null);
  const scrollRef = useRef(null);
  const safeItems = Array.isArray(items) ? items : [];
  const paginationTotal = Number(pagination?.total || 0);
  const paginationHasMore =
    Boolean(pagination?.onPageChange) &&
    (Number.isFinite(paginationTotal) && paginationTotal > 0
      ? safeItems.length < paginationTotal
      : Boolean(pagination?.hasNextPage));
  const infiniteLoading = Boolean(infiniteScroll?.loading || paginationLoadingMore);
  const infiniteHasMore = Boolean(infiniteScroll?.hasMore || paginationHasMore);
  const infiniteLoadLabel = infiniteScroll?.loadingLabel || "加载中...";
  const infiniteDoneLabel = infiniteScroll?.doneLabel || "";
  const loadMore = useCallback(() => {
    if (infiniteScroll?.onLoadMore) {
      infiniteScroll.onLoadMore();
      return;
    }
    pagination?.onPageChange?.(Number(pagination?.page || 1) + 1);
  }, [infiniteScroll, pagination]);
  const renderContext = useMemo(() => (context ? { ...context, timeZone } : { timeZone }), [context, timeZone]);

  const preparedColumns = useMemo(
    () =>
      columns.map((column) => {
        const normalizedColumn = normalizeColumn(column);
        return {
          ...normalizedColumn,
          fixed: resolveColumnFixed(normalizedColumn, fixedEdges && hasHorizontalOverflow),
          width: columnWidths[normalizedColumn.key] || normalizedColumn.width || DEFAULT_COLUMN_WIDTH
        };
      }),
    [columnWidths, columns, fixedEdges, hasHorizontalOverflow]
  );

  const gridTemplate = preparedColumns.map((column) => column.width).join(" ");
  const tableContentWidth = `${Math.ceil(
    preparedColumns.reduce((total, column) => total + readMinWidth(column.width), 0)
  )}px`;
  const activeFilterColumn = preparedColumns.find((column) => column.key === filterMenu.columnKey && column.filter) || null;
  const activeFilter = activeFilterColumn?.filter || null;
  const activeFilterIsText = activeFilter?.type === "text";
  const activeFilterIsTextFields = activeFilter?.type === "textFields";
  const filterColumns = preparedColumns.filter((column) => column.filter);
  const hasColumnFilters = filterColumns.length > 0;
  const hasActiveColumnFilters = filterColumns.some((column) => isFilterActive(column.filter));
  const filterOptions = useMemo(
    () => filterVisibleOptions(activeFilter, typeof filterQuery === "string" ? filterQuery : ""),
    [activeFilter, filterQuery]
  );

  useEffect(() => {
    const scrollNode = scrollRef.current;
    if (!scrollNode) {
      return undefined;
    }

    const measureOverflow = () => {
      const nextHasOverflow = scrollNode.scrollWidth > scrollNode.clientWidth + 1;
      setHasHorizontalOverflow((current) => (current === nextHasOverflow ? current : nextHasOverflow));
    };

    measureOverflow();
    window.addEventListener("resize", measureOverflow);

    if (typeof ResizeObserver === "undefined") {
      return () => {
        window.removeEventListener("resize", measureOverflow);
      };
    }

    const observer = new ResizeObserver(measureOverflow);
    observer.observe(scrollNode);
    if (scrollNode.firstElementChild) {
      observer.observe(scrollNode.firstElementChild);
    }

    return () => {
      observer.disconnect();
      window.removeEventListener("resize", measureOverflow);
    };
  }, [gridTemplate, minWidth, safeItems.length]);

  useEffect(() => {
    if (!resizeSession) {
      return undefined;
    }

    const moveColumn = (event) => {
      const delta = event.clientX - resizeSession.startX;
      const nextWidth = clamp(resizeSession.startWidth + delta, resizeSession.minWidth, resizeSession.maxWidth);
      const cssWidth = `${Math.round(nextWidth)}px`;

      setColumnWidths((current) => {
        if (current[resizeSession.key] === cssWidth) {
          return current;
        }
        const next = { ...current, [resizeSession.key]: cssWidth };
        onColumnWidthsChange?.(next);
        return next;
      });
    };

    const finishResize = () => {
      setResizeSession(null);
    };

    document.body.style.cursor = "col-resize";
    document.body.style.userSelect = "none";
    window.addEventListener("pointermove", moveColumn);
    window.addEventListener("pointerup", finishResize, { once: true });
    window.addEventListener("pointercancel", finishResize, { once: true });

    return () => {
      document.body.style.cursor = "";
      document.body.style.userSelect = "";
      window.removeEventListener("pointermove", moveColumn);
      window.removeEventListener("pointerup", finishResize);
      window.removeEventListener("pointercancel", finishResize);
    };
  }, [onColumnWidthsChange, resizeSession]);

  useEffect(() => {
    const root = scrollRef.current;
    const target = loadMoreRef.current;
    if (!root || !target || !loadMore || !infiniteHasMore || infiniteLoading) {
      return undefined;
    }

    const observer = new IntersectionObserver(
      (entries) => {
        if (entries.some((entry) => entry.isIntersecting)) {
          setPaginationLoadingMore(Boolean(paginationHasMore));
          loadMore();
        }
      },
      { root, rootMargin: "0px 0px 96px", threshold: 0.01 }
    );

    observer.observe(target);
    return () => {
      observer.disconnect();
    };
  }, [infiniteHasMore, infiniteLoading, loadMore, paginationHasMore, safeItems.length]);

  useEffect(() => {
    setPaginationLoadingMore(false);
  }, [safeItems.length]);

  const startResize = (event, column) => {
    if (!resizableColumns || column.resizable === false) {
      return;
    }

    const headerCell = event.currentTarget.closest("[data-table-header-cell]");
    const startWidth = headerCell?.getBoundingClientRect().width;
    if (!startWidth) {
      return;
    }

    event.preventDefault();
    event.stopPropagation();
    setResizeSession({
      key: column.key,
      maxWidth: resolveResizeMaxWidth(column),
      minWidth: resolveResizeMinWidth(column),
      startWidth,
      startX: event.clientX
    });
  };

  const openColumnFilter = (event, column) => {
    if (!column.filter) {
      return;
    }
    event.preventDefault();
    setFilterMenu({ anchorEl: event.currentTarget, columnKey: column.key });
    setFilterQuery(initialFilterDraft(column.filter));
  };

  const closeColumnFilter = () => {
    setFilterMenu({ anchorEl: null, columnKey: "" });
    setFilterQuery("");
  };

  const changeFilter = (value) => {
    activeFilter?.onChange?.(value);
    closeColumnFilter();
  };

  const submitTextFilter = (event) => {
    event.preventDefault();
    activeFilter?.onChange?.(filterQuery.trim());
    closeColumnFilter();
  };

  const submitTextFieldsFilter = (event) => {
    event.preventDefault();
    activeFilter?.onChange?.(normalizeTextFieldsDraft(activeFilter, filterQuery));
    closeColumnFilter();
  };

  const clearTextFilter = () => {
    if (activeFilter?.onReset) {
      activeFilter.onReset();
    } else {
      activeFilter?.onChange?.("");
    }
    closeColumnFilter();
  };

  const clearTextFieldsFilter = () => {
    if (activeFilter?.onReset) {
      activeFilter.onReset();
    } else {
      activeFilter?.onChange?.(emptyTextFieldsDraft(activeFilter));
    }
    closeColumnFilter();
  };

  const resetColumnFilters = () => {
    closeColumnFilter();
    filterColumns.forEach((column) => {
      if (!isFilterActive(column.filter)) {
        return;
      }
      if (column.filter.onReset) {
        column.filter.onReset();
        return;
      }
      column.filter.onChange?.("");
    });
  };

  return (
    <section className={["table-frame", className].filter(Boolean).join(" ")}>
      {title || total !== undefined ? (
        <div className="table-toolbar">
          <div className="card-title">{title}</div>
          {total !== undefined ? <div className="muted">共 {total} 条</div> : null}
        </div>
      ) : null}
      <div className="table-scroll" ref={scrollRef}>
        <div
          className={["admin-table", resizeSession ? "admin-table--resizing" : "", tableClassName].filter(Boolean).join(" ")}
          style={{
            "--admin-table-columns": gridTemplate,
            "--admin-table-content-width": tableContentWidth,
            "--admin-table-min-width": minWidth
          }}
        >
          <div className="admin-row admin-row--head">
            {preparedColumns.map((column, index) => {
              const isLastColumn = index === preparedColumns.length - 1;
              return (
                <div
                  className={cellClassName(column, ["admin-cell--head", resizeSession?.key === column.key ? "admin-cell--resizing" : ""])}
                  data-table-header-cell
                  key={column.key}
                >
                  <HeaderLabel
                    column={column}
                    trailingAction={
                      isLastColumn && hasColumnFilters && hasActiveColumnFilters ? (
                        <TableFilterResetButton onClick={resetColumnFilters} />
                      ) : null
                    }
                    onOpenFilter={openColumnFilter}
                  />
                  {resizableColumns && column.resizable !== false ? (
                    <button
                      aria-label={`调整${typeof column.label === "string" ? column.label : column.key}列宽`}
                      className="admin-column-resizer"
                      tabIndex={-1}
                      type="button"
                      onPointerDown={(event) => startResize(event, column)}
                    />
                  ) : null}
                </div>
              );
            })}
          </div>
          {safeItems.length ? (
            <>
              {safeItems.map((item, index) => {
                const rowProps = getRowProps ? getRowProps(item, index) : {};
                const { className: rowClassName = "", ...restRowProps } = rowProps;
                const key = rowKey(item);

                return (
                  <Fragment key={key}>
                    <div className={["admin-row", rowClassName].filter(Boolean).join(" ")} {...restRowProps}>
                      {preparedColumns.map((column) => (
                        <div className={cellClassName(column)} key={column.key}>
                          {column.render ? column.render(item, index, renderContext) : displayValue(item[column.key])}
                        </div>
                      ))}
                    </div>
                    {renderRowDetail ? renderRowDetail(item, index, renderContext) : null}
                  </Fragment>
                );
              })}
              {infiniteScroll || paginationHasMore || paginationLoadingMore ? (
                <div
                  className={["admin-row", "admin-row--load-more", !infiniteHasMore && !infiniteLoading ? "admin-row--load-more-done" : ""]
                    .filter(Boolean)
                    .join(" ")}
                  ref={loadMoreRef}
                >
                  <div className="admin-cell admin-cell--load-more">
                    {infiniteLoading ? infiniteLoadLabel : infiniteHasMore ? "" : infiniteDoneLabel}
                  </div>
                </div>
              ) : null}
            </>
          ) : infiniteLoading ? (
            <div className="admin-row admin-row--load-more" ref={loadMoreRef}>
              <div className="admin-cell admin-cell--load-more">
                {infiniteLoadLabel}
              </div>
            </div>
          ) : (
            <div className="empty-state empty-state--table">
              <div className="empty-state__title">{emptyLabel}</div>
            </div>
          )}
          {safeItems.length === 0 && (infiniteScroll || paginationHasMore) && !infiniteLoading ? (
            <div className="admin-row--load-more-sentinel" ref={loadMoreRef} />
          ) : null}
        </div>
      </div>
      {pagination ? (
        <PaginationBar
          {...pagination}
          itemCount={safeItems.length}
        />
      ) : null}
      <Popover
        anchorEl={filterMenu.anchorEl}
        anchorOrigin={{ horizontal: "left", vertical: "bottom" }}
        className="admin-table-filter-popover"
        open={Boolean(activeFilterColumn)}
        transformOrigin={{ horizontal: "left", vertical: "top" }}
        onClose={closeColumnFilter}
      >
        {activeFilterColumn ? (
          activeFilterIsText ? (
            <form className="admin-table-filter" onSubmit={submitTextFilter}>
              <TextField
                autoFocus
                className="admin-table-filter__search"
                disabled={activeFilter.loading}
                placeholder={activeFilter.placeholder || `搜索${activeFilterColumn.label || ""}`}
                size="small"
                value={filterQuery}
                onChange={(event) => setFilterQuery(event.target.value)}
              />
              <div className="admin-table-filter__actions">
                <Button disabled={activeFilter.loading || !isFilterActive(activeFilter)} onClick={clearTextFilter}>
                  清空
                </Button>
                <Button disabled={activeFilter.loading} type="submit" variant="primary">
                  搜索
                </Button>
              </div>
            </form>
          ) : activeFilterIsTextFields ? (
            <form className="admin-table-filter admin-table-filter--fields" onSubmit={submitTextFieldsFilter}>
              {normalizeTextFields(activeFilter).map((field, index) => (
                <TextField
                  key={field.key}
                  autoFocus={index === 0}
                  className="admin-table-filter__search"
                  disabled={activeFilter.loading}
                  label={field.label}
                  placeholder={field.placeholder || field.label}
                  size="small"
                  value={filterQuery?.[field.key] ?? ""}
                  onChange={(event) =>
                    setFilterQuery((current) => ({
                      ...(current && typeof current === "object" ? current : {}),
                      [field.key]: event.target.value
                    }))
                  }
                />
              ))}
              <div className="admin-table-filter__actions">
                <Button disabled={activeFilter.loading || !isFilterActive(activeFilter)} onClick={clearTextFieldsFilter}>
                  清空
                </Button>
                <Button disabled={activeFilter.loading} type="submit" variant="primary">
                  搜索
                </Button>
              </div>
            </form>
          ) : (
            <div className="admin-table-filter">
              <TextField
                autoFocus
                className="admin-table-filter__search"
                disabled={activeFilter.loading}
                placeholder={activeFilter.placeholder || `搜索${activeFilterColumn.label || ""}`}
                size="small"
                value={filterQuery}
                onChange={(event) => setFilterQuery(event.target.value)}
              />
              <MenuList className="admin-table-filter__options">
                {filterOptions.length ? (
                  filterOptions.map((option) => (
                    <MenuItem
                      key={option.value}
                      selected={String(activeFilter.value ?? "") === option.value}
                      onClick={() => changeFilter(option.value)}
                    >
                      {option.label}
                    </MenuItem>
                  ))
                ) : (
                  <div className="admin-table-filter__empty">{activeFilter.loading ? "加载中..." : "无匹配选项"}</div>
                )}
              </MenuList>
            </div>
          )
        ) : null}
      </Popover>
    </section>
  );
}

function displayValue(value) {
  return value === 0 || value === false || value ? value : "-";
}

function HeaderLabel({ column, onOpenFilter, trailingAction }) {
  const content = column.header || column.label;
  const hasCustomHeader = column.header !== undefined && column.header !== null;
  const label = !column.filter ? (
    <span className={hasCustomHeader ? "admin-cell__head-custom" : "admin-cell__head-label"}>{content}</span>
  ) : (
    <FilterHeaderButton column={column} onOpenFilter={onOpenFilter} />
  );

  return (
    <span className="admin-cell__head-content">
      {label}
      {trailingAction}
    </span>
  );
}

function FilterHeaderButton({ column, onOpenFilter }) {
  const content = column.header || column.label;
  if (!column.filter) {
    return <span className="admin-cell__head-label">{content}</span>;
  }

  const active = isFilterActive(column.filter);
  const filterValueLabel = active ? filterDisplayValue(column.filter) : "";
  return (
    <button
      className={["admin-cell__head-trigger", active ? "admin-cell__head-trigger--active" : ""].filter(Boolean).join(" ")}
      type="button"
      onClick={(event) => onOpenFilter(event, column)}
    >
      <SearchOutlined className="admin-cell__head-icon" fontSize="inherit" />
      <span className="admin-cell__head-label">
        {content}
        {filterValueLabel ? <span className="admin-cell__head-filter">({filterValueLabel})</span> : null}
      </span>
    </button>
  );
}

function TableFilterResetButton({ onClick }) {
  return (
    <Tooltip arrow title="重置筛选">
      <span className="admin-table-filter-reset-wrap">
        <IconButton
          className="admin-table-filter-reset"
          label="重置筛选"
          sx={tableFilterResetSx}
          onClick={(event) => {
            event.stopPropagation();
            onClick();
          }}
        >
          <RestartAltOutlined fontSize="small" />
        </IconButton>
      </span>
    </Tooltip>
  );
}

function cellClassName(column, extraClassName = "") {
  const extraClassNames = Array.isArray(extraClassName) ? extraClassName : [extraClassName];
  return [
    "admin-cell",
    column.fixed === "left" ? "admin-cell--fixed-left" : "",
    column.fixed === "right" ? "admin-cell--fixed-right" : "",
    column.className || "",
    ...extraClassNames
  ]
    .filter(Boolean)
    .join(" ");
}

function normalizeColumn(column) {
  const actionColumn = isActionColumn(column);
  const className = [column.className || "", actionColumn ? "admin-cell--actions" : ""].filter(Boolean).join(" ");
  return {
    ...column,
    className,
    resizable: actionColumn ? false : column.resizable,
    width: actionColumn && !column.width ? ACTION_COLUMN_WIDTH : normalizeColumnWidth(column)
  };
}

function normalizeColumnWidth(column) {
  if (!column.width || typeof column.width !== "string") {
    return column.width;
  }

  const minmaxMatch = column.width.match(/^minmax\(\s*(\d+(?:\.\d+)?)px\s*,\s*(.+)\)$/i);
  if (!minmaxMatch) {
    return column.width;
  }

  const minWidth = Number(minmaxMatch[1]);
  const flexibleWidth = minmaxMatch[2];
  const maxCompactWidth = isLowPriorityColumn(column) ? LOW_PRIORITY_COMPACT_MIN_WIDTH : DEFAULT_COMPACT_MIN_WIDTH;
  const compactMinWidth = Math.min(minWidth, maxCompactWidth);
  return `minmax(${compactMinWidth}px, ${flexibleWidth})`;
}

function isActionColumn(column) {
  return column.key === "actions" || column.label === "操作";
}

function isLowPriorityColumn(column) {
  return /description|detail|remark|param|url|metadata|context|reason/i.test(String(column.key || ""));
}

function resolveColumnFixed(column, fixedEdges) {
  if (fixedEdges && (column.fixed === "left" || column.fixed === "right")) {
    return column.fixed;
  }
  return "";
}

function resolveResizeMinWidth(column) {
  return readPixelWidth(column.resizeMinWidth) || readPixelWidth(column.minWidth) || readMinWidth(column.width) || DEFAULT_RESIZE_MIN_WIDTH;
}

function resolveResizeMaxWidth(column) {
  return readPixelWidth(column.resizeMaxWidth) || readPixelWidth(column.maxWidth) || DEFAULT_RESIZE_MAX_WIDTH;
}

function readMinWidth(width) {
  const pixelWidth = readPixelWidth(width);
  if (pixelWidth) {
    return pixelWidth;
  }
  if (typeof width === "number") {
    return width;
  }
  if (typeof width !== "string") {
    return 0;
  }

  const minmaxMatch = width.match(/minmax\(\s*(\d+(?:\.\d+)?)px/i);
  if (minmaxMatch) {
    return Number(minmaxMatch[1]);
  }

  return 0;
}

function readPixelWidth(width) {
  if (typeof width === "number") {
    return width;
  }
  if (typeof width !== "string") {
    return 0;
  }
  const pixelMatch = width.match(/^(\d+(?:\.\d+)?)px$/i);
  return pixelMatch ? Number(pixelMatch[1]) : 0;
}

function clamp(value, min, max) {
  return Math.min(Math.max(value, min), max);
}

function isFilterActive(filter) {
  if (filter?.activeLabel) {
    return true;
  }
  if (filter?.type === "textFields") {
    return normalizeTextFields(filter).some((field) => {
      const value = field.activeValue !== undefined ? field.activeValue : field.value;
      return value !== undefined && value !== null && String(value).trim() !== "";
    });
  }
  const value = filter?.activeValue !== undefined ? filter.activeValue : filter?.value;
  return value !== undefined && value !== null && String(value) !== "";
}

function filterDisplayValue(filter) {
  if (filter?.activeLabel) {
    return filter.activeLabel;
  }
  if (filter?.type === "textFields") {
    const activeFields = normalizeTextFields(filter)
      .map((field) => {
        const value = String((field.activeValue !== undefined ? field.activeValue : field.value) ?? "").trim();
        return value ? `${field.label || field.key}: ${value}` : "";
      })
      .filter(Boolean);
    if (activeFields.length <= 2) {
      return activeFields.join(" / ");
    }
    return `${activeFields.length} 项`;
  }
  const value = String((filter?.activeValue !== undefined ? filter.activeValue : filter?.value) ?? "");
  if (!value) {
    return "";
  }
  if (filter?.type === "text") {
    return value;
  }

  const matchedOption = normalizeFilterOptions(filter).find((option) => option.value === value);
  return matchedOption?.label || value;
}

function initialFilterDraft(filter) {
  if (filter?.type === "text") {
    return String(filter.value ?? "");
  }
  if (filter?.type === "textFields") {
    return normalizeTextFieldsDraft(filter);
  }
  return "";
}

function normalizeTextFields(filter) {
  return (filter?.fields || []).map((field) => ({
    ...field,
    key: String(field.key || "")
  })).filter((field) => field.key);
}

function normalizeTextFieldsDraft(filter, draft) {
  const source = draft && typeof draft === "object" ? draft : undefined;
  return Object.fromEntries(
    normalizeTextFields(filter).map((field) => {
      const rawValue = source ? source[field.key] : field.value;
      return [field.key, String(rawValue ?? "").trim()];
    })
  );
}

function emptyTextFieldsDraft(filter) {
  return Object.fromEntries(normalizeTextFields(filter).map((field) => [field.key, ""]));
}

function filterVisibleOptions(filter, query) {
  if (!filter) {
    return [];
  }

  const normalizedOptions = normalizeFilterOptions(filter);
  const normalizedQuery = query.trim().toLowerCase();
  if (!normalizedQuery) {
    return normalizedOptions;
  }

  return normalizedOptions.filter((option) => option.label.toLowerCase().includes(normalizedQuery) || option.value.toLowerCase().includes(normalizedQuery));
}

function normalizeFilterOptions(filter) {
  const options = (filter.options || []).map((option) => {
    if (Array.isArray(option)) {
      return { label: String(option[1] ?? option[0] ?? ""), value: String(option[0] ?? "") };
    }
    return { label: String(option.label ?? option.name ?? option.value ?? ""), value: String(option.value ?? "") };
  });

  if (filter.emptyLabel !== undefined && !options.some((option) => option.value === "")) {
    return [{ label: filter.emptyLabel, value: "" }, ...options];
  }
  return options;
}

const tableFilterResetSx = {
  width: 28,
  height: 28,
  color: "var(--text-secondary)",
  backgroundColor: "var(--bg-card)",
  "&:hover": {
    borderColor: "var(--primary-border-strong)",
    backgroundColor: "var(--primary-hover)",
    color: "var(--text-primary)"
  }
};
