import {
  Body,
  Controller,
  Get,
  Param,
  Patch,
  Post,
  Query,
  Res,
} from '@nestjs/common';
import { Response } from 'express';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import {
  ArrayMinSize,
  IsArray,
  IsDateString,
  IsEnum,
  IsNumber,
  IsOptional,
  IsString,
  IsUUID,
  ValidateNested,
} from 'class-validator';
import { Type } from 'class-transformer';
import { AssemblyType, VoteChoice } from '@prisma/client';
import { AssembliesService } from './assemblies.service';
import { Roles } from '../../common/decorators/roles.decorator';
import { CurrentUser, AuthenticatedUser } from '../../common/decorators/current-user.decorator';

class AgendaItemDto {
  @IsString() title!: string;
  @IsOptional() @IsString() description?: string;
  @IsOptional() requiresVote?: boolean;
  @IsOptional() @IsString() resolutionText?: string;
  @IsOptional() @IsString() majority?: string; // SIMPLE|ABSOLUTE|DOUBLE
}

class CreateAssemblyDto {
  @IsUUID() complexId!: string;
  @IsOptional() @IsEnum(AssemblyType) type?: AssemblyType;
  @IsString() title!: string;
  @IsDateString() scheduledAt!: string;
  @IsOptional() @IsString() location?: string;
  @IsOptional() @IsString() onlineUrl?: string;
  @IsOptional() @IsNumber() quorumPct?: number;
  @IsArray() @ValidateNested({ each: true }) @Type(() => AgendaItemDto) @ArrayMinSize(1)
  agendaItems!: AgendaItemDto[];
}

class VoteDto {
  @IsEnum(VoteChoice) choice!: VoteChoice;
}

class GiveProxyDto {
  @IsUUID() receiverId!: string;
}

@ApiTags('assemblies')
@ApiBearerAuth()
@Controller('assemblies')
export class AssembliesController {
  constructor(private readonly service: AssembliesService) {}

  @Post()
  @Roles('SUPERADMIN', 'SYNDIC')
  create(@CurrentUser() user: AuthenticatedUser, @Body() dto: CreateAssemblyDto) {
    return this.service.create(user, { ...dto, scheduledAt: new Date(dto.scheduledAt) });
  }

  @Get()
  list(@CurrentUser() user: AuthenticatedUser, @Query('complexId') complexId?: string) {
    return this.service.list(user, complexId);
  }

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

  @Post(':id/convocations')
  @Roles('SUPERADMIN', 'SYNDIC')
  convocate(@CurrentUser() user: AuthenticatedUser, @Param('id') id: string) {
    return this.service.sendConvocations(user, id);
  }

  @Patch(':id/open')
  @Roles('SUPERADMIN', 'SYNDIC')
  open(@CurrentUser() user: AuthenticatedUser, @Param('id') id: string) {
    return this.service.open(user, id);
  }

  @Patch(':id/close')
  @Roles('SUPERADMIN', 'SYNDIC')
  close(@CurrentUser() user: AuthenticatedUser, @Param('id') id: string) {
    return this.service.close(user, id);
  }

  @Post(':id/proxies')
  giveProxy(
    @CurrentUser() user: AuthenticatedUser,
    @Param('id') id: string,
    @Body() dto: GiveProxyDto,
  ) {
    return this.service.giveProxy(user, id, dto.receiverId);
  }

  @Get(':id/my-weight')
  myWeight(@CurrentUser() user: AuthenticatedUser, @Param('id') id: string) {
    return this.service.myWeight(user, id);
  }

  @Post('resolutions/:resolutionId/vote')
  vote(
    @CurrentUser() user: AuthenticatedUser,
    @Param('resolutionId') resolutionId: string,
    @Body() dto: VoteDto,
  ) {
    return this.service.vote(user, resolutionId, dto.choice);
  }

  @Get(':id/pv.pdf')
  async pvPdf(
    @CurrentUser() user: AuthenticatedUser,
    @Param('id') id: string,
    @Res() res: Response,
  ) {
    const pdf = await this.service.generatePvPdf(user, id);
    if (!pdf) {
      res.status(404).send('Assembly not found or not yet closed');
      return;
    }
    res
      .status(200)
      .type('application/pdf')
      .set('Content-Disposition', `inline; filename=pv-assembly-${id}.pdf`)
      .send(pdf);
  }
}
