import { formatCellWithSuffix } from './cellDisplay';
import type {
  SpreadsheetColumnConfig,
  SpreadsheetColumnFilter,
  SpreadsheetViewState,
} from '../types';

type ResolveDisplayValue = (
  viewRow: number,
  colCfg: SpreadsheetColumnConfig,
  row?: Record<string, unknown>,
) => unknown;

function formatDisplayText(value: unknown, colCfg: SpreadsheetColumnConfig): string {
  if (value === null || value === undefined) return '';
  const cellType = colCfg.cellType === 'formula' ? 'number' : colCfg.cellType;
  if (colCfg.suffix) {
    return formatCellWithSuffix(value, colCfg);
  }
  if (cellType === 'number') {
    const n = Number(value);
    return Number.isFinite(n) ? String(n) : String(value);
  }
  if (typeof value === 'object') return JSON.stringify(value);
  return String(value);
}

export function getCellDisplayText(
  row: Record<string, unknown>,
  dataRowIndex: number,
  colCfg: SpreadsheetColumnConfig,
  resolveDisplayValue: ResolveDisplayValue,
): string {
  const raw = resolveDisplayValue(dataRowIndex, colCfg, row);
  return formatDisplayText(raw, colCfg);
}

export function collectColumnUniqueValues(
  rows: Record<string, unknown>[],
  col: number,
  columns: SpreadsheetColumnConfig[],
  resolveDisplayValue: ResolveDisplayValue,
): string[] {
  const colCfg = columns.find((c) => c.col === col);
  if (!colCfg) return [];
  const set = new Set<string>();
  rows.forEach((row, dataRowIndex) => {
    set.add(getCellDisplayText(row, dataRowIndex, colCfg, resolveDisplayValue));
  });
  return [...set].sort((a, b) => a.localeCompare(b, 'zh-CN'));
}

export function isColumnFilterActive(filter?: SpreadsheetColumnFilter): boolean {
  return filter?.checkedValues !== undefined;
}

export function isColumnFilterMatching(
  displayText: string,
  filter?: SpreadsheetColumnFilter,
  allValues?: string[],
): boolean {
  if (!filter || filter.checkedValues === undefined) return true;
  if (filter.checkedValues.length === 0) return false;
  if (allValues && filter.checkedValues.length >= allValues.length) return true;
  return filter.checkedValues.includes(displayText);
}

function compareRows(
  a: number,
  b: number,
  sortCol: number,
  rows: Record<string, unknown>[],
  columns: SpreadsheetColumnConfig[],
  resolveDisplayValue: ResolveDisplayValue,
): number {
  const colCfg = columns.find((c) => c.col === sortCol);
  if (!colCfg) return 0;
  const rowA = rows[a];
  const rowB = rows[b];
  if (!rowA || !rowB) return 0;

  const textA = getCellDisplayText(rowA, a, colCfg, resolveDisplayValue);
  const textB = getCellDisplayText(rowB, b, colCfg, resolveDisplayValue);

  if (colCfg.cellType === 'number' || colCfg.cellType === 'formula') {
    const nA = Number(textA);
    const nB = Number(textB);
    const aValid = Number.isFinite(nA);
    const bValid = Number.isFinite(nB);
    if (!aValid && !bValid) return 0;
    if (!aValid) return 1;
    if (!bValid) return -1;
    return nA - nB;
  }
  return textA.localeCompare(textB, 'zh-CN');
}

export function applyViewState(
  rows: Record<string, unknown>[],
  columns: SpreadsheetColumnConfig[],
  viewState: SpreadsheetViewState,
  resolveDisplayValue: ResolveDisplayValue,
): number[] {
  const activeFilterCols = Object.entries(viewState.filters).filter(([, f]) =>
    isColumnFilterActive(f),
  );

  let indices = rows.map((_, i) => i);

  if (activeFilterCols.length > 0) {
    const uniqueValuesMap = new Map<number, string[]>();
    for (const [colStr] of activeFilterCols) {
      const col = Number(colStr);
      if (!uniqueValuesMap.has(col)) {
        uniqueValuesMap.set(
          col,
          collectColumnUniqueValues(
            rows,
            col,
            columns,
            resolveDisplayValue,
          ),
        );
      }
    }

    indices = indices.filter((dataRowIndex) => {
      const row = rows[dataRowIndex];
      if (!row) return false;
      return activeFilterCols.every(([colStr, filter]) => {
        const col = Number(colStr);
        const colCfg = columns.find((c) => c.col === col);
        if (!colCfg) return true;
        const allValues = uniqueValuesMap.get(col);
        const text = getCellDisplayText(
          row,
          dataRowIndex,
          colCfg,
          resolveDisplayValue,
        );
        return isColumnFilterMatching(text, filter, allValues);
      });
    });
  }

  if (viewState.sort) {
    const { col, direction } = viewState.sort;
    indices = [...indices].sort((a, b) => {
      const cmp = compareRows(a, b, col, rows, columns, resolveDisplayValue);
      return direction === 'asc' ? cmp : -cmp;
    });
  }

  return indices;
}
