import { AgentToolContextFactory } from '../agent-tools/agent-tool.types';
import type { AiChatServiceContext, AiSseEmitter, ModelToolCall } from './ai-chat.types';
import type { AiToolRun } from './entities';
import {
  executeRenderChart,
  executeRenderForm,
  executeStartImportWizard,
} from './ai-chat.tool-actions';

export type AgentToolContext = ReturnType<typeof AgentToolContextFactory.fromAuthenticatedUser>;

export async function startToolRun(
  context: AiChatServiceContext,
  messageId: number,
  call: ModelToolCall,
  emit: AiSseEmitter,
  options: {
    toolName: string;
    skillKey: string | null;
    argumentsData?: Record<string, unknown> | null;
    parsedArgs?: unknown;
  },
): Promise<{ run: AiToolRun; parsedArgs: unknown; startedAt: number }> {
  const startedAt = Date.now();
  const parsedArgs = options.parsedArgs ?? context.parseToolArguments(call.arguments);
  const run = await context.toolRuns.save(
    context.toolRuns.create({
      messageId,
      toolCallId: call.id.slice(0, 100),
      toolName: options.toolName,
      skillKey: options.skillKey,
      argumentsSummary: context.summarize(parsedArgs),
      resultSummary: null,
      argumentsData:
        options.argumentsData ??
        (context.safeStructured(parsedArgs) as Record<string, unknown> | null),
      resultData: null,
      status: 'running',
      durationMs: null,
    }),
  );
  emit('tool.started', {
    messageId,
    toolCallId: call.id,
    toolName: run.toolName,
    skillKey: run.skillKey,
    status: 'running',
    summary: run.argumentsSummary,
  });
  return { run, parsedArgs, startedAt };
}

export async function finishToolRun(
  context: AiChatServiceContext,
  run: AiToolRun,
  call: ModelToolCall,
  startedAt: number,
  outcome: { status: 'success' | 'failed'; summary: string | null; error?: string },
  emit: AiSseEmitter,
): Promise<void> {
  run.status = outcome.status;
  run.resultSummary = outcome.summary;
  run.durationMs = Date.now() - startedAt;
  await context.toolRuns.save(run);
  emit(outcome.status === 'success' ? 'tool.completed' : 'tool.failed', {
    messageId: run.messageId,
    toolCallId: call.id,
    toolName: run.toolName,
    skillKey: run.skillKey,
    status: outcome.status,
    summary: outcome.summary,
    ...(outcome.error ? { error: outcome.error } : {}),
    durationMs: run.durationMs,
  });
}

export async function executeTool(
  context: AiChatServiceContext,
  messageId: number,
  call: ModelToolCall,
  agentContext: AgentToolContext,
  allowedSkillKey: string | null,
  allowWriteTools: boolean,
  reviewSubmitted: boolean,
  userId: number,
  emit: AiSseEmitter,
): Promise<string> {
  if (call.name === 'render_form') {
    return executeRenderForm(context, messageId, call, userId, emit);
  }
  if (call.name === 'start_import_wizard') {
    return executeStartImportWizard(context, messageId, call, agentContext, emit);
  }
  if (call.name === 'render_chart') {
    return executeRenderChart(context, messageId, call, emit);
  }
  if ((call.name === 'create_student' || call.name === 'update_students') && !allowWriteTools) {
    return denyWriteTool(context, messageId, call, emit);
  }
  const toolSkillKey =
    context.toolExecutor.listAvailable(agentContext).find((tool) => tool.name === call.name)
      ?.skillKey ?? allowedSkillKey;
  const { run, parsedArgs, startedAt } = await startToolRun(context, messageId, call, emit, {
    toolName: context.safeToolName(call.name),
    skillKey: toolSkillKey,
  });

  const result = await context.toolExecutor.execute(call.name, parsedArgs, agentContext, allowedSkillKey);
  run.status = result.status;
  run.skillKey = result.skillKey ?? run.skillKey;
  run.resultSummary = context.summarize(result.result ?? result.error ?? null);
  run.resultData = context.safeStructured(result.result) as
    | Record<string, unknown>
    | unknown[]
    | null;
  run.durationMs = Date.now() - startedAt;
  await context.toolRuns.save(run);

  emit(result.status === 'success' ? 'tool.completed' : 'tool.failed', {
    messageId,
    toolCallId: call.id,
    toolName: run.toolName,
    skillKey: run.skillKey,
    status: result.status,
    summary: run.resultSummary,
    ...(result.error ? { error: result.error } : {}),
    durationMs: run.durationMs,
  });
  const modelPayload = JSON.stringify(
    result.status === 'success'
      ? { status: result.status, data: result.result }
      : { status: result.status, error: result.error },
  );
  if (modelPayload.length <= 32 * 1024) return modelPayload;
  return JSON.stringify({
    status: result.status,
    truncated: true,
    summary: context.summarize(result.result ?? result.error ?? null),
  });
}

export async function denyWriteTool(
  context: AiChatServiceContext,
  messageId: number,
  call: ModelToolCall,
  emit: AiSseEmitter,
): Promise<string> {
  const toolName =
    typeof call.name === 'string' && call.name.trim() ? call.name : 'create_student';
  return denyTool(context, messageId, call, toolName, '该操作需要表单确认', '该操作需要表单确认', emit);
}

export async function denyTool(
  context: AiChatServiceContext,
  messageId: number,
  call: ModelToolCall,
  toolName: string,
  summary: string,
  error: string,
  emit: AiSseEmitter,
): Promise<string> {
  await context.toolRuns.save(
    context.toolRuns.create({
      messageId,
      toolCallId: call.id.slice(0, 100),
      toolName: context.safeToolName(toolName),
      skillKey: null,
      argumentsSummary: context.summarize(context.parseToolArguments(call.arguments)),
      resultSummary: summary,
      argumentsData: null,
      resultData: null,
      status: 'failed',
      durationMs: 0,
    }),
  );
  emit('tool.failed', {
    messageId,
    toolCallId: call.id,
    toolName: context.safeToolName(toolName),
    status: 'failed',
    summary,
    error,
    durationMs: 0,
  });
  return JSON.stringify({ status: 'failed', error });
}
