import {
  BadRequestException,
  Body,
  Controller,
  ForbiddenException,
  Get,
  Param,
  Post,
  Query,
  Request,
} from '@nestjs/common';
import type { Request as ExpressRequest } from 'express';
import { PrismaService } from '@core/database/prisma/prisma.service';
import { ToolRegistry } from '../tools/tool-registry.service';
import { resolveActor, getActor } from '../utils/auth-resolution.util';
import { normalizeSurfaceLabel, type AgentSurfaceLabel } from '../router/routing.types';

type Surface = AgentSurfaceLabel;

/**
 * PR5 Tool 入口 + PR4.5 mode 过滤。
 *
 *   GET  /api/v1/agent/tools?surface=web&sessionId=xxx
 *   POST /api/v1/agent/tools/:name
 */
@Controller('agent/tools')
export class AgentToolsController {
  constructor(
    private readonly toolRegistry: ToolRegistry,
    private readonly prisma: PrismaService,
  ) {}

  @Get()
  async list(
    @Query() query: { surface?: string; sessionId?: string },
    @Request() req: ExpressRequest,
  ) {
    const actor = getActor(req);
    const session = query.sessionId
      ? await this.prisma.agentSession.findUnique({
          where: { id: query.sessionId },
          select: { planMode: true, permissionMode: true },
        })
      : null;
    const items = this.toolRegistry.list({
      surface: normalizeSurfaceLabel(query.surface) ?? 'web',
      permissions: actor?.permissions ?? [],
      planMode: session?.planMode,
      permissionMode: session?.permissionMode,
    });
    return { items };
  }

  @Post(':name')
  async invoke(
    @Param('name') name: string,
    @Body()
    body: {
      input?: Record<string, unknown>;
      sessionId?: string;
      turnId?: string;
      surface?: string;
    },
    @Request() req: ExpressRequest,
  ) {
    const { actor, orgId, userId } = resolveActor(req);
    const surface: Surface = normalizeSurfaceLabel(body.surface) ?? 'web';

    const session = body.sessionId
      ? await this.prisma.agentSession.findUnique({
          where: { id: body.sessionId },
          select: { planMode: true, permissionMode: true, organizationId: true },
        })
      : null;
    if (session && session.organizationId !== orgId) {
      throw new ForbiddenException('cross-organization access denied');
    }

    this.toolRegistry.assertAvailable(name, {
      surface,
      permissions: actor.permissions ?? [],
      planMode: session?.planMode,
      permissionMode: session?.permissionMode,
    });

    return this.toolRegistry.invoke(name, {
      organizationId: orgId,
      userId,
      sessionId: body.sessionId,
      turnId: body.turnId,
      input: { ...body.input, ...(body.sessionId ? { sessionId: body.sessionId } : {}) },
    });
  }
}
