import {
  Body,
  Controller,
  ForbiddenException,
  Get,
  HttpCode,
  HttpStatus,
  NotFoundException,
  Param,
  Patch,
  Req,
  UseGuards,
} from '@nestjs/common';
import { ApiBearerAuth, ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { ApiProperty } from '@nestjs/swagger';
import { IsInt, Max, Min } from 'class-validator';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import type { RequestAutenticado } from '../auth/jwt-auth.guard';
import { PrismaService } from '../prisma/prisma.service';
import { CorrecaoService } from './correcao.service';

export class CorrigirPlacarDto {
  @ApiProperty({ minimum: 0, maximum: 20 })
  @IsInt()
  @Min(0)
  @Max(20)
  homeScore!: number;

  @ApiProperty({ minimum: 0, maximum: 20 })
  @IsInt()
  @Min(0)
  @Max(20)
  awayScore!: number;
}

@ApiTags('Correção de placar')
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Controller('pools/:poolId')
export class CorrecaoController {
  constructor(
    private readonly correcao: CorrecaoService,
    private readonly prisma: PrismaService,
  ) {}

  /** Correção manual pelo admin. Fica visível a todos como intervenção humana. */
  @Patch('matches/:matchId/score')
  corrigir(
    @Param('poolId') poolId: string,
    @Param('matchId') matchId: string,
    @Body() dto: CorrigirPlacarDto,
    @Req() req: RequestAutenticado,
  ) {
    return this.correcao.corrigirManualmente(
      poolId,
      matchId,
      req.conta.id,
      dto.homeScore,
      dto.awayScore,
    );
  }

  /**
   * Histórico de alterações do placar, visível a **qualquer** participante.
   * É a única mitigação prevista para o poder de correção manual do admin.
   */
  @Get('matches/:matchId/corrections')
  @ApiOkResponse({
    description: 'quem alterou, quando, e de que valor para qual',
  })
  async historico(
    @Param('poolId') poolId: string,
    @Param('matchId') matchId: string,
    @Req() req: RequestAutenticado,
  ) {
    // A rota é escopada em `:poolId` e não checava nada: qualquer conta
    // autenticada lia o histórico de qualquer jogo, incluindo o nome do admin
    // que corrigiu em bolão alheio. Mesma classe do vazamento dos avisos.
    const membro = await this.prisma.poolMember.findUnique({
      where: { poolId_userId: { poolId, userId: req.conta.id } },
      select: { leftAt: true },
    });
    if (!membro) {
      throw new ForbiddenException('você não participa deste bolão');
    }

    const noBolao = await this.prisma.poolMatch.count({
      where: { poolId, matchId },
    });
    if (noBolao === 0) {
      throw new NotFoundException('este jogo não pertence ao bolão');
    }

    return this.prisma.scoreCorrection.findMany({
      where: { matchId },
      orderBy: { createdAt: 'desc' },
      select: {
        previousHome: true,
        previousAway: true,
        newHome: true,
        newAway: true,
        source: true,
        createdAt: true,
        correctedBy: { select: { displayName: true } },
      },
    });
  }

  /**
   * Avisos do participante **naquele bolão** — funcionam sem push (PR-7 FE-2).
   *
   * O filtro por `poolId` estava faltando: a rota é escopada em `:poolId` mas
   * devolvia os avisos de todos os bolões, então quem joga em dois via aviso do
   * outro e ia procurar no ranking errado.
   */
  @Get('notifications')
  async avisos(
    @Param('poolId') poolId: string,
    @Req() req: RequestAutenticado,
  ) {
    return this.prisma.notification.findMany({
      where: { userId: req.conta.id, poolId },
      orderBy: { createdAt: 'desc' },
      take: 50,
      select: {
        id: true,
        kind: true,
        title: true,
        body: true,
        createdAt: true,
        readAt: true,
      },
    });
  }

  /** Marca um aviso como lido. Sem isto o contador não zera nunca. */
  @Patch('notifications/:id/read')
  @HttpCode(HttpStatus.NO_CONTENT)
  async marcarLido(
    @Param('poolId') poolId: string,
    @Param('id') id: string,
    @Req() req: RequestAutenticado,
  ): Promise<void> {
    // `updateMany` com o dono no filtro: `update` por id deixaria um
    // participante marcar o aviso de outro como lido.
    await this.prisma.notification.updateMany({
      where: { id, userId: req.conta.id, poolId, readAt: null },
      data: { readAt: new Date() },
    });
  }
}
