import {
  Body,
  Controller,
  Delete,
  ForbiddenException,
  Get,
  Param,
  Patch,
  Post,
  Query,
  Request,
} from '@nestjs/common';
import type { Request as ExpressRequest } from 'express';
import type { MemoryScope, MemoryCategory } from '@prisma/client';
import { AgentMemoriesService } from '../services/memories.service';
import { resolveActor, getActor } from '../utils/auth-resolution.util';

@Controller('agent/memories')
export class AgentMemoriesController {
  constructor(private readonly memoriesService: AgentMemoriesService) {}

  @Get()
  async list(
    @Query() query: { scope?: MemoryScope; category?: MemoryCategory },
    @Request() req: ExpressRequest,
  ) {
    const { orgId, userId } = resolveActor(req);
    const items = await this.memoriesService.list(orgId, userId, query.scope, query.category);
    return { items };
  }

  @Post()
  async create(
    @Body()
    body: {
      content: string;
      scope?: MemoryScope;
      category?: MemoryCategory;
      projectId?: string;
      personaId?: string;
      source?: string;
    },
    @Request() req: ExpressRequest,
  ) {
    const { orgId, userId } = resolveActor(req);
    return this.memoriesService.create({
      organizationId: orgId,
      createdById: userId,
      ...body,
    });
  }

  @Patch(':id')
  async update(
    @Param('id') id: string,
    @Body() body: { content?: string; category?: MemoryCategory },
    @Request() req: ExpressRequest,
  ) {
    const { orgId, userId } = resolveActor(req);
    return this.memoriesService.update(id, orgId, userId, body);
  }

  @Delete(':id')
  async remove(@Param('id') id: string, @Request() req: ExpressRequest) {
    const { orgId, userId } = resolveActor(req);
    return this.memoriesService.remove(id, orgId, userId);
  }
}

/**
 * Admin-only routes for per-org shared memory.
 * 权限：要求用户在该 org 有 'agent.admin' 角色（resolveActor 已校验 org 归属，
 * 此处再校验角色，控制器层守门，service 层不放宽 ownerScope=ORG 的非 admin 写）。
 */
@Controller('agent/admin/memories')
export class AgentAdminMemoriesController {
  constructor(private readonly memoriesService: AgentMemoriesService) {}

  @Get()
  async listOrg(
    @Query() query: { scope?: MemoryScope; category?: MemoryCategory },
    @Request() req: ExpressRequest,
  ) {
    const { orgId } = resolveActor(req);
    assertAdminRoleInOrg(req, orgId);
    const items = await this.memoriesService.listOrgMemories(orgId, query.scope, query.category);
    return { items };
  }

  @Post()
  async createOrg(
    @Body()
    body: {
      content: string;
      scope?: MemoryScope;
      category?: MemoryCategory;
      projectId?: string;
      personaId?: string;
      source?: string;
    },
    @Request() req: ExpressRequest,
  ) {
    const { orgId } = resolveActor(req);
    assertAdminRoleInOrg(req, orgId);
    return this.memoriesService.createOrgMemory({
      organizationId: orgId,
      ...body,
    });
  }

  @Delete(':id')
  async removeOrg(@Param('id') id: string, @Request() req: ExpressRequest) {
    const { orgId } = resolveActor(req);
    assertAdminRoleInOrg(req, orgId);
    return this.memoriesService.removeOrgMemory(id, orgId);
  }
}

/**
 * Admin 角色判定，三层来源择一通过：
 *   ① 顶层 `roles`（系统级 + 当前 org 合集；itadmin 的全局 Administrator 在这里）
 *   ② `organizationRoles[orgId]`（仅该 org 内授予的角色）
 *   ③ `permissions` 含 'agent.admin'（细粒度授权）
 * 接受任一 'Administrator' / 'ITAdmin' / 'agent.admin' 角色码（不同部署命名不一）。
 *
 * TODO(#432): 替换为项目标准的 `@RequirePermissions('agent:admin')` 声明式守卫
 * （跟踪 issue #432 / 关联 .learnings/ERRORS/ERR-20260518-005）。本 PR 因主题单一
 * 不在范围内修，AI review 评审已 ack 为 follow-up。
 */
function assertAdminRoleInOrg(req: ExpressRequest, orgId: string): void {
  const actor = getActor(req);
  const topRoles = (actor as { roles?: string[] } | undefined)?.roles ?? [];
  const rolesInOrg = actor?.organizationRoles?.[orgId] ?? [];
  const permissions = actor?.permissions ?? [];
  const allowed = ['Administrator', 'ITAdmin', 'agent.admin'];
  const ok =
    topRoles.some((r) => allowed.includes(r)) ||
    rolesInOrg.some((r) => allowed.includes(r)) ||
    permissions.includes('agent.admin');
  if (!ok) throw new ForbiddenException('admin role required for org-shared memory');
}
