import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
  UseGuards,
  Query,
  Request,
  Req,
  Logger,
  UseInterceptors,
  UploadedFile,
} from '@nestjs/common';
import { TicketsService } from './ticket.service';
import { CreateTicketDto } from './dto/create-ticket.dto';
import { UpdateTicketDto } from './dto/update-ticket.dto';
import { CreateCommentDto } from './dto/create-comment.dto';
import { JwtAuthGuard } from 'common/guards/jwt-auth.guard';
import { TicketPriority, TicketSource, TicketStatus } from 'common/enum/ticket.enum';
import { ApiBearerAuth, ApiTags, ApiOperation, ApiQuery, ApiResponse, ApiConsumes, ApiBody } from '@nestjs/swagger';
import { TicketResponseDto } from './dto/response-ticket.dto';
import { Roles } from 'common/decorators/role.decorator';
import { UserRole } from 'common/enum/UserRole.enum';
import { FileInterceptor } from '@nestjs/platform-express';
import { Comment } from './entities/comment.entity';
import { RolesGuard } from 'common/guards/roles.guard';

@ApiTags('tickets')
@Controller({ path: 'tickets', version: '1' })
@UseGuards(JwtAuthGuard, RolesGuard)
@ApiBearerAuth()
export class TicketsController {
  private readonly logger = new Logger(TicketsController.name);
  constructor(private readonly ticketsService: TicketsService) {}

  @Post()
  @UseInterceptors(FileInterceptor('attachment'))
  @ApiOperation({ summary: 'Create a new ticket' })
  @ApiResponse({ status: 201, description: 'Ticket successfully created.', type: TicketResponseDto })
  @ApiConsumes('multipart/form-data')
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        title: { type: 'string', example: 'Login issue' },
        description: { type: 'string', example: 'Unable to log into the system' },
        labels: { type: 'simple-array', items: { type: 'string' }, example: ['bug', 'urgent'] },
        status: { type: 'string', enum: Object.values(TicketStatus), example: TicketStatus.OPEN },
        priority: { type: 'string', enum: Object.values(TicketPriority), example: TicketPriority.MEDIUM },
        source: { type: 'string', enum: Object.values(TicketSource), example: TicketSource.MANUAL },
        attachment: { type: 'string', format: 'binary' },
      },
      required: ['title', 'description'],
    },
  })
  async create(@Body() createTicketDto: CreateTicketDto, @Req() req: any, @UploadedFile() file: Express.Multer.File) {
    const { user } = req;
    if (!user) {
      throw new Error('User not found');
    }
    this.logger.log('Creating ticket...', createTicketDto);
    this.logger.log('file', file);
    this.logger.debug('User ID:', user.id);
    return this.ticketsService.create(createTicketDto, file, user.role, user.id);
  }

  @Get()
  @Roles(UserRole.ADMIN, UserRole.SUPER_ADMIN)
  @ApiOperation({ summary: 'Get all tickets with optional filters' })
  @ApiQuery({ name: 'status', enum: TicketStatus, required: false })
  @ApiQuery({ name: 'assigneeId', required: false })
  @ApiQuery({ name: 'authorId', required: false })
  @ApiQuery({ name: 'labels', required: false, isArray: true })
  async findAll(
    @Query('status') status?: TicketStatus,
    @Query('assigneeId') assigneeId?: number,
    @Query('authorId') authorId?: number,
    @Query('labels') labels?: string[],
  ): Promise<TicketResponseDto[]> {
    return this.ticketsService.findAll(status, assigneeId, authorId, labels);
  }

  @Get('my-tickets')
  @Roles(UserRole.ADMIN, UserRole.USER, UserRole.SUPER_ADMIN)
  @ApiOperation({ summary: 'Get all tickets assigned to the current user' })
  @ApiResponse({ status: 200, description: 'Tickets found.', type: [TicketResponseDto] })
  @ApiResponse({ status: 404, description: 'Tickets not found.' })
  async findMyTickets(@Req() req: any): Promise<TicketResponseDto[]> {
    const { user } = req;
    if (!user) {
      throw new Error('User not found');
    }
    return this.ticketsService.findMyTickets(user.id, user.role);
  }

  @Get(':id')
  @Roles(UserRole.ADMIN, UserRole.USER, UserRole.SUPER_ADMIN)
  @ApiOperation({ summary: 'Get a ticket by ID' })
  @ApiResponse({ status: 200, description: 'Ticket found.', type: TicketResponseDto })
  @ApiResponse({ status: 404, description: 'Ticket not found.' })
  async findOne(@Req() req: any, @Param('id') id: number): Promise<TicketResponseDto> {
    const { user } = req;
    return this.ticketsService.findOne(id, user.role);
  }

  @Patch(':id')
  @Roles(UserRole.ADMIN, UserRole.SUPER_ADMIN)
  @ApiOperation({ summary: 'Update a ticket' })
  @ApiResponse({ status: 200, description: 'Ticket updated successfully.', type: TicketResponseDto })
  @ApiResponse({ status: 404, description: 'Ticket not found.' })
  async update(@Req() req: any, @Param('id') id: number, @Body() updateTicketDto: UpdateTicketDto): Promise<TicketResponseDto> {
    const { user } = req;
    return this.ticketsService.update(id, updateTicketDto, user.role);
  }

  @Delete(':id')
  @Roles(UserRole.ADMIN, UserRole.SUPER_ADMIN)
  @ApiOperation({ summary: 'Delete a ticket' })
  @ApiResponse({ status: 200, description: 'Ticket deleted successfully.' })
  @ApiResponse({ status: 404, description: 'Ticket not found.' })
  async remove(@Param('id') id: number, @Request() req): Promise<{ message: string }> {
    const { user } = req;
    if (!user) {
      throw new Error('User not found');
    }
    return this.ticketsService.remove(id, user.id);
  }

  @Post(':id/comments')
  @UseInterceptors(FileInterceptor('attachment'))
  @Roles(UserRole.ADMIN, UserRole.USER, UserRole.SUPER_ADMIN)
  @ApiOperation({ summary: 'Add a comment to a ticket' })
  @ApiResponse({ status: 201, description: 'Comment added successfully.', type: TicketResponseDto })
  @ApiResponse({ status: 404, description: 'Ticket not found.' })
  @ApiConsumes('multipart/form-data')
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        content: { type: 'string' },
        role: { type: 'string', enum: Object.values(UserRole) },
        attachment: { type: 'string', format: 'binary' },
      },
      required: ['content'],
    },
  })
  async addComment(@Param('id') id: number, @Body() createCommentDto: CreateCommentDto, @Request() req, @UploadedFile() file: Express.Multer.File) {
    const { user } = req;
    if (!user) {
      throw new Error('User not found');
    }

    return this.ticketsService.addComment(id, createCommentDto, user.id, user.role, file);
  }

  @Get(':id/comments')
  @Roles(UserRole.ADMIN, UserRole.USER, UserRole.SUPER_ADMIN)
  @ApiOperation({ summary: 'Get all comments for a ticket' })
  @ApiResponse({ status: 200, description: 'Comments retrieved.' })
  @ApiResponse({ status: 404, description: 'Ticket not found.' })
  async getComments(@Req() req: any, @Param('id') id: number) {
    const { user } = req;
    return this.ticketsService.getComments(id, user.role);
  }

  @Patch(':ticketToken/comments/:commentId/seen')
  @Roles(UserRole.ADMIN, UserRole.USER, UserRole.SUPER_ADMIN)
  @ApiOperation({ summary: 'Update a comment' })
  @ApiResponse({ status: 200, description: 'Comment updated successfully.' })
  @ApiResponse({ status: 404, description: 'Comment not found.' })
  @ApiResponse({ status: 403, description: 'Forbidden.' })
  @ApiResponse({ status: 400, description: 'Bad request.' })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        isSeen: { type: 'boolean', example: true },
        seenAt: { type: 'string', format: 'date-time', example: '2023-10-01T12:00:00Z' },
      },
      required: ['isSeen', 'seenAt'],
    },
  })
  async updateCommentSeen(
    @Param('ticketToken') ticketToken: string,
    @Param('commentId') commentId: number,
    @Body() body: { isSeen: boolean; seenAt: Date },
  ): Promise<Comment> {
    const { isSeen, seenAt } = body;
    return this.ticketsService.markCommentAsSeen(ticketToken, commentId, isSeen, seenAt);
  }

  @Delete('comments/:commentId')
  @Roles(UserRole.ADMIN, UserRole.USER, UserRole.SUPER_ADMIN)
  @ApiOperation({ summary: 'Delete a comment' })
  @ApiResponse({ status: 200, description: 'Comment deleted successfully.' })
  @ApiResponse({ status: 404, description: 'Comment not found.' })
  @ApiResponse({ status: 403, description: 'Forbidden.' })
  async deleteComment(@Param('commentId') commentId: number, @Request() req): Promise<{ message: string }> {
    const { user } = req;
    if (!user) {
      throw new Error('User not found');
    }
    return this.ticketsService.deleteComment(commentId, user.id);
  }
}
