import {
  Body,
  Controller,
  Get,
  Ip,
  Param,
  Post,
  Query,
  UseGuards,
} from '@nestjs/common';
import { CommandBus, QueryBus } from '@nestjs/cqrs';
import {
  ApiTags,
  ApiOperation,
  ApiResponse,
  ApiBearerAuth,
  ApiParam,
} from '@nestjs/swagger';
import { Throttle } from '@nestjs/throttler';
import { type PaymentProvider } from '@prisma/client';
import { type JwtPayload, CurrentUser, Roles, JwtAuthGuard, RolesGuard } from '@modules/auth';
import { EndpointRateLimit, EndpointRateLimitGuard } from '@modules/shared';
import { ConfirmBankTransferCommand } from '../../application/commands/confirm-bank-transfer/confirm-bank-transfer.command';
import { type ConfirmBankTransferResult } from '../../application/commands/confirm-bank-transfer/confirm-bank-transfer.handler';
import { CreatePaymentCommand } from '../../application/commands/create-payment/create-payment.command';
import { type CreatePaymentResult } from '../../application/commands/create-payment/create-payment.handler';
import { HandleCallbackCommand } from '../../application/commands/handle-callback/handle-callback.command';
import { type HandleCallbackResult } from '../../application/commands/handle-callback/handle-callback.handler';
import { RefundPaymentCommand } from '../../application/commands/refund-payment/refund-payment.command';
import { type RefundPaymentResult } from '../../application/commands/refund-payment/refund-payment.handler';
import { type PaymentStatusDto } from '../../application/queries/get-payment-status/get-payment-status.handler';
import { GetPaymentStatusQuery } from '../../application/queries/get-payment-status/get-payment-status.query';
import { type TransactionListDto } from '../../application/queries/list-transactions/list-transactions.handler';
import { ListTransactionsQuery } from '../../application/queries/list-transactions/list-transactions.query';
import { ConfirmBankTransferDto } from '../dto/confirm-bank-transfer.dto';
import { CreatePaymentDto } from '../dto/create-payment.dto';
import { ListTransactionsDto } from '../dto/list-transactions.dto';
import { RefundPaymentDto } from '../dto/refund-payment.dto';

@ApiTags('payments')
@Controller('payments')
export class PaymentsController {
  constructor(
    private readonly commandBus: CommandBus,
    private readonly queryBus: QueryBus,
  ) {}

  @ApiBearerAuth('JWT')
  @ApiOperation({ summary: 'Create a new payment' })
  @ApiResponse({ status: 201, description: 'Payment created successfully' })
  @ApiResponse({ status: 400, description: 'Bad request' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  @UseGuards(JwtAuthGuard)
  @Post()
  async createPayment(
    @Body() dto: CreatePaymentDto,
    @CurrentUser() user: JwtPayload,
    @Ip() ip: string,
  ): Promise<CreatePaymentResult> {
    return this.commandBus.execute(
      new CreatePaymentCommand(
        user.sub,
        dto.provider,
        dto.type,
        BigInt(dto.amountVND),
        dto.description,
        dto.returnUrl,
        ip || '127.0.0.1',
        dto.transactionId,
        dto.idempotencyKey,
      ),
    );
  }

  @ApiOperation({ summary: 'Handle payment provider callback (webhook)' })
  @ApiResponse({ status: 201, description: 'Callback processed successfully' })
  @ApiParam({ name: 'provider', enum: ['vnpay', 'momo', 'zalopay', 'bank_transfer'] })
  @Throttle({ 'payment-callback': { ttl: 60_000, limit: 20 } })
  @EndpointRateLimit({ limit: 100, windowSeconds: 60, keyStrategy: 'ip', adminBypass: false })
  @UseGuards(EndpointRateLimitGuard)
  @Post('callback/:provider')
  async handleCallback(
    @Param('provider') provider: string,
    @Body() callbackData: Record<string, string>,
    @Query() queryData: Record<string, string>,
  ): Promise<HandleCallbackResult> {
    const providerUpper = provider.toUpperCase() as PaymentProvider;
    // Merge query params and body (VNPay sends via query, MoMo/ZaloPay via body)
    const mergedData = { ...queryData, ...callbackData };
    return this.commandBus.execute(
      new HandleCallbackCommand(providerUpper, mergedData),
    );
  }

  @ApiBearerAuth('JWT')
  @ApiOperation({ summary: 'Get payment status by ID' })
  @ApiResponse({ status: 200, description: 'Payment status retrieved' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  @ApiResponse({ status: 404, description: 'Payment not found' })
  @UseGuards(JwtAuthGuard)
  @Get(':id')
  async getPaymentStatus(
    @Param('id') id: string,
    @CurrentUser() user: JwtPayload,
  ): Promise<PaymentStatusDto> {
    return this.queryBus.execute(new GetPaymentStatusQuery(id, user.sub));
  }

  @ApiBearerAuth('JWT')
  @ApiOperation({ summary: 'List transactions for the authenticated user' })
  @ApiResponse({ status: 200, description: 'Transactions retrieved' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  @UseGuards(JwtAuthGuard)
  @Get()
  async listTransactions(
    @CurrentUser() user: JwtPayload,
    @Query() dto: ListTransactionsDto,
  ): Promise<TransactionListDto> {
    return this.queryBus.execute(
      new ListTransactionsQuery(user.sub, dto.status, dto.limit, dto.offset),
    );
  }

  @ApiBearerAuth('JWT')
  @ApiOperation({ summary: 'Refund a payment (admin only)' })
  @ApiResponse({ status: 201, description: 'Refund initiated successfully' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  @ApiResponse({ status: 403, description: 'Forbidden — admin role required' })
  @ApiResponse({ status: 404, description: 'Payment not found' })
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles('ADMIN')
  @Post(':id/refund')
  async refundPayment(
    @Param('id') id: string,
    @Body() dto: RefundPaymentDto,
    @CurrentUser() user: JwtPayload,
  ): Promise<RefundPaymentResult> {
    return this.commandBus.execute(
      new RefundPaymentCommand(id, dto.reason, user.sub),
    );
  }

  @ApiBearerAuth('JWT')
  @ApiOperation({ summary: 'Confirm a bank transfer payment (admin only)' })
  @ApiResponse({ status: 201, description: 'Bank transfer confirmed successfully' })
  @ApiResponse({ status: 400, description: 'Payment is not a bank transfer or invalid status' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  @ApiResponse({ status: 403, description: 'Forbidden — admin role required' })
  @ApiResponse({ status: 404, description: 'Payment not found' })
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles('ADMIN')
  @Post(':id/confirm-transfer')
  async confirmBankTransfer(
    @Param('id') id: string,
    @Body() dto: ConfirmBankTransferDto,
    @CurrentUser() user: JwtPayload,
  ): Promise<ConfirmBankTransferResult> {
    return this.commandBus.execute(
      new ConfirmBankTransferCommand(id, user.sub, dto.bankReference),
    );
  }
}
