import { Provide, Inject, Logger } from '@midwayjs/core';
import { ILogger } from '@midwayjs/logger';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { NetaClawCrewEntity } from '../entity/crew.js';
import { NetaClawCrewAgentEntity } from '../entity/crew_agent.js';
import { NetaClawCrewRunEntity } from '../entity/crew_run.js';
import { NetaClawAgentEntity } from '../entity/agent.js';
import { runAgent, AgentConfig } from '../runtime/agent.js';
import { initDefaultProviders } from '../runtime/model_selection.js';
import { CrewDelegateService } from './crew_delegate.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, CrewCallbacks } from './crew_types.js';

/**
 * Crew 核心编排器，负责启动、运行、暂停、恢复集群。
 */
@Provide()
export class CrewOrchestratorService {
  @Logger()
  logger: ILogger;

  @InjectEntityModel(NetaClawCrewEntity)
  crewRepo: Repository<NetaClawCrewEntity>;

  @InjectEntityModel(NetaClawCrewAgentEntity)
  crewAgentRepo: Repository<NetaClawCrewAgentEntity>;

  @InjectEntityModel(NetaClawCrewRunEntity)
  crewRunRepo: Repository<NetaClawCrewRunEntity>;

  @InjectEntityModel(NetaClawAgentEntity)
  agentRepo: Repository<NetaClawAgentEntity>;

  @Inject()
  delegateService: CrewDelegateService;

  @Inject()
  skillLoader: SkillLoaderService;

  @Inject()
  modelChannelService: NetaClawModelChannelService;

  @Inject()
  toolResolver: NetaClawToolResolverService;

  async start(
    crewId: number,
    triggerType: string,
    triggerInput: string,
    callbacks: CrewCallbacks,
  ): Promise<number> {
    const crew = await this.crewRepo.findOneBy({ id: crewId });
    if (!crew) throw new Error(`集群 ${crewId} 不存在`);
    if (crew.status !== 1) throw new Error(`集群 "${crew.label}" 未发布，无法运行`);

    const masterAgent = await this.agentRepo.findOneBy({ id: crew.masterAgentId });
    if (!masterAgent) throw new Error(`主 Agent (id=${crew.masterAgentId}) 不存在`);

    const crewAgents = await this.crewAgentRepo.find({ where: { crewId } });
    const memberAgents: CrewRunContext['memberAgents'] = [];
    for (const ca of crewAgents) {
      if (ca.agentId === crew.masterAgentId) continue;
      const agent = await this.agentRepo.findOneBy({ id: ca.agentId });
      if (agent) {
        memberAgents.push({
          id: agent.id,
          name: agent.name,
          label: agent.label,
          role: ca.role || '',
          agent,
        });
      }
    }

    const run = this.crewRunRepo.create({
      crewId,
      triggerType,
      triggerInput,
      status: 'running',
      startTime: new Date(),
      tokenUsage: { inputTokens: 0, outputTokens: 0 },
    });
    const saved = await this.crewRunRepo.save(run);
    const runId = saved.id;

    const ctx: CrewRunContext = {
      runId,
      crewId,
      masterAgentId: crew.masterAgentId,
      memberAgents,
      maxConcurrent: crew.maxConcurrent || 3,
      taskTimeout: crew.taskTimeout || 300,
      callbacks,
    };

    this.runOrchestration(crew, masterAgent, ctx, triggerInput, callbacks).catch(err => {
      this.logger.error(`[Crew] 编排异常 runId=${runId}: ${err.message}`);
    });

    callbacks.onRunStatus(runId, 'running', '编排已启动');
    return runId;
  }

  private async resolveModelConfig(agent: NetaClawAgentEntity): Promise<{
    model: string;
    apiKey: string;
    baseUrl?: string;
    capability: ModelCapability;
  }> {
    const mc = (agent.modelConfig || {}) as any;
    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',
    };
  }

  private async runOrchestration(
    crew: NetaClawCrewEntity,
    masterAgent: NetaClawAgentEntity,
    ctx: CrewRunContext,
    triggerInput: string,
    callbacks: CrewCallbacks,
  ): Promise<void> {
    try {
      initDefaultProviders();
      (ctx as any)._delegate = this.delegateService;
      (ctx as any).crewRunRepo = this.crewRunRepo;

      const modelInfo = await this.resolveModelConfig(masterAgent);
      const pureModelId = modelInfo.model.includes(':')
        ? modelInfo.model.split(':')[1]
        : modelInfo.model;
      const crewContext = this.buildCrewContext(masterAgent, ctx, crew);
      const resolvedTools = await this.toolResolver.resolve({
        agent: masterAgent,
        modelCapability: modelInfo.capability,
        hasSkills: !!masterAgent.skills?.length,
        crewRole: 'master',
        crewContext: ctx as any,
      });
      const { prompt: systemPrompt } = buildSystemPrompt({
        agentSystemPrompt: masterAgent.systemPrompt || '',
        modelId: pureModelId,
        availableToolNames: resolvedTools.toolNames,
        skills: masterAgent.skills || [],
        skillLoader: this.skillLoader,
        memoryEnabled: false,
        crewContext,
        toolPromptHints: resolvedTools.toolPromptHints,
      });

      const agentConfig: AgentConfig = {
        name: masterAgent.name,
        systemPrompt,
        model: modelInfo.model,
        apiKey: modelInfo.apiKey,
        baseUrl: modelInfo.baseUrl,
        temperature: (masterAgent.config as any)?.temperature,
        maxTokens: (masterAgent.config as any)?.maxTokens,
        maxToolRounds: 50,
        skills: masterAgent.skills,
      };

      let conversationRef: any[] = [];
      ctx.getConversation = () => conversationRef;

      const result = await runAgent({
        agentConfig,
        tools: resolvedTools.tools,
        toolNames: resolvedTools.toolNames,
        userMessage: triggerInput,
        history: [],
        onToolCall: name => {
          callbacks.onLog(ctx.runId, 0, masterAgent.name, 'info', `主 Agent 调用工具: ${name}`);
        },
        onToolResult: name => {
          callbacks.onLog(ctx.runId, 0, masterAgent.name, 'debug', `工具返回: ${name}`);
        },
      });

      conversationRef = result.messages;

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

      callbacks.onRunStatus(ctx.runId, 'completed', '编排完成');
    } catch (err: any) {
      this.logger.error(`[Crew] runOrchestration 失败 runId=${ctx.runId}: ${err.message}`);
      await this.crewRunRepo.update(ctx.runId, {
        status: 'failed',
        endTime: new Date(),
        error: err.message,
      });
      callbacks.onRunStatus(ctx.runId, 'failed', err.message);
    }
  }

  private buildCrewContext(
    masterAgent: NetaClawAgentEntity,
    ctx: CrewRunContext,
    crew: NetaClawCrewEntity,
  ): string {
    const crewParts: string[] = [];

    const memberList = ctx.memberAgents
      .map(m => {
        const skills = m.agent?.skills?.length ? `，已具备技能: ${m.agent.skills.join('、')}` : '';
        const desc = m.agent?.description ? `，简介: ${m.agent.description}` : '';
        return `- ${m.name} (${m.label}): ${m.role || '无角色描述'}${desc}${skills}`;
      })
      .join('\n');
    crewParts.push(`## 你的团队成员\n${memberList}\n\n注意：委派任务时无需让成员安装工具或环境，他们已具备所列技能，直接描述要完成的目标即可。`);

    if ((crew as any).delegateHints?.hints) {
      crewParts.push(`## 调度建议\n${(crew as any).delegateHints.hints}`);
    }

    crewParts.push(`## 编排工具说明
- delegate_task: 将任务委派给指定子 Agent 串行执行
- delegate_parallel: 同时委派多个任务给不同子 Agent 并行执行
- escalate: 遇到无法解决的问题时升级给人工处理
请根据任务需求合理分配工作给团队成员，充分利用并行执行提高效率。`);

    return crewParts.join('\n\n');
  }

  async stop(runId: number): Promise<void> {
    await this.crewRunRepo.update(runId, {
      status: 'stopped',
      endTime: new Date(),
    });
  }

  async pause(runId: number): Promise<void> {
    await this.crewRunRepo.update(runId, {
      status: 'paused',
    });
  }

  async resumeFromPausedState(
    runId: number,
    pausedHistory: any[],
    userMessage: string,
    callbacks: CrewCallbacks,
  ): Promise<void> {
    const run = await this.crewRunRepo.findOneBy({ id: runId });
    if (!run) throw new Error(`运行记录 ${runId} 不存在`);
    if (run.status !== 'paused') throw new Error(`运行记录 ${runId} 状态不是 paused`);

    await this.crewRunRepo.update(runId, { status: 'running' });
    callbacks.onRunStatus(runId, 'running', '从暂停恢复');

    const crew = await this.crewRepo.findOneBy({ id: run.crewId });
    if (!crew) throw new Error(`集群 ${run.crewId} 不存在`);

    const masterAgent = await this.agentRepo.findOneBy({ id: crew.masterAgentId });
    if (!masterAgent) throw new Error('主 Agent 不存在');

    const crewAgents = await this.crewAgentRepo.find({ where: { crewId: crew.id } });
    const memberAgents: CrewRunContext['memberAgents'] = [];
    for (const ca of crewAgents) {
      const agent = await this.agentRepo.findOneBy({ id: ca.agentId });
      if (agent) {
        memberAgents.push({
          id: agent.id,
          name: agent.name,
          label: agent.label,
          role: ca.role || '',
          agent,
        });
      }
    }

    const ctx: CrewRunContext = {
      runId,
      crewId: crew.id,
      masterAgentId: crew.masterAgentId,
      memberAgents,
      maxConcurrent: crew.maxConcurrent || 3,
      taskTimeout: crew.taskTimeout || 300,
      callbacks,
    };

    this.runOrchestration(crew, masterAgent, ctx, userMessage, callbacks).catch(err => {
      this.logger.error(`[Crew] 恢复执行异常 runId=${runId}: ${err.message}`);
    });
  }
}
