import {
  Controller,
  Get,
  Post,
  Patch,
  Delete,
  Body,
  Param,
  Query,
  Inject,
  UseGuards,
  HttpCode,
  HttpStatus,
} from '@nestjs/common';
// eslint-disable-next-line @typescript-eslint/consistent-type-imports -- NestJS DI requires value imports
import { CommandBus, QueryBus } from '@nestjs/cqrs';
import { AuthGuard } from '@nestjs/passport';
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, ApiQuery, ApiProperty } from '@nestjs/swagger';
import { MessageType as PrismaMessageType } from '@prisma/client';
import { IsString, IsOptional, IsEnum, MaxLength } from 'class-validator';
import { CurrentUser, type JwtPayload } from '@modules/auth';
import { CreateConversationCommand } from '../../application/commands/create-conversation/create-conversation.command';
import { MarkConversationReadCommand } from '../../application/commands/mark-read/mark-read.command';
import { SendMessageCommand } from '../../application/commands/send-message/send-message.command';
import { GetConversationsQuery } from '../../application/queries/get-conversations/get-conversations.query';
import { GetMessagesQuery } from '../../application/queries/get-messages/get-messages.query';
import {
  MESSAGE_REPOSITORY,
  type IMessageRepository,
} from '../../domain/repositories/message.repository';

class CreateConversationDto {
  @ApiProperty({ description: 'User ID of the other participant' })
  @IsString()
  participantUserId!: string;

  @ApiProperty({ required: false, description: 'Associated listing ID' })
  @IsString()
  @IsOptional()
  listingId?: string;

  @ApiProperty({ required: false, description: 'Conversation subject' })
  @IsString()
  @IsOptional()
  @MaxLength(200)
  subject?: string;

  @ApiProperty({ required: false, description: 'Initial message text' })
  @IsString()
  @IsOptional()
  @MaxLength(5000)
  initialMessage?: string;
}

class SendMessageDto {
  @ApiProperty({ description: 'Message content' })
  @IsString()
  @MaxLength(5000)
  content!: string;

  @ApiProperty({ enum: PrismaMessageType, required: false, default: 'TEXT' })
  @IsEnum(PrismaMessageType)
  @IsOptional()
  type?: PrismaMessageType;

  @ApiProperty({ required: false, description: 'Additional metadata (e.g. file URL)' })
  @IsOptional()
  metadata?: Record<string, unknown>;
}

@ApiTags('messaging')
@ApiBearerAuth('JWT')
@Controller('messaging')
@UseGuards(AuthGuard('jwt'))
export class MessagingController {
  constructor(
    private readonly commandBus: CommandBus,
    private readonly queryBus: QueryBus,
    @Inject(MESSAGE_REPOSITORY)
    private readonly messageRepo: IMessageRepository,
  ) {}

  @Post('conversations')
  @ApiOperation({ summary: 'Create a conversation or return existing one' })
  @ApiResponse({ status: 201, description: 'Conversation created' })
  @ApiResponse({ status: 400, description: 'Cannot create conversation with yourself' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  async createConversation(
    @CurrentUser() user: JwtPayload,
    @Body() dto: CreateConversationDto,
  ) {
    return this.commandBus.execute(
      new CreateConversationCommand(
        user.sub,
        dto.participantUserId,
        dto.listingId,
        dto.subject,
        dto.initialMessage,
      ),
    );
  }

  @Get('conversations')
  @ApiOperation({ summary: 'List user conversations' })
  @ApiResponse({ status: 200, description: 'Conversations retrieved' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  @ApiQuery({ name: 'limit', required: false, type: Number })
  @ApiQuery({ name: 'offset', required: false, type: Number })
  async getConversations(
    @CurrentUser() user: JwtPayload,
    @Query('limit') limit?: number,
    @Query('offset') offset?: number,
  ) {
    return this.queryBus.execute(
      new GetConversationsQuery(user.sub, limit ?? 20, offset ?? 0),
    );
  }

  @Get('conversations/:id/messages')
  @ApiOperation({ summary: 'Get messages in a conversation' })
  @ApiResponse({ status: 200, description: 'Messages retrieved' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  @ApiResponse({ status: 403, description: 'Not a participant' })
  @ApiResponse({ status: 404, description: 'Conversation not found' })
  @ApiQuery({ name: 'limit', required: false, type: Number })
  @ApiQuery({ name: 'before', required: false, type: String, description: 'Cursor: message ID for pagination' })
  async getMessages(
    @CurrentUser() user: JwtPayload,
    @Param('id') conversationId: string,
    @Query('limit') limit?: number,
    @Query('before') before?: string,
  ) {
    return this.queryBus.execute(
      new GetMessagesQuery(conversationId, user.sub, limit ?? 50, before),
    );
  }

  @Post('conversations/:id/messages')
  @HttpCode(HttpStatus.CREATED)
  @ApiOperation({ summary: 'Send a message in a conversation' })
  @ApiResponse({ status: 201, description: 'Message sent' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  @ApiResponse({ status: 403, description: 'Not a participant' })
  @ApiResponse({ status: 404, description: 'Conversation not found' })
  async sendMessage(
    @CurrentUser() user: JwtPayload,
    @Param('id') conversationId: string,
    @Body() dto: SendMessageDto,
  ) {
    return this.commandBus.execute(
      new SendMessageCommand(
        conversationId,
        user.sub,
        dto.content,
        dto.type ?? 'TEXT',
        dto.metadata,
      ),
    );
  }

  @Patch('conversations/:id/read')
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiOperation({ summary: 'Mark a conversation as read' })
  @ApiResponse({ status: 204, description: 'Conversation marked as read' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  @ApiResponse({ status: 403, description: 'Not a participant' })
  @ApiResponse({ status: 404, description: 'Conversation not found' })
  async markAsRead(
    @CurrentUser() user: JwtPayload,
    @Param('id') conversationId: string,
  ) {
    await this.commandBus.execute(
      new MarkConversationReadCommand(conversationId, user.sub),
    );
  }

  @Delete('conversations/:conversationId/messages/:messageId')
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiOperation({ summary: 'Soft-delete a message (sender only)' })
  @ApiResponse({ status: 204, description: 'Message deleted' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  async deleteMessage(
    @CurrentUser() user: JwtPayload,
    @Param('messageId') messageId: string,
  ) {
    await this.messageRepo.softDelete(messageId, user.sub);
  }
}
