import {
  Body,
  Controller,
  DefaultValuePipe,
  Delete,
  Get,
  HttpCode,
  HttpStatus,
  Param,
  ParseIntPipe,
  ParseUUIDPipe,
  Patch,
  Post,
  Query,
  Res,
  UploadedFile,
  UseGuards,
  UseInterceptors,
} from '@nestjs/common';
import {
  ApiConsumes,
  ApiOperation,
  ApiResponse,
  ApiTags,
} from '@nestjs/swagger';
import { FileInterceptor } from '@nestjs/platform-express';
import type { Response } from 'express';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { CurrentUser } from '../auth/decorators/current-user.decorator';
import { BusinessException } from '../../common/exceptions/business.exception';
import type { PublicUser } from '../user/user.service';
import type { PaginatedResult } from '../../common/pagination/pagination';
import {
  ProjectService,
  type ProjectResponse,
  type ProjectAttachmentResponse,
} from './project.service';
import {
  UPLOAD_DIR,
  MAX_FILE_SIZE,
  fileMimeFilter,
  type UploadedDiskFile,
} from '../task/upload.config';
import { CreateProjectDto } from './dto/create-project.dto';
import { UpdateProjectDto } from './dto/update-project.dto';

// 프로젝트 컨트롤러 — 모든 엔드포인트 인증 필요
@ApiTags('Project')
@Controller('projects')
@UseGuards(JwtAuthGuard)
export class ProjectController {
  constructor(private readonly projectService: ProjectService) {}

  @Get()
  @ApiOperation({ summary: '프로젝트 전체 목록 (페이지네이션)' })
  @ApiResponse({ status: 200, description: '목록 조회 성공' })
  findAll(
    @Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number,
    @Query('size', new DefaultValuePipe(20), ParseIntPipe) size: number,
    @Query('sort') sort?: string,
    @Query('q') q?: string,
  ): Promise<PaginatedResult<ProjectResponse>> {
    return this.projectService.findAll(page, size, { sort, q });
  }

  @Post()
  @HttpCode(HttpStatus.CREATED)
  @ApiOperation({ summary: '프로젝트 생성' })
  @ApiResponse({ status: 201, description: '생성 성공' })
  create(
    @Body() dto: CreateProjectDto,
    @CurrentUser() user: PublicUser,
  ): Promise<ProjectResponse> {
    return this.projectService.create(dto, user.id);
  }

  @Get(':projectId')
  @ApiOperation({ summary: '프로젝트 단건 조회' })
  @ApiResponse({ status: 200, description: '조회 성공' })
  @ApiResponse({
    status: 404,
    description: '프로젝트를 찾을 수 없음 (RES_001)',
  })
  findOne(
    @Param('projectId', ParseUUIDPipe) projectId: string,
    @CurrentUser() user: PublicUser,
  ): Promise<ProjectResponse> {
    return this.projectService.findOne(projectId, user.id);
  }

  @Patch(':projectId')
  @ApiOperation({ summary: '프로젝트 수정 (admin)' })
  @ApiResponse({ status: 200, description: '수정 성공' })
  @ApiResponse({ status: 403, description: '권한 없음 (AUTH_003)' })
  update(
    @Param('projectId', ParseUUIDPipe) projectId: string,
    @Body() dto: UpdateProjectDto,
    @CurrentUser() user: PublicUser,
  ): Promise<ProjectResponse> {
    return this.projectService.update(projectId, user.id, dto);
  }

  @Delete(':projectId')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: '프로젝트 삭제 (소유자, 시스템 프로젝트 제외)' })
  @ApiResponse({ status: 200, description: '삭제 성공' })
  @ApiResponse({ status: 403, description: '권한 없음 (AUTH_003)' })
  async remove(
    @Param('projectId', ParseUUIDPipe) projectId: string,
    @CurrentUser() user: PublicUser,
  ): Promise<null> {
    await this.projectService.remove(projectId, user.id);
    return null;
  }

  /* ===================== 문서 첨부 ===================== */

  @Post(':projectId/files')
  @HttpCode(HttpStatus.CREATED)
  @UseInterceptors(
    FileInterceptor('file', {
      dest: UPLOAD_DIR,
      limits: { fileSize: MAX_FILE_SIZE },
      fileFilter: fileMimeFilter,
    }),
  )
  @ApiConsumes('multipart/form-data')
  @ApiOperation({ summary: '프로젝트 문서 업로드 (admin)' })
  @ApiResponse({ status: 201, description: '업로드 성공' })
  addFile(
    @Param('projectId', ParseUUIDPipe) projectId: string,
    @UploadedFile() file: UploadedDiskFile,
    @CurrentUser() user: PublicUser,
  ): Promise<ProjectAttachmentResponse> {
    if (!file) {
      throw new BusinessException(
        'BIZ_001',
        '업로드할 수 없는 파일 형식이거나 파일이 없습니다.',
        HttpStatus.BAD_REQUEST,
      );
    }
    return this.projectService.addFile(projectId, user.id, file);
  }

  @Delete(':projectId/files/:fileId')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: '프로젝트 문서 삭제 (admin)' })
  @ApiResponse({ status: 200, description: '삭제 성공' })
  async removeFile(
    @Param('projectId', ParseUUIDPipe) projectId: string,
    @Param('fileId', ParseUUIDPipe) fileId: string,
    @CurrentUser() user: PublicUser,
  ): Promise<null> {
    await this.projectService.removeFile(projectId, user.id, fileId);
    return null;
  }

  @Get(':projectId/files/:fileId/download')
  @ApiOperation({ summary: '프로젝트 문서 다운로드 (멤버)' })
  @ApiResponse({ status: 200, description: '파일 스트림' })
  async downloadFile(
    @Param('projectId', ParseUUIDPipe) projectId: string,
    @Param('fileId', ParseUUIDPipe) fileId: string,
    @CurrentUser() user: PublicUser,
    @Res() res: Response,
  ): Promise<void> {
    const { attachment, absolutePath } =
      await this.projectService.getFileForDownload(projectId, user.id, fileId);
    res.setHeader('Content-Type', attachment.mime);
    res.setHeader('X-Content-Type-Options', 'nosniff');
    res.setHeader(
      'Content-Disposition',
      `attachment; filename*=UTF-8''${encodeURIComponent(attachment.name)}`,
    );
    res.sendFile(absolutePath);
  }
}
