import { Provide, Inject, Logger } from '@midwayjs/core';
import { ILogger } from '@midwayjs/logger';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { NetaClawCrewTaskEntity } from '../entity/crew_task.js';
import { runAgent, AgentConfig } from '../runtime/agent.js';
import { SkillLoaderService } from './skill_loader.js';
import { NetaClawModelChannelService } from './model_channel.js';
import { buildSystemPrompt } from '../runtime/prompt_builder.js';
import { NetaClawToolResolverService } from './tool_resolver.js';
import type { ModelCapability } from '../entity/model_channel.js';
import type { CrewRunContext, DelegateResult, ParallelTaskItem } from './crew_types.js';

/**
 * 委派执行器，负责执行子 Agent 任务。
 */
@Provide()
export class CrewDelegateService {
  @Logger()
  logger: ILogger;

  @InjectEntityModel(NetaClawCrewTaskEntity)
  crewTaskRepo: Repository<NetaClawCrewTaskEntity>;

  @Inject()
  skillLoader: SkillLoaderService;

  @Inject()
  modelChannelService: NetaClawModelChannelService;

  @Inject()
  toolResolver: NetaClawToolResolverService;

  /**
   * 解析 Agent 的模型配置，返回 AgentConfig 所需的 model/apiKey/baseUrl/capability。
   */
  private async resolveModelConfig(agent: any): Promise<{
    model: string;
    apiKey: string;
    baseUrl?: string;
    capability: ModelCapability;
  }> {
    const mc = agent.modelConfig || {};
    if (mc.channelId) {
      const resolved = await this.modelChannelService.resolveForAgent(mc.channelId, mc.modelId || '');
      return {
        model: `${resolved.provider}:${resolved.model}`,
        apiKey: resolved.apiKey,
        baseUrl: resolved.baseUrl,
        capability: resolved.capability,
      };
    }
    if (mc.apiKey && mc.modelId) {
      return {
        model: mc.modelId,
        apiKey: mc.apiKey,
        baseUrl: mc.apiUrl,
        capability: 'text',
      };
    }
    return {
      model: mc.modelId || (process.env.NETACLAW_MODEL ?? 'anthropic:claude-sonnet-4-20250514'),
      apiKey: process.env.NETACLAW_API_KEY ?? '',
      baseUrl: mc.apiUrl,
      capability: 'text',
    };
  }

  /**
   * 执行单个子 Agent 任务。
   */
  async executeSubAgent(
    ctx: CrewRunContext,
    member: CrewRunContext['memberAgents'][0],
    taskDescription: string,
    context?: string,
  ): Promise<DelegateResult> {
    const startTime = Date.now();
    const agent = member.agent;

    const task = this.crewTaskRepo.create({
      runId: ctx.runId,
      agentId: member.id,
      taskDescription,
      status: 'running',
      startTime: new Date(),
      timeout: ctx.taskTimeout,
    });
    const saved = await this.crewTaskRepo.save(task);
    const taskId = saved.id;

    ctx.callbacks.onTaskStatus(ctx.runId, taskId, member.name, 'running');
    ctx.callbacks.onLog(ctx.runId, taskId, member.name, 'info', `开始执行: ${taskDescription}`);

    try {
      const modelInfo = await this.resolveModelConfig(agent);
      const resolvedTools = await this.toolResolver.resolve({
        agent,
        modelCapability: modelInfo.capability,
        hasSkills: !!agent.skills?.length,
        crewRole: 'sub',
      });

      const pureModelId = modelInfo.model.includes(':')
        ? modelInfo.model.split(':')[1]
        : modelInfo.model;
      const rolePrefix = member.role
        ? `${agent.systemPrompt || ''}\n\n你在团队中的角色: ${member.role}`
        : (agent.systemPrompt || '');
      const { prompt: systemPrompt } = buildSystemPrompt({
        agentSystemPrompt: rolePrefix,
        modelId: pureModelId,
        availableToolNames: resolvedTools.toolNames,
        skills: agent.skills || [],
        skillLoader: this.skillLoader,
        memoryEnabled: false,
        toolPromptHints: resolvedTools.toolPromptHints,
      });

      const agentConfig: AgentConfig = {
        name: member.name,
        systemPrompt,
        model: modelInfo.model,
        apiKey: modelInfo.apiKey,
        baseUrl: modelInfo.baseUrl,
        temperature: agent.config?.temperature,
        maxTokens: agent.config?.maxTokens,
        maxToolRounds: agent.config?.maxToolRounds ?? 20,
        skills: agent.skills,
      };

      const userMessage = context
        ? `${taskDescription}\n\n<context>\n${context}\n</context>`
        : taskDescription;

      const timeoutMs = ctx.taskTimeout * 1000;
      const agentPromise = runAgent({
        agentConfig,
        tools: resolvedTools.tools,
        toolNames: resolvedTools.toolNames,
        userMessage,
        history: [],
        onToolCall: name => {
          ctx.callbacks.onLog(ctx.runId, taskId, member.name, 'debug', `调用工具: ${name}`);
        },
        onToolResult: name => {
          ctx.callbacks.onLog(ctx.runId, taskId, member.name, 'debug', `工具返回: ${name}`);
        },
      });

      const timeoutPromise = new Promise<never>((_, reject) => {
        setTimeout(() => reject(new Error(`子 Agent "${member.name}" 执行超时 (${ctx.taskTimeout}s)`)), timeoutMs);
      });

      const attemptResult = await Promise.race([agentPromise, timeoutPromise]);
      const duration = `${((Date.now() - startTime) / 1000).toFixed(1)}s`;

      await this.crewTaskRepo.update(taskId, {
        status: 'completed',
        endTime: new Date(),
        result: { finalContent: attemptResult.finalContent, toolCallCount: attemptResult.toolCallCount } as any,
        tokenUsage: attemptResult.usage,
      });

      ctx.callbacks.onTaskStatus(ctx.runId, taskId, member.name, 'completed');
      ctx.callbacks.onLog(ctx.runId, taskId, member.name, 'info', `执行完成 (${duration})`);

      return {
        agent: member.name,
        status: 'completed',
        result: attemptResult.finalContent,
        duration,
        tokenUsage: attemptResult.usage,
      };
    } catch (err: any) {
      const duration = `${((Date.now() - startTime) / 1000).toFixed(1)}s`;
      const errorMsg = err.message || String(err);

      await this.crewTaskRepo.update(taskId, {
        status: 'failed',
        endTime: new Date(),
        error: errorMsg,
      });

      ctx.callbacks.onTaskStatus(ctx.runId, taskId, member.name, 'failed', undefined, errorMsg);
      ctx.callbacks.onLog(ctx.runId, taskId, member.name, 'error', `执行失败: ${errorMsg}`);

      return {
        agent: member.name,
        status: 'failed',
        result: '',
        error: errorMsg,
        duration,
        tokenUsage: { inputTokens: 0, outputTokens: 0 },
      };
    }
  }

  /**
   * 并行执行多个子 Agent 任务，受 maxConcurrent 限制分批执行。
   */
  async executeParallel(
    ctx: CrewRunContext,
    tasks: ParallelTaskItem[],
  ): Promise<DelegateResult[]> {
    const results: DelegateResult[] = [];
    const batchSize = ctx.maxConcurrent;

    for (let i = 0; i < tasks.length; i += batchSize) {
      const batch = tasks.slice(i, i + batchSize);
      const batchResults = await Promise.all(
        batch.map(t => {
          const member = ctx.memberAgents.find(m => m.name === t.agent_name)!;
          return this.executeSubAgent(ctx, member, t.task_description, t.context);
        }),
      );
      results.push(...batchResults);
    }

    return results;
  }
}
