import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DataSource, Repository } from 'typeorm';
import { AiConfigService } from '../ai-config/ai-config.service';
import { AgentToolExecutor } from '../agent-tools/agent-tool.executor';
import {
  AgentToolContextFactory,
  type AgentSkillDescriptor,
} from '../agent-tools/agent-tool.types';
import { AuthorizationService, CaslAbilityFactory, type AuthenticatedUser } from '../authorization';
import { AiAttachmentService } from './ai-attachment.service';
import { ImportsService } from '../imports/imports.service';
import { AiChartService } from './ai-chart.service';
import { AiExcelReaderService } from './ai-excel-reader.service';
import { AiFormService } from './ai-form.service';
import { AiReviewService } from './ai-review.service';
import { A2uiSubmissionsService } from './ai-a2ui-submissions.service';
import { AiModelStreamService } from './ai-model-stream.service';
import { OperationLogsService } from '../operation-logs/operation-logs.service';
import { AiConversation, AiMessage, AiToolRun } from './entities';
import type {
  GenerationInput,
  ModelContentPart,
  ModelMessage,
} from './ai-chat.types';
import {
  requireOwnedConversation,
  acquireConversation,
  normalizeTitle,
  titleFromMessage,
  metadataSkillKey,
  assertSkillAvailable,
  truncateText,
  serializeMessage,
} from './ai-chat.conversations';
import { buildContext, buildUserContent } from './ai-chat.streaming';
import { executeGeneration } from './ai-chat.generation';
import {
  assertGeneratedLength,
  errorCode,
  makeRedactingReplacer,
  parseToolArguments,
  redactText,
  safeToolName,
  throwIfAborted,
} from './ai-chat.helpers';
import { AiChatServiceBase } from './ai-chat.service-base';

@Injectable()
export class AiChatService extends AiChatServiceBase {
  private readonly redactingReplacer = makeRedactingReplacer((value) => this.redactText(value));

  constructor(
    @InjectRepository(AiConversation)
    conversations: Repository<AiConversation>,
    @InjectRepository(AiMessage)
    messages: Repository<AiMessage>,
    @InjectRepository(AiToolRun)
    toolRuns: Repository<AiToolRun>,
    dataSource: DataSource,
    configService: AiConfigService,
    toolExecutor: AgentToolExecutor,
    modelStream: AiModelStreamService,
    attachmentService: AiAttachmentService,
    formService: AiFormService,
    reviewService: AiReviewService,
    chartService: AiChartService,
    abilityFactory: CaslAbilityFactory,
    authorization: AuthorizationService,
    a2uiSubmissions?: A2uiSubmissionsService,
    excelReader?: AiExcelReaderService,
    importsService?: ImportsService,
    opLog?: OperationLogsService,
  ) {
    super(
      conversations,
      messages,
      toolRuns,
      dataSource,
      configService,
      toolExecutor,
      modelStream,
      attachmentService,
      formService,
      reviewService,
      chartService,
      abilityFactory,
      authorization,
      a2uiSubmissions,
      excelReader,
      importsService,
      opLog,
    );
  }

  listSkills(user: AuthenticatedUser): AgentSkillDescriptor[] {
    return this.toolExecutor.listSkills(AgentToolContextFactory.fromAuthenticatedUser(user));
  }

  serializeMessage(message: AiMessage): Record<string, unknown> {
    return serializeMessage(this, message);
  }

  redactText(value: string): string {
    return redactText(value);
  }

  summarize(value: unknown): string | null {
    if (value === undefined || value === null) return null;
    let json: string;
    try {
      json = JSON.stringify(value, this.redactingReplacer);
    } catch {
      return '[无法序列化]';
    }
    return this.redactText(json).slice(0, 2000);
  }

  safeStructured(value: unknown): unknown {
    if (value === undefined || value === null) return null;
    try {
      return JSON.parse(JSON.stringify(value, this.redactingReplacer)) as unknown;
    } catch {
      return null;
    }
  }

  parseToolArguments(value: string): unknown {
    return parseToolArguments(value);
  }

  safeToolName(name: string): string {
    return safeToolName(name);
  }

  throwIfAborted(signal: AbortSignal): void {
    return throwIfAborted(signal);
  }

  errorCode(error: unknown): string {
    return errorCode(error);
  }

  assertGeneratedLength(reasoning: string, content: string): void {
    return assertGeneratedLength(reasoning, content);
  }

  buildContext(
    conversationId: number,
    focusUserMessageId: number,
    focusContent: string | ModelContentPart[],
    skillKey: string | null,
    supportsVision: boolean,
  ): Promise<ModelMessage[]> {
    return buildContext(this, conversationId, focusUserMessageId, focusContent, skillKey, supportsVision);
  }

  buildUserContent(
    text: string,
    attachments: any[],
    supportsVision: boolean,
  ): Promise<string | ModelContentPart[]> {
    return buildUserContent(this, text, attachments, supportsVision);
  }

  truncateText(value: string, max: number): string {
    return truncateText(this, value, max);
  }

  metadataSkillKey(metadata: Record<string, unknown> | null): string | null {
    return metadataSkillKey(this, metadata);
  }

  normalizeTitle(title?: string): string {
    return normalizeTitle(this, title);
  }

  titleFromMessage(message: string): string {
    return titleFromMessage(this, message);
  }

  assertSkillAvailable(user: AuthenticatedUser, skillKey?: string | null): void {
    return assertSkillAvailable(this, user, skillKey);
  }

  requireOwnedConversation(userId: number, id: number): Promise<AiConversation> {
    return requireOwnedConversation(this, userId, id);
  }

  acquireConversation(conversationId: number): Promise<void> {
    return acquireConversation(this, conversationId);
  }

  executeGeneration(input: GenerationInput): Promise<void> {
    return executeGeneration(this, input);
  }
}
