import type { IWorkbookData } from '@univerjs/core';
import { CellValueType, LocaleType } from '@univerjs/core';
import type {
  ModelEvidenceStatus,
  ModelWorkbook,
  ModelWorkbookCell,
  ModelWorkbookSheet,
} from '../types/research';

const MOSAICIQ_NAMESPACE = 'mosaiciq';
const UNIVER_APP_VERSION = '3.0.0';

const columnNameToIndex = (columnName: string) =>
  columnName
    .toUpperCase()
    .split('')
    .reduce((sum, char) => sum * 26 + char.charCodeAt(0) - 64, 0) - 1;

const columnIndexToName = (index: number) => {
  let value = index + 1;
  let name = '';
  while (value > 0) {
    const remainder = (value - 1) % 26;
    name = String.fromCharCode(65 + remainder) + name;
    value = Math.floor((value - 1) / 26);
  }
  return name;
};

export const addressToCellPosition = (address: string) => {
  const match = /^\$?([A-Za-z]+)\$?(\d+)$/.exec(address.trim());
  if (!match) {
    return null;
  }
  return {
    row: Number(match[2]) - 1,
    column: columnNameToIndex(match[1]),
  };
};

export const cellPositionToAddress = (row: number, column: number) =>
  `${columnIndexToName(column)}${row + 1}`;

const getValueType = (value: ModelWorkbookCell['value']) => {
  if (typeof value === 'number') {
    return CellValueType.NUMBER;
  }
  if (typeof value === 'boolean') {
    return CellValueType.BOOLEAN;
  }
  return CellValueType.STRING;
};

const cellToUniverCell = (
  cell: ModelWorkbookCell,
  evidenceStatus?: ModelEvidenceStatus,
) => ({
  v: cell.value ?? undefined,
  f: cell.formula ?? undefined,
  t: cell.value === null || cell.value === undefined ? undefined : getValueType(cell.value),
  custom: {
    [MOSAICIQ_NAMESPACE]: {
      cellId: cell.cellId,
      binding: cell.binding,
      evidenceStatus,
      format: cell.format,
    },
  },
});

const univerCellToModelCell = (cell: Record<string, unknown>): ModelWorkbookCell => {
  const custom = (cell.custom as Record<string, unknown> | undefined)?.[
    MOSAICIQ_NAMESPACE
  ] as Record<string, unknown> | undefined;
  const formula = typeof cell.f === 'string' ? cell.f : undefined;
  const value = cell.v as ModelWorkbookCell['value'];

  return {
    ...(value !== undefined ? { value } : {}),
    ...(formula ? { formula } : {}),
    ...(typeof custom?.format === 'string'
      ? { format: custom.format as ModelWorkbookCell['format'] }
      : {}),
    ...(typeof custom?.cellId === 'string' ? { cellId: custom.cellId } : {}),
    ...(custom?.binding && typeof custom.binding === 'object'
      ? { binding: custom.binding as ModelWorkbookCell['binding'] }
      : {}),
  };
};

export const modelWorkbookToUniverSnapshot = (
  workbook: ModelWorkbook,
  evidenceByCellId: Record<string, ModelEvidenceStatus> = {},
): Partial<IWorkbookData> => {
  const sheets: IWorkbookData['sheets'] = {};
  const sheetOrder = workbook.sheets.map((sheet) => sheet.id);

  workbook.sheets.forEach((sheet) => {
    const cellData: Record<number, Record<number, ReturnType<typeof cellToUniverCell>>> = {};
    Object.entries(sheet.cells).forEach(([address, cell]) => {
      const position = addressToCellPosition(address);
      if (!position) {
        return;
      }
      const evidenceStatus = cell.cellId ? evidenceByCellId[cell.cellId] : undefined;
      cellData[position.row] ??= {};
      cellData[position.row][position.column] = cellToUniverCell(cell, evidenceStatus);
    });

    sheets[sheet.id] = {
      id: sheet.id,
      name: sheet.name,
      rowCount: 120,
      columnCount: 26,
      cellData,
      custom: {
        [MOSAICIQ_NAMESPACE]: {
          source: 'financial_model',
        },
      },
    };
  });

  return {
    id: `mosaiciq-model-${Date.now()}`,
    name: 'Financial Model',
    appVersion: UNIVER_APP_VERSION,
    locale: LocaleType.EN_US,
    styles: {},
    sheetOrder,
    sheets,
    custom: {
      [MOSAICIQ_NAMESPACE]: {
        activeSheetId: workbook.activeSheetId,
        workbookVersion: workbook.version,
      },
    },
  };
};

export const univerSnapshotToModelWorkbook = (
  snapshot: Partial<IWorkbookData>,
  fallback: ModelWorkbook,
): ModelWorkbook => {
  const sheetOrder = snapshot.sheetOrder?.length
    ? snapshot.sheetOrder
    : Object.keys(snapshot.sheets ?? {});
  const sheets: ModelWorkbookSheet[] = sheetOrder.map((sheetId) => {
    const sourceSheet = snapshot.sheets?.[sheetId] ?? {};
    const cells: ModelWorkbookSheet['cells'] = {};
    const cellData = (sourceSheet.cellData ?? {}) as Record<
      string,
      Record<string, Record<string, unknown>>
    >;

    Object.entries(cellData).forEach(([rowKey, row]) => {
      Object.entries(row).forEach(([columnKey, cell]) => {
        const modelCell = univerCellToModelCell(cell);
        if (modelCell.value !== undefined || modelCell.formula) {
          cells[cellPositionToAddress(Number(rowKey), Number(columnKey))] = modelCell;
        }
      });
    });

    return {
      id: sheetId,
      name:
        typeof sourceSheet.name === 'string'
          ? sourceSheet.name
          : fallback.sheets.find((sheet) => sheet.id === sheetId)?.name ?? sheetId,
      cells,
    };
  });

  const custom = snapshot.custom?.[MOSAICIQ_NAMESPACE] as
    | Record<string, unknown>
    | undefined;

  return {
    version: fallback.version,
    sheets,
    activeSheetId:
      typeof custom?.activeSheetId === 'string'
        ? custom.activeSheetId
        : fallback.activeSheetId,
    metadata: {
      ...fallback.metadata,
      univerSnapshot: {
        id: snapshot.id,
        rev: snapshot.rev,
        appVersion: snapshot.appVersion,
      },
    },
  };
};
