import { Injectable, Inject, forwardRef } from '@nestjs/common';
import { AgentSessionsService } from '../services/sessions.service';
import { AgentMessagesService } from '../services/messages.service';
import { TaskTrackerService } from '../subagent/task-tracker.service';
import { QuotaService } from '../quota/quota.service';
import type { AgentTool, ToolDescriptor, ToolInvocation, ToolResult } from './tool.types';

/**
 * Sub-agent 启动前 quota 预检阈值（tokens）。父配额剩余低于此值则拒绝 delegation，
 * 避免子任务把父 quota 耗光导致父对话续 turn 时硬上限拦截。
 * env: FFAI_SUBAGENT_MIN_TOKENS（默认 2000）。
 */
const SUBAGENT_MIN_REMAINING_TOKENS = Number(process.env.FFAI_SUBAGENT_MIN_TOKENS ?? 2000);

/**
 * PR8 `delegate_task` tool —— sub-agent 同进程 fork。
 *
 * 行为：
 *   1. 在 TaskTracker 写一条 PENDING 任务
 *   2. 创建 child session（带 title=task）
 *   3. 在 child session 跑一个 runTurn（独立上下文，独立 routing 决策，独立 trajectory）
 *   4. 任务状态更新成 COMPLETED + metadata 写 child session id 和 result
 *   5. 返回 child agent 的最终回复给父 agent
 *
 * 父 agent 拿到结果后整合到自己的回答里。
 */
@Injectable()
export class DelegateTaskTool implements AgentTool {
  constructor(
    private readonly sessionsService: AgentSessionsService,
    @Inject(forwardRef(() => AgentMessagesService))
    private readonly messagesService: AgentMessagesService,
    private readonly taskTracker: TaskTrackerService,
    private readonly quota: QuotaService,
  ) {}

  readonly descriptor: ToolDescriptor = {
    name: 'delegate_task',
    description:
      '派一个 sub-agent 在独立上下文里跑一个子任务，返回它的最终回答。' +
      ' 适合需要长上下文 / 不想污染主对话 / 需要独立审计轨迹的任务。',
    inputSchema: {
      title: { type: 'string', required: true, description: '子任务标题（5-50 字）' },
      prompt: {
        type: 'string',
        required: true,
        description: '子任务完整描述 —— sub-agent 会基于这个开一个全新 session 解决',
      },
    },
    availability: { surface: ['web', 'desktop', 'mobile', 'teams', 'cli'] },
  };

  async invoke(inv: ToolInvocation): Promise<ToolResult> {
    const title = String(inv.input.title ?? '').trim();
    const prompt = String(inv.input.prompt ?? '').trim();
    if (!title || !prompt) {
      return { ok: false, errorMessage: 'title 和 prompt 必填' };
    }
    if (!inv.sessionId) {
      return { ok: false, errorMessage: '父 sessionId 缺失（delegate_task 必须在 session 上下文中调用）' };
    }

    // sub-agent quota 隔离：父配额剩余低于阈值时拒绝 delegation
    // 避免子任务把 token 全部耗光、父 session 后续 turn 被硬上限拦截
    const quotaCheck = await this.quota.assertAllowed({
      organizationId: inv.organizationId,
      userId: inv.userId,
    });
    if (quotaCheck.remainingTokens < SUBAGENT_MIN_REMAINING_TOKENS) {
      return {
        ok: false,
        errorMessage:
          `quota 剩余 ${quotaCheck.remainingTokens} tokens 不足 sub-agent 阈值 ` +
          `${SUBAGENT_MIN_REMAINING_TOKENS}（FFAI_SUBAGENT_MIN_TOKENS）；先在父 session 收尾或调高 quota`,
      };
    }

    const task = await this.taskTracker.create({
      organizationId: inv.organizationId,
      sessionId: inv.sessionId,
      title,
      description: prompt,
      createdById: inv.userId,
      metadata: { kind: 'sub_agent' },
    });

    try {
      await this.taskTracker.updateStatus(task.id, inv.organizationId, { status: 'IN_PROGRESS' });

      const childSession = await this.sessionsService.createSession({
        organizationId: inv.organizationId,
        createdById: inv.userId,
        title: `[sub] ${title}`,
        parentSessionId: inv.sessionId, // 显式拓扑：child 知道父
      });

      const result = await this.messagesService.runTurn({
        sessionId: childSession.id,
        organizationId: inv.organizationId,
        userId: inv.userId,
        prompt,
      });

      const assistantMsg = result.messages.find((m) => m.type === 'ASSISTANT_TEXT');
      const reply = assistantMsg?.content ?? '(sub-agent 没产出文本回复)';

      await this.taskTracker.updateStatus(task.id, inv.organizationId, {
        status: 'COMPLETED',
        progress: 100,
        metadata: {
          kind: 'sub_agent',
          childSessionId: childSession.id,
          childTurnId: result.turnId,
        },
      });

      return {
        ok: true,
        output: {
          taskId: task.id,
          childSessionId: childSession.id,
          childTurnId: result.turnId,
          reply,
        },
      };
    } catch (err) {
      await this.taskTracker
        .updateStatus(task.id, inv.organizationId, {
          status: 'FAILED',
          metadata: { error: (err as Error).message },
        })
        .catch(() => undefined);
      return { ok: false, errorMessage: (err as Error).message };
    }
  }
}
