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 { AiModelStreamService } from './ai-model-stream.service';
import { OperationLogsService } from '../operation-logs/operation-logs.service';
import {
  AiConversation,
  AiMessage,
  AiReview,
  AiToolRun,
  type AiReviewSectionType,
} from './entities';
import type {
  AiChatServiceContext,
  AiSseEmitter,
  GenerationInput,
  ModelContentPart,
  ModelMessage,
  ModelToolCall,
  PublicConversation,
} from './ai-chat.types';
import {
  listConversations,
  createConversation,
  updateConversation,
  deleteConversation,
  deleteAllConversations,
  getMessages,
  deleteMessage,
  requireOwnedConversation,
  acquireConversation,
  normalizeTitle,
  titleFromMessage,
  metadataSkillKey,
  assertSkillAvailable,
  truncateText,
  serializeMessage,
} from './ai-chat.conversations';
import {
  streamMessage,
  regenerateMessage,
  editMessage,
  buildContext,
  buildUserContent,
} from './ai-chat.streaming';
import {
  resolveFormConversationId,
  resolvePreflightConversationId,
  resolveReviewConversationId,
  resolveImportPreflight,
  submitForm,
  submitReview,
  confirmReviewStep,
  confirmReviewGroup,
  assertReviewImportPermissions,
  a2uiSubmitInfo,
  buildFormSubmitModelContent,
  markFormSubmittedOnMessage,
  a2uiReviewSubmitInfo,
  buildReviewSubmitModelContent,
  markReviewSubmittedOnMessage,
} from './ai-chat.submissions';
import { denyWriteTool, executeTool } from './ai-chat.tools';
import {
  executePreflightImport,
  executeStartImportWizard,
} from './ai-chat.tool-actions';
import { executeGeneration } from './ai-chat.generation';
import {
  assertGeneratedLength,
  errorCode,
  makeRedactingReplacer,
  parseToolArguments,
  redactText,
  safeToolName,
  throwIfAborted,
} from './ai-chat.helpers';

@Injectable()
export class AiChatService implements AiChatServiceContext {
  readonly activeConversations = new Set<number>();

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

  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);
  }

  private readonly redactingReplacer = makeRedactingReplacer((value) => this.redactText(value));

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

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

  buildFormSubmitModelContent(submit: { title: string; values: Record<string, unknown> }): string {
    return buildFormSubmitModelContent(submit);
  }

  buildReviewSubmitModelContent(submit: {
    reviewId: string;
    reviewTitle: string;
    resultMessage: string;
  }): string {
    return buildReviewSubmitModelContent(submit);
  }

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

  markReviewSubmittedOnMessage(
    assistantMessageId: number,
    conversationId: number,
    review?: AiReview,
  ): Promise<void> {
    return markReviewSubmittedOnMessage(this, assistantMessageId, conversationId, review);
  }

  assertReviewImportPermissions(
    user: AuthenticatedUser,
    review: AiReview,
    sectionKey?: string,
    sectionType?: AiReviewSectionType,
  ): void {
    return assertReviewImportPermissions(this, user, review, sectionKey, sectionType);
  }

  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);
  }

  executeTool(
    messageId: number,
    call: ModelToolCall,
    context: ReturnType<typeof AgentToolContextFactory.fromAuthenticatedUser>,
    allowedSkillKey: string | null,
    allowWriteTools: boolean,
    reviewSubmitted: boolean,
    userId: number,
    emit: AiSseEmitter,
  ): Promise<string> {
    return executeTool(
      this,
      messageId,
      call,
      context,
      allowedSkillKey,
      allowWriteTools,
      reviewSubmitted,
      userId,
      emit,
    );
  }

  denyWriteTool(
    messageId: number,
    call: ModelToolCall,
    emit: AiSseEmitter,
  ): Promise<string> {
    return denyWriteTool(this, messageId, call, emit);
  }

  executeStartImportWizard(
    messageId: number,
    call: ModelToolCall,
    context: ReturnType<typeof AgentToolContextFactory.fromAuthenticatedUser>,
    emit: AiSseEmitter,
  ): Promise<string> {
    return executeStartImportWizard(this, messageId, call, context, emit);
  }

  executePreflightImport(
    messageId: number,
    call: ModelToolCall,
    context: ReturnType<typeof AgentToolContextFactory.fromAuthenticatedUser>,
    emit: AiSseEmitter,
  ): Promise<string> {
    return executePreflightImport(this, messageId, call, context, emit);
  }

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

  listConversations(userId: number): Promise<PublicConversation[]> {
    return listConversations(this, userId);
  }

  createConversation(
    user: AuthenticatedUser,
    title?: string,
    lockedSkillKey?: string | null,
  ): Promise<PublicConversation> {
    return createConversation(this, user, title, lockedSkillKey);
  }

  updateConversation(
    user: AuthenticatedUser,
    id: number,
    dto: { title?: string; lockedSkillKey?: string | null },
  ): Promise<PublicConversation> {
    return updateConversation(this, user, id, dto);
  }

  deleteConversation(userId: number, id: number): Promise<void> {
    return deleteConversation(this, userId, id);
  }

  deleteAllConversations(userId: number): Promise<number> {
    return deleteAllConversations(this, userId);
  }

  getMessages(userId: number, conversationId: number, page = 1, limit = 50) {
    return getMessages(this, userId, conversationId, page, limit);
  }

  deleteMessage(
    userId: number,
    conversationId: number,
    messageId: number,
  ): Promise<{ deletedIds: number[] }> {
    return deleteMessage(this, userId, conversationId, messageId);
  }

  streamMessage(
    user: AuthenticatedUser,
    conversationId: number,
    dto: {
      message: string;
      attachmentIds?: number[];
      clientRequestId: string;
      skillKey?: string | null;
      reasoningEffort?: string | null;
    },
    signal: AbortSignal,
    emit: AiSseEmitter,
    onReady: () => void,
  ): Promise<void> {
    return streamMessage(this, user, conversationId, dto, signal, emit, onReady);
  }

  regenerateMessage(
    user: AuthenticatedUser,
    conversationId: number,
    assistantMessageId: number,
    clientRequestId: string,
    reasoningEffort: string | null | undefined,
    signal: AbortSignal,
    emit: AiSseEmitter,
    onReady: () => void,
  ): Promise<void> {
    return regenerateMessage(
      this,
      user,
      conversationId,
      assistantMessageId,
      clientRequestId,
      reasoningEffort,
      signal,
      emit,
      onReady,
    );
  }

  editMessage(
    user: AuthenticatedUser,
    conversationId: number,
    messageId: number,
    dto: { content: string; clientRequestId: string; reasoningEffort?: string | null },
    signal: AbortSignal,
    emit: AiSseEmitter,
    onReady: () => void,
  ): Promise<void> {
    return editMessage(this, user, conversationId, messageId, dto, signal, emit, onReady);
  }

  resolveFormConversationId(userId: number, formId: string): Promise<number> {
    return resolveFormConversationId(this, userId, formId);
  }

  resolveReviewConversationId(userId: number, reviewId: string): Promise<number> {
    return resolveReviewConversationId(this, userId, reviewId);
  }

  resolvePreflightConversationId(userId: number, messageId: number): Promise<number> {
    return resolvePreflightConversationId(this, userId, messageId);
  }

  submitForm(
    user: AuthenticatedUser,
    formId: string,
    dto: {
      values: Record<string, unknown>;
      clientRequestId: string;
      reasoningEffort?: string | null;
    },
    signal: AbortSignal,
    emit: AiSseEmitter,
    onReady: () => void,
  ): Promise<void> {
    return submitForm(this, user, formId, dto, signal, emit, onReady);
  }

  submitReview(
    user: AuthenticatedUser,
    reviewId: string,
    dto: { clientRequestId: string; reasoningEffort?: string | null },
    signal: AbortSignal,
    emit: AiSseEmitter,
    onReady: () => void,
  ): Promise<void> {
    return submitReview(this, user, reviewId, dto, signal, emit, onReady);
  }

  resolveImportPreflight(
    user: AuthenticatedUser,
    messageId: number,
    dto: {
      clientRequestId: string;
      mapping?: Record<string, unknown>;
      settings?: Record<string, unknown>;
    },
    signal: AbortSignal,
    emit: AiSseEmitter,
    onReady: () => void,
  ): Promise<void> {
    return resolveImportPreflight(this, user, messageId, dto, signal, emit, onReady);
  }

  confirmReviewStep(
    user: AuthenticatedUser,
    reviewId: string,
    sectionKey: string,
  ): Promise<Record<string, unknown>> {
    return confirmReviewStep(this, user, reviewId, sectionKey);
  }

  confirmReviewGroup(
    user: AuthenticatedUser,
    reviewId: string,
    type: AiReviewSectionType,
  ): Promise<Record<string, unknown>> {
    return confirmReviewGroup(this, user, reviewId, type);
  }
}
