import { Injectable } from '@nestjs/common';
import { PrismaService } from '@core/database/prisma/prisma.service';
import type { AgentTool, ToolDescriptor, ToolInvocation, ToolResult } from './tool.types';
import type { AgentPlanMode, AgentPermissionMode } from '@prisma/client';

/**
 * PR4.5 Plan/Permission mode 控制工具。
 *
 * 三个：EnterPlanMode / ExitPlanMode / SetPermissionMode。
 * 控制工具不受 mode 过滤——否则用户没法切回去（典型死锁）。
 */

@Injectable()
export class EnterPlanModeTool implements AgentTool {
  constructor(private readonly prisma: PrismaService) {}

  readonly descriptor: ToolDescriptor = {
    name: 'EnterPlanMode',
    description: '进入 Plan mode —— agent 后续必须先输出执行计划待用户批准才能跑写动作。',
    inputSchema: {
      sessionId: { type: 'string', required: true, description: '当前 session UUID' },
    },
    availability: { surface: ['web', 'desktop', 'mobile', 'teams', 'cli'] },
    controlTool: true,
  };

  async invoke(inv: ToolInvocation): Promise<ToolResult> {
    const sessionId = String(inv.input.sessionId ?? inv.sessionId ?? '');
    if (!sessionId) return { ok: false, errorMessage: 'sessionId required' };
    return updateSessionMode(this.prisma, sessionId, inv.organizationId, { planMode: 'REQUIRED' });
  }
}

@Injectable()
export class ExitPlanModeTool implements AgentTool {
  constructor(private readonly prisma: PrismaService) {}

  readonly descriptor: ToolDescriptor = {
    name: 'ExitPlanMode',
    description: 'Plan 已被用户批准 —— 退出 Plan mode，agent 可以执行写动作。',
    inputSchema: {
      sessionId: { type: 'string', required: true, description: '当前 session UUID' },
    },
    availability: { surface: ['web', 'desktop', 'mobile', 'teams', 'cli'] },
    controlTool: true,
  };

  async invoke(inv: ToolInvocation): Promise<ToolResult> {
    const sessionId = String(inv.input.sessionId ?? inv.sessionId ?? '');
    if (!sessionId) return { ok: false, errorMessage: 'sessionId required' };
    return updateSessionMode(this.prisma, sessionId, inv.organizationId, { planMode: 'OFF' });
  }
}

@Injectable()
export class SetPermissionModeTool implements AgentTool {
  constructor(private readonly prisma: PrismaService) {}

  readonly descriptor: ToolDescriptor = {
    name: 'SetPermissionMode',
    description: '切换 Permission mode：DEFAULT / BYPASS / READ_ONLY / ASK_EVERY_TIME。',
    inputSchema: {
      sessionId: { type: 'string', required: true, description: '当前 session UUID' },
      mode: {
        type: 'string',
        required: true,
        description: 'DEFAULT / BYPASS / READ_ONLY / ASK_EVERY_TIME',
      },
    },
    availability: { surface: ['web', 'desktop', 'mobile', 'teams', 'cli'] },
    controlTool: true,
  };

  async invoke(inv: ToolInvocation): Promise<ToolResult> {
    const sessionId = String(inv.input.sessionId ?? inv.sessionId ?? '');
    const mode = String(inv.input.mode ?? '').toUpperCase() as AgentPermissionMode;
    const allowed: AgentPermissionMode[] = ['DEFAULT', 'BYPASS', 'READ_ONLY', 'ASK_EVERY_TIME'];
    if (!sessionId) return { ok: false, errorMessage: 'sessionId required' };
    if (!allowed.includes(mode)) {
      return { ok: false, errorMessage: `invalid mode '${mode}'; allowed: ${allowed.join(', ')}` };
    }
    // PR4.5 admin 收紧：检查 OrganizationAgentSettings.allowedPermissionModes
    const orgSettings = await this.prisma.organizationAgentSettings.findUnique({
      where: { organizationId: inv.organizationId },
    });
    if (orgSettings?.allowedPermissionModes?.length) {
      if (!orgSettings.allowedPermissionModes.includes(mode)) {
        return {
          ok: false,
          errorMessage: `mode '${mode}' not allowed by organization policy`,
        };
      }
    }
    return updateSessionMode(this.prisma, sessionId, inv.organizationId, {
      permissionMode: mode,
    });
  }
}

async function updateSessionMode(
  prisma: PrismaService,
  sessionId: string,
  organizationId: string,
  patch: { planMode?: AgentPlanMode; permissionMode?: AgentPermissionMode },
): Promise<ToolResult> {
  const existing = await prisma.agentSession.findUnique({
    where: { id: sessionId },
    select: { organizationId: true },
  });
  if (!existing) return { ok: false, errorMessage: 'session not found' };
  if (existing.organizationId !== organizationId) {
    return { ok: false, errorMessage: 'cross-organization access denied' };
  }
  const updated = await prisma.agentSession.update({
    where: { id: sessionId },
    data: patch,
    select: { id: true, planMode: true, permissionMode: true },
  });
  return { ok: true, output: updated };
}
