import { BadRequestException } from '@nestjs/common';
import type {
  AiReviewColumn,
  AiReviewRow,
  AiReviewSection,
  AiReviewSectionType,
} from './entities/ai-review.entity';
import type { ExcelSheetRows } from './ai-excel-reader.service';
import {
  isPlainRecord,
  MAX_CELL_LENGTH,
  MAX_COLUMN_TITLE,
  MAX_ISSUES,
  MAX_ROWS,
  MAX_SECTIONS,
  MAX_SECTION_JSON_BYTES,
  MAX_SECTION_TITLE,
  normalizeSectionType,
  requireString,
  SECTION_ALIASES,
  SECTION_CANONICAL_KEYS,
  SECTION_HEADER_ALIASES,
  SECTION_KEY_RE,
  sectionStatus,
} from './ai-review.shared';

export function buildSectionsFromWorkbook(
  sheets: ExcelSheetRows[],
  rawArgs: unknown,
): AiReviewSection[] {
  if (!isPlainRecord(rawArgs)) throw new BadRequestException('导入预览参数必须是对象');
  const rawSections = rawArgs.sections;
  if (!Array.isArray(rawSections) || rawSections.length === 0) {
    throw new BadRequestException('至少需要一个分表');
  }
  if (rawSections.length > MAX_SECTIONS) {
    throw new BadRequestException(`分表不能超过 ${MAX_SECTIONS} 个`);
  }

  const seen = new Set<string>();
  const sections: AiReviewSection[] = [];
  for (let index = 0; index < rawSections.length; index += 1) {
    const raw: unknown = rawSections[index];
    if (!isPlainRecord(raw) || typeof raw.key !== 'string') {
      throw new BadRequestException(`第 ${index + 1} 个分表格式无效`);
    }
    const key = raw.key.trim();
    if (!SECTION_KEY_RE.test(key)) {
      throw new BadRequestException(`分表标识 ${key} 只能包含字母、数字、下划线（≤50）`);
    }
    const type = normalizeSectionType(key, raw.type);
    if (seen.has(key)) throw new BadRequestException(`分表标识重复: ${key}`);
    seen.add(key);

    const title = requireString(raw.title, '分表标题', MAX_SECTION_TITLE);
    const rawSheet = raw.sheet;
    const sheetName =
      rawSheet === undefined || rawSheet === null
        ? undefined
        : typeof rawSheet === 'string'
          ? rawSheet.trim()
          : (JSON.stringify(rawSheet) ?? '').trim();
    const headerRow = raw.headerRow === undefined || raw.headerRow === null ? 1 : Number(raw.headerRow);
    if (!Number.isInteger(headerRow) || headerRow < 1 || headerRow > 1000) {
      throw new BadRequestException(`分表 ${key} 的 headerRow 无效`);
    }

    const sheet = sheetName
      ? (sheets.find((item) => item.name === sheetName) ??
        sheets.find((item) => item.name.includes(sheetName)))
      : sheets[0];
    if (!sheet) {
      throw new BadRequestException(`找不到工作表「${sheetName}」`);
    }

    sections.push(
      buildSectionFromSheet(key, type, title, sheet.name, sheet, headerRow, raw.columns),
    );
  }
  return sections;
}

export async function buildSectionsFromWorkbookAsync(
  sheets: ExcelSheetRows[],
  rawArgs: unknown,
): Promise<AiReviewSection[]> {
  return await Promise.resolve(buildSectionsFromWorkbook(sheets, rawArgs));
}

function buildSectionFromSheet(
  key: string,
  type: AiReviewSectionType,
  title: string,
  sheetName: string,
  sheet: ExcelSheetRows,
  headerRow: number,
  rawColumns: unknown,
): AiReviewSection {
  const issues: string[] = [];
  const aliasMap = buildHeaderAliasMap(type);
  if (sheet.rows.length < headerRow) {
    return {
      key,
      type,
      title,
      kind: 'table',
      sheet: sheetName,
      columns: [],
      rows: [],
      issues: [`工作表「${sheet.name}」没有第 ${headerRow} 行表头`],
    };
  }

  const explicit = new Map<string, string>();
  if (rawColumns !== undefined) {
    if (!Array.isArray(rawColumns)) {
      throw new BadRequestException(`分表 ${key} 的 columns 无效`);
    }
    for (const column of rawColumns) {
      if (!isPlainRecord(column) || typeof column.key !== 'string') {
        throw new BadRequestException(`分表 ${key} 的列定义无效`);
      }
      const canonical = aliasMap.get(normalizeHeader(column.key));
      if (!canonical || !SECTION_CANONICAL_KEYS[type].has(canonical)) {
        throw new BadRequestException(`分表 ${key} 的列标识无效: ${column.key}`);
      }
      if (typeof column.sourceHeader === 'string' && column.sourceHeader.trim()) {
        explicit.set(normalizeHeader(column.sourceHeader), canonical);
      } else {
        explicit.set(normalizeHeader(column.key), canonical);
      }
    }
  }

  const headerCells = sheet.rows[headerRow - 1];
  const dataRows = sheet.rows.slice(headerRow);
  const mapping = new Map<number, string>();
  const columns: AiReviewColumn[] = [];

  for (let colIndex = 0; colIndex < headerCells.length; colIndex += 1) {
    const header = String(headerCells[colIndex] ?? '').trim();
    if (!header) continue;
    const canonical =
      explicit.get(normalizeHeader(header)) ?? aliasMap.get(normalizeHeader(header));
    if (!canonical) {
      issues.push(`列「${header}」未识别，已忽略`);
      continue;
    }
    if (Array.from(mapping.values()).includes(canonical)) continue;
    mapping.set(colIndex, canonical);
    columns.push({ key: canonical, title: header.slice(0, MAX_COLUMN_TITLE) });
  }

  if (columns.length === 0) {
    return {
      key,
      type,
      title,
      kind: 'table',
      sheet: sheetName,
      columns: [],
      rows: [],
      issues: [...issues, '没有识别到可导入的列'],
    };
  }

  const rows: AiReviewRow[] = [];
  let totalBytes = 0;
  for (const cells of dataRows) {
    const row: AiReviewRow = {};
    for (const [colIndex, canonical] of mapping) {
      const raw = cells[colIndex];
      const text = raw === undefined || raw === null ? '' : String(raw).trim();
      if (!text) continue;
      row[canonical] = text.length > MAX_CELL_LENGTH ? text.slice(0, MAX_CELL_LENGTH) : text;
    }
    if (Object.keys(row).length === 0) continue;
    const rowBytes = Buffer.byteLength(JSON.stringify(row), 'utf8');
    if (totalBytes + rowBytes > MAX_SECTION_JSON_BYTES) {
      issues.push(`「${title}」数据量过大，仅保留前 ${rows.length} 行`);
      break;
    }
    totalBytes += rowBytes;
    rows.push(row);
    if (rows.length >= MAX_ROWS) {
      issues.push(`「${title}」超过 ${MAX_ROWS} 行，仅保留前 ${MAX_ROWS} 行`);
      break;
    }
  }

  return {
    key,
    type,
    title,
    kind: 'table',
    sheet: sheetName,
    columns,
    rows,
    issues: [...new Set(issues)].slice(-MAX_ISSUES),
  };
}

function buildHeaderAliasMap(key: AiReviewSectionType): Map<string, string> {
  const merged: Record<string, string> = {
    ...SECTION_HEADER_ALIASES[key],
    ...SECTION_ALIASES[key],
  };
  const map = new Map<string, string>();
  for (const [header, canonical] of Object.entries(merged)) {
    map.set(normalizeHeader(header), canonical);
  }
  return map;
}

function normalizeHeader(value: string): string {
  return value.trim().toLowerCase().replace(/[\s_-]+/g, '');
}

export function parseSections(sectionsJson: string): AiReviewSection[] {
  let parsed: unknown;
  try {
    parsed = JSON.parse(sectionsJson);
  } catch {
    return [];
  }
  if (!Array.isArray(parsed)) return [];
  return parsed.map((item) => {
    if (!isPlainRecord(item) || typeof item.key !== 'string') {
      throw new BadRequestException('导入预览分表格式无效');
    }
    const section = item as Partial<AiReviewSection> & { key: string };
    const type = normalizeSectionType(section.key, section.type);
    return {
      ...section,
      key: section.key,
      type,
      title: typeof section.title === 'string' ? section.title : section.key,
      kind: 'table',
      columns: Array.isArray(section.columns) ? section.columns : [],
      rows: Array.isArray(section.rows) ? section.rows : [],
      issues: Array.isArray(section.issues) ? section.issues : [],
      ...(typeof section.sheet === 'string' ? { sheet: section.sheet } : {}),
      status: sectionStatus(section as AiReviewSection),
      resultSummary:
        typeof section.resultSummary === 'string' ? section.resultSummary : null,
      submittedAt:
        typeof section.submittedAt === 'string' ? section.submittedAt : null,
    };
  });
}
