import { BadRequestException, Injectable } from '@nestjs/common';
import { uuidV7 } from '../common/uuid-v7';
import type { AiReviewColumn, AiReviewRow } from './entities/ai-review.entity';

const MAX_TITLE = 50;
const MAX_COLUMNS = 10;
const MIN_COLUMNS = 2;
const MAX_ROWS = 500;
const MAX_CELL_LENGTH = 200;

const COLUMN_KEY_RE = /^[a-zA-Z0-9_]{1,50}$/;
const CHART_TYPES = new Set(['line', 'bar', 'pie', 'area', 'scatter', 'radar', 'gauge', 'funnel']);
const SCHEMA_KEYS = new Set(['title', 'chartType', 'columns', 'rows']);
const COLUMN_KEYS_ALLOWED = new Set(['key', 'title']);

export interface AiChart {
  id: string;
  title: string;
  chartType: 'line' | 'bar' | 'pie' | 'area' | 'scatter' | 'radar' | 'gauge' | 'funnel';
  columns: AiReviewColumn[];
  rows: AiReviewRow[];
}

function isPlainRecord(value: unknown): value is Record<string, unknown> {
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
}

function requireString(value: unknown, label: string, max: number): string {
  if (typeof value !== 'string' || !value.trim()) {
    throw new BadRequestException(`${label}必须是字符串`);
  }
  const trimmed = value.trim();
  if (trimmed.length > max) {
    throw new BadRequestException(`${label}长度不能超过 ${max}`);
  }
  return trimmed;
}

function assertKeys(raw: Record<string, unknown>, allowed: Set<string>, label: string): void {
  for (const key of Object.keys(raw)) {
    if (!allowed.has(key)) throw new BadRequestException(`${label}包含未知属性: ${key}`);
  }
}

/**
 * Validates the `render_chart` tool arguments. The model sends a
 * whitelisted tabular shape (columns + rows); the frontend converts it
 * into an ECharts option, so no arbitrary option objects reach the client.
 */
@Injectable()
export class AiChartService {
  createChart(rawArgs: unknown): AiChart {
    if (!isPlainRecord(rawArgs)) throw new BadRequestException('图表参数必须是对象');
    assertKeys(rawArgs, SCHEMA_KEYS, '图表');
    const title = requireString(rawArgs.title, '图表标题', MAX_TITLE);
    if (typeof rawArgs.chartType !== 'string' || !CHART_TYPES.has(rawArgs.chartType)) {
      throw new BadRequestException('图表类型不支持');
    }
    const chartType = rawArgs.chartType as AiChart['chartType'];

    if (!Array.isArray(rawArgs.columns) || rawArgs.columns.length < MIN_COLUMNS) {
      throw new BadRequestException('图表至少需要 2 列（类别/名称 + 数值）');
    }
    if (rawArgs.chartType === 'scatter' && rawArgs.columns.length < 3) {
      throw new BadRequestException('散点图需要 3 列：名称、X 数值、Y 数值');
    }
    if (rawArgs.columns.length > MAX_COLUMNS) {
      throw new BadRequestException(`图表列数不能超过 ${MAX_COLUMNS}`);
    }
    const seenColumns = new Set<string>();
    const columns = rawArgs.columns.map((column, index) => {
      if (!isPlainRecord(column)) {
        throw new BadRequestException(`图表第 ${index + 1} 列格式无效`);
      }
      assertKeys(column, COLUMN_KEYS_ALLOWED, `图表第 ${index + 1} 列`);
      const key = requireString(column.key, `图表第 ${index + 1} 列名`, 50);
      if (!COLUMN_KEY_RE.test(key)) {
        throw new BadRequestException(`图表列名 ${key} 只能包含字母、数字、下划线`);
      }
      if (seenColumns.has(key)) throw new BadRequestException(`图表列名重复: ${key}`);
      seenColumns.add(key);
      const columnTitle = requireString(column.title, `图表列「${key}」标题`, 50);
      return { key, title: columnTitle };
    });

    if (!Array.isArray(rawArgs.rows) || rawArgs.rows.length > MAX_ROWS) {
      throw new BadRequestException(`图表行数不能超过 ${MAX_ROWS}`);
    }
    const rows = rawArgs.rows.map((row, index) => this.validateRow(row, index, seenColumns));
    return { id: uuidV7(), title, chartType, columns, rows };
  }

  serialize(chart: AiChart): AiChart {
    return chart;
  }

  private validateRow(raw: unknown, index: number, knownColumns: Set<string>): AiReviewRow {
    if (!isPlainRecord(raw)) throw new BadRequestException(`图表第 ${index + 1} 行格式无效`);
    const row: AiReviewRow = {};
    for (const [key, value] of Object.entries(raw)) {
      if (!knownColumns.has(key)) continue;
      if (value === null || typeof value === 'boolean') {
        row[key] = value;
        continue;
      }
      if (typeof value === 'number') {
        if (!Number.isFinite(value)) {
          throw new BadRequestException(`图表第 ${index + 1} 行 ${key} 必须是有效数字`);
        }
        row[key] = value;
        continue;
      }
      if (typeof value === 'string') {
        if (value.length > MAX_CELL_LENGTH) {
          throw new BadRequestException(
            `图表第 ${index + 1} 行 ${key} 长度超过 ${MAX_CELL_LENGTH}`,
          );
        }
        row[key] = value;
        continue;
      }
      throw new BadRequestException(`图表第 ${index + 1} 行 ${key} 类型不支持`);
    }
    return row;
  }
}
