import {
  Body,
  Controller,
  Delete,
  ForbiddenException,
  Get,
  HttpCode,
  HttpStatus,
  Inject,
  NotFoundException,
  Param,
  Patch,
  Post,
  UseGuards,
} from '@nestjs/common';
import type { Brain } from '@mosaicstack/brain';
import { BRAIN } from '../brain/brain.tokens.js';
import { AuthGuard } from '../auth/auth.guard.js';
import { CurrentUser } from '../auth/current-user.decorator.js';
import { TeamsService } from '../workspace/teams.service.js';
import { CreateProjectDto, UpdateProjectDto } from './projects.dto.js';

@Controller('api/projects')
@UseGuards(AuthGuard)
export class ProjectsController {
  constructor(
    @Inject(BRAIN) private readonly brain: Brain,
    private readonly teamsService: TeamsService,
  ) {}

  @Get()
  async list(@CurrentUser() user: { id: string }) {
    return this.brain.projects.findAllForUser(user.id);
  }

  @Get(':id')
  async findOne(@Param('id') id: string, @CurrentUser() user: { id: string }) {
    return this.getAccessibleProject(id, user.id);
  }

  @Post()
  async create(@CurrentUser() user: { id: string }, @Body() dto: CreateProjectDto) {
    return this.brain.projects.create({
      name: dto.name,
      description: dto.description,
      status: dto.status,
      ownerId: user.id,
    });
  }

  @Patch(':id')
  async update(
    @Param('id') id: string,
    @Body() dto: UpdateProjectDto,
    @CurrentUser() user: { id: string },
  ) {
    await this.getAccessibleProject(id, user.id);
    const project = await this.brain.projects.update(id, dto);
    if (!project) throw new NotFoundException('Project not found');
    return project;
  }

  @Delete(':id')
  @HttpCode(HttpStatus.NO_CONTENT)
  async remove(@Param('id') id: string, @CurrentUser() user: { id: string }) {
    await this.getAccessibleProject(id, user.id);
    const deleted = await this.brain.projects.remove(id);
    if (!deleted) throw new NotFoundException('Project not found');
  }

  /**
   * Verify the requesting user can access the project — either as the direct
   * owner or as a member of the owning team.  Throws NotFoundException when the
   * project does not exist and ForbiddenException when the user lacks access.
   */
  private async getAccessibleProject(id: string, userId: string) {
    const project = await this.brain.projects.findById(id);
    if (!project) throw new NotFoundException('Project not found');
    const canAccess = await this.teamsService.canAccessProject(userId, id);
    if (!canAccess) throw new ForbiddenException('Project does not belong to the current user');
    return project;
  }
}
