import { BadRequestException } from '@nestjs/common';
import { AiConfig } from './ai-config.entity';
import {
  pinnedGet,
  testFailureResult,
  validateAndNormalizeBaseUrl,
  validateDnsNotPrivate,
} from './ai-config.helpers';
import type {
  AiConfigTestResultDto,
  FetchModelsDto,
  FetchModelsResultDto,
  TestAiConfigDto,
} from './dto/ai-config.dto';

export interface AiConfigProbeContext {
  getOrCreateConfig(): Promise<AiConfig>;
  resolveApiKey(config: AiConfig | null): {
    plaintext: string | null;
    source: 'database' | 'environment' | 'none';
  };
  save(config: AiConfig): Promise<AiConfig>;
}

export async function testConnection(
  context: AiConfigProbeContext,
  dto?: TestAiConfigDto,
): Promise<AiConfigTestResultDto> {
  const config = await context.getOrCreateConfig();
  const now = new Date().toISOString();

  // Determine effective provider / baseUrl
  const provider = dto?.provider ?? config.provider;
  const rawBaseUrl = dto?.baseUrl ?? config.baseUrl;
  let baseUrl: string;
  try {
    baseUrl = validateAndNormalizeBaseUrl(rawBaseUrl, provider);
  } catch (err: unknown) {
    const message = err instanceof BadRequestException ? err.message : '请求参数无效';
    return testFailureResult(message, now);
  }

  // Determine effective defaultModel
  const effectiveDefaultModel = dto?.defaultModel ?? config.defaultModel ?? '';

  // DNS check
  try {
    await validateDnsNotPrivate(new URL(baseUrl).hostname);
  } catch (err: unknown) {
    const message = err instanceof BadRequestException ? err.message : '请求参数无效';
    return testFailureResult(message, now);
  }

  // Determine API key
  let apiKey: string;
  if (dto?.apiKey) {
    apiKey = dto.apiKey;
  } else {
    const { plaintext } = context.resolveApiKey(config);
    if (!plaintext) {
      return {
        success: false,
        latencyMs: null,
        modelCount: null,
        modelAvailable: false,
        testedAt: now,
        message: '未配置 API Key',
      };
    }
    apiKey = plaintext;
  }

  const timeoutMs = dto?.timeoutMs ?? config.timeoutMs;

  let result: AiConfigTestResultDto;
  try {
    const { status, contentType, body, latencyMs } = await pinnedGet(
      `${baseUrl}/models`,
      { Authorization: `Bearer ${apiKey}` },
      timeoutMs,
    );

    // Classify by HTTP status first, then content-type
    if (status === 401 || status === 403) {
      result = {
        success: false,
        latencyMs,
        modelCount: null,
        modelAvailable: false,
        testedAt: now,
        message: '认证失败，请检查 API Key',
      };
    } else if (status >= 500) {
      result = {
        success: false,
        latencyMs,
        modelCount: null,
        modelAvailable: false,
        testedAt: now,
        message: '服务不可用',
      };
    } else if (status >= 400) {
      result = {
        success: false,
        latencyMs,
        modelCount: null,
        modelAvailable: false,
        testedAt: now,
        message: `服务返回错误状态 ${status}`,
      };
    } else if (!contentType || !contentType.includes('application/json')) {
      result = {
        success: false,
        latencyMs,
        modelCount: null,
        modelAvailable: false,
        testedAt: now,
        message: '响应格式无效',
      };
    } else {
      let data: { data?: Array<{ id: string }> };
      try {
        const parsed: unknown = JSON.parse(body);
        if (!parsed || typeof parsed !== 'object') throw new Error('invalid');
        data = parsed;
      } catch {
        config.lastTestedAt = new Date();
        config.lastTestLatencyMs = latencyMs;
        config.verified = false;
        await context.save(config);
        return {
          success: false,
          latencyMs,
          modelCount: null,
          modelAvailable: false,
          testedAt: now,
          message: '响应格式无效',
        };
      }

      const models = Array.isArray(data?.data) ? data.data : [];
      const modelCount = models.length;
      const modelAvailable =
        !effectiveDefaultModel || models.some((m) => m.id === effectiveDefaultModel);

      const message = modelAvailable
        ? `连接成功，目标模型 "${effectiveDefaultModel}" 可用`
        : effectiveDefaultModel
          ? '连接成功，但未找到目标模型'
          : models.length > 0
            ? `连接成功，可用模型 ${models.length} 个`
            : '连接成功，但未返回可用模型';

      result = {
        success: true,
        latencyMs,
        modelCount,
        modelAvailable,
        testedAt: now,
        message,
      };
    }
  } catch (err: unknown) {
    const message =
      err instanceof Error
        ? err.message === '连接超时'
          ? '连接超时'
          : err.message === '响应过大'
            ? '响应过大'
            : err.message === '禁止重定向'
              ? '连接失败，请检查 Base URL'
              : '连接失败，请检查 Base URL'
        : '连接失败，请检查 Base URL';
    result = {
      success: false,
      latencyMs: null,
      modelCount: null,
      modelAvailable: false,
      testedAt: now,
      message,
    };
  }

  config.lastTestedAt = new Date();
  config.lastTestLatencyMs = result.latencyMs;
  config.verified = result.success;
  await context.save(config);
  return result;
}

export async function fetchModels(
  context: AiConfigProbeContext,
  dto?: FetchModelsDto,
): Promise<FetchModelsResultDto> {
  const config = await context.getOrCreateConfig();

  const provider = dto?.provider ?? config.provider;
  const rawBaseUrl = dto?.baseUrl ?? config.baseUrl;
  let baseUrl: string;
  try {
    baseUrl = validateAndNormalizeBaseUrl(rawBaseUrl, provider);
  } catch (err: unknown) {
    const message = err instanceof BadRequestException ? err.message : '请求参数无效';
    return { success: false, models: [], message };
  }

  // DNS SSRF check
  try {
    await validateDnsNotPrivate(new URL(baseUrl).hostname);
  } catch (err: unknown) {
    const message = err instanceof BadRequestException ? err.message : '请求参数无效';
    return { success: false, models: [], message };
  }

  // Determine API key
  let apiKey: string;
  if (dto?.apiKey) {
    apiKey = dto.apiKey;
  } else {
    const { plaintext } = context.resolveApiKey(config);
    if (!plaintext) {
      return { success: false, models: [], message: '未配置 API Key' };
    }
    apiKey = plaintext;
  }

  const timeoutMs = dto?.timeoutMs ?? config.timeoutMs;

  try {
    const { status, contentType, body } = await pinnedGet(
      `${baseUrl}/models`,
      { Authorization: `Bearer ${apiKey}` },
      timeoutMs,
    );

    if (status === 401 || status === 403) {
      return { success: false, models: [], message: '认证失败，请检查 API Key' };
    }
    if (status >= 500) {
      return { success: false, models: [], message: '服务不可用' };
    }
    if (status >= 400) {
      return { success: false, models: [], message: `服务返回错误状态 ${status}` };
    }
    if (!contentType || !contentType.includes('application/json')) {
      return { success: false, models: [], message: '响应格式无效' };
    }

    const parsed: unknown = JSON.parse(body);
    if (!parsed || typeof parsed !== 'object') {
      return { success: false, models: [], message: '响应格式无效' };
    }

    const data = parsed as { data?: Array<{ id: string }> };
    const models = Array.isArray(data?.data) ? data.data : [];
    return { success: true, models };
  } catch {
    return { success: false, models: [], message: '获取模型列表失败，请检查配置' };
  }
}
