import { Injectable, Logger } from '@nestjs/common';
import { createClient, type ClickHouseClient } from '@clickhouse/client';
import type { ClickHouseSource } from './manifest.schema';

/**
 * ClickHouseSourceService — 把 ClickHouse SQL 查询结果作为 cold-import 数据源。
 *
 * 形态:执行 manifest.sql_source.queries 里每个 SQL,把结果以
 *      `Record<tableName, row[]>` 形式返回,跟文件源完全等价
 *      (下游 AssemblerEngine 无感知差别)。
 *
 * 短期 cold-import 直连模式;W4+ 升级为带 cursor 的真增量 pull(ClickHouseSqlPullStrategy)。
 *
 * 安全:密码从环境变量读取(password_env),plaintext 永不进 yaml。
 */
@Injectable()
export class ClickHouseSourceService {
  private readonly logger = new Logger(ClickHouseSourceService.name);

  async loadAllTables(source: ClickHouseSource): Promise<Record<string, unknown[]>> {
    const password = process.env[source.connection.password_env];
    if (!password) {
      throw new Error(
        `ClickHouse 密码缺失:环境变量 ${source.connection.password_env} 未设置;` +
          `请检查 .env 或 dotenv 加载`,
      );
    }

    const client: ClickHouseClient = createClient({
      url: source.connection.url,
      database: source.connection.database,
      username: source.connection.username,
      password,
      // Aliyun ADS 公网偶尔抽风,加大重试
      request_timeout: 60_000,
      compression: { response: true, request: false },
    });

    const tables: Record<string, unknown[]> = {};
    const defaultLimit = source.default_limit ?? 100_000;

    try {
      for (const [tableName, sql] of Object.entries(source.queries)) {
        const sqlWithLimit = this.applyDefaultLimit(sql, defaultLimit);
        this.logger.log(
          `[clickhouse] query "${tableName}" — ${sqlWithLimit.slice(0, 120).replace(/\s+/g, ' ')}...`,
        );
        const started = Date.now();
        const result = await client.query({
          query: sqlWithLimit,
          format: 'JSONEachRow',
        });
        const rows = (await result.json()) as unknown[];
        const elapsed = Date.now() - started;
        tables[tableName] = rows;
        this.logger.log(
          `[clickhouse]   "${tableName}" → ${rows.length} 行,${elapsed} ms`,
        );
      }
    } finally {
      await client.close();
    }

    return tables;
  }

  /**
   * 给没显式带 LIMIT 的 SQL 自动追加 LIMIT,防内存炸。
   * 简单字符串嗅探,匹配 LIMIT N(忽略大小写,允许末尾分号)。
   */
  private applyDefaultLimit(sql: string, defaultLimit: number): string {
    const trimmed = sql.trim().replace(/;\s*$/, '');
    if (/\bLIMIT\s+\d+(\s*,\s*\d+)?\s*$/i.test(trimmed)) return trimmed;
    return `${trimmed} LIMIT ${defaultLimit}`;
  }
}
