import {
  BadRequestException,
  Body,
  Controller,
  Delete,
  Get,
  MaxFileSizeValidator,
  Param,
  Patch,
  Post,
  ParseFilePipe,
  Req,
  UploadedFile,
  UseGuards,
  UseInterceptors,
} from '@nestjs/common';
import { UserRole } from '@prisma/client';
import { FileInterceptor } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import { extname, join } from 'path';
import { existsSync, mkdirSync, readdirSync } from 'fs';
import type { AuthenticatedRequest } from '../auth/jwt-auth.guard';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { Roles } from '../auth/roles.decorator';
import { RolesGuard } from '../auth/roles.guard';
import { AssignProjectParticipantDto } from './dto/assign-project-participant.dto';
import { CloneProjectDto } from './dto/clone-project.dto';
import { CreateProjectDto } from './dto/create-project.dto';
import { UpdateProjectDto } from './dto/update-project.dto';
import { ProjectsService } from './projects.service';

const uploadsDir = join(process.cwd(), 'uploads');

const storage = diskStorage({
  destination: (_req, _file, callback) => {
    if (!existsSync(uploadsDir)) {
      mkdirSync(uploadsDir, { recursive: true });
    }
    callback(null, uploadsDir);
  },
  filename: (_req, file, callback) => {
    const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1e9)}`;
    callback(null, `project-${uniqueSuffix}${extname(file.originalname)}`);
  },
});

@Controller('projects')
@UseGuards(JwtAuthGuard)
export class ProjectsController {
  private readonly defaultProjectAvatars: string[];

  constructor(private readonly projectsService: ProjectsService) {
    const avatarsDir = join(process.cwd(), 'public', 'default-project-avatars');
    const imageExts = ['.png', '.jpg', '.jpeg', '.webp'];
    try {
      this.defaultProjectAvatars = readdirSync(avatarsDir)
        .filter((f) => imageExts.includes(extname(f).toLowerCase()))
        .sort()
        .map((f) => `/default-project-avatars/${f}`);
    } catch {
      this.defaultProjectAvatars = [];
    }
  }

  @Get()
  listForUser(@Req() request: AuthenticatedRequest) {
    return this.projectsService.listForUser(request.user!.sub);
  }

  @Get(':projectId')
  getById(@Req() request: AuthenticatedRequest, @Param('projectId') projectId: string) {
    return this.projectsService.getByIdForUser(request.user!.sub, projectId);
  }

  @Post()
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  create(@Req() request: AuthenticatedRequest, @Body() dto: CreateProjectDto) {
    return this.projectsService.create(request.user!.sub, dto);
  }

  @Patch(':projectId')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  update(
    @Req() request: AuthenticatedRequest,
    @Param('projectId') projectId: string,
    @Body() dto: UpdateProjectDto,
  ) {
    return this.projectsService.update(request.user!.sub, projectId, dto);
  }

  @Post(':projectId/clone')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  clone(
    @Req() request: AuthenticatedRequest,
    @Param('projectId') projectId: string,
    @Body() dto: CloneProjectDto,
  ) {
    return this.projectsService.clone(request.user!.sub, projectId, dto);
  }

  @Get(':projectId/participants')
  listParticipants(@Req() request: AuthenticatedRequest, @Param('projectId') projectId: string) {
    return this.projectsService.listParticipants(request.user!.sub, projectId);
  }

  @Post(':projectId/participants')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  addParticipant(
    @Req() request: AuthenticatedRequest,
    @Param('projectId') projectId: string,
    @Body() dto: AssignProjectParticipantDto,
  ) {
    return this.projectsService.addParticipant(request.user!.sub, projectId, dto);
  }

  @Delete(':projectId/participants/:participantUserId')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  removeParticipant(
    @Req() request: AuthenticatedRequest,
    @Param('projectId') projectId: string,
    @Param('participantUserId') participantUserId: string,
  ) {
    return this.projectsService.removeParticipant(
      request.user!.sub,
      projectId,
      participantUserId,
    );
  }

  @Get(':projectId/default-project-avatars')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  listDefaultProjectAvatars() {
    return this.defaultProjectAvatars;
  }

  @Post(':projectId/photo')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  @UseInterceptors(FileInterceptor('file', { storage }))
  uploadProjectPhoto(
    @Req() request: AuthenticatedRequest,
    @Param('projectId') projectId: string,
    @Body() body: { bgColor?: string },
    @UploadedFile(
      new ParseFilePipe({
        validators: [new MaxFileSizeValidator({ maxSize: 5 * 1024 * 1024 })],
        fileIsRequired: false,
      }),
    )
    file?: Express.Multer.File,
  ) {
    if (file) {
      const allowedMimeTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/webp'];
      if (!allowedMimeTypes.includes(file.mimetype)) {
        throw new BadRequestException('Format non supporté. Utilisez PNG, JPG ou WEBP.');
      }
    }

    if (!file && !body.bgColor) {
      throw new BadRequestException('Envoyez une image ou une couleur de fond.');
    }

    return this.projectsService.updateProjectImage(
      request.user!.sub,
      projectId,
      file ? `/uploads/${file.filename}` : undefined,
      body.bgColor ?? undefined,
    );
  }

  @Delete(':projectId/photo')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  deleteProjectPhoto(
    @Req() request: AuthenticatedRequest,
    @Param('projectId') projectId: string,
  ) {
    return this.projectsService.updateProjectImage(request.user!.sub, projectId, null, null);
  }

  @Patch(':projectId/avatar')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN)
  setProjectDefaultAvatar(
    @Req() request: AuthenticatedRequest,
    @Param('projectId') projectId: string,
    @Body() body: { avatarUrl: string; bgColor?: string },
  ) {
    if (!body.avatarUrl || !this.defaultProjectAvatars.includes(body.avatarUrl)) {
      throw new BadRequestException('Avatar par defaut invalide.');
    }
    return this.projectsService.updateProjectImage(
      request.user!.sub,
      projectId,
      body.avatarUrl,
      body.bgColor,
    );
  }
}
