import {
  Controller,
  Get,
  Post,
  Patch,
  Delete,
  Param,
  Body,
  Query,
  UseGuards,
  ParseUUIDPipe,
} from '@nestjs/common';
import { ApiTags, ApiBearerAuth, ApiOperation, ApiQuery } from '@nestjs/swagger';
import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard';
import { CurrentUser, JwtPayload } from '../../../common/decorators/current-user.decorator';
import { RequireModule } from '../../../common/decorators/require-module.decorator';
import { ModuleGuard } from '../../../common/guards/module.guard';
import { Roles, UserRole } from '../../../common/decorators/roles.decorator';
import { RolesGuard } from '../../auth/guards/roles.guard';
import { PrismaService } from '../../../prisma/prisma.service';
import { WebhookDispatcherService } from '../services/webhook-dispatcher.service';
import { CreateWebhookEndpointDto, UpdateWebhookEndpointDto } from '../dto/webhook-settings.dto';
import { randomBytes } from 'crypto';

@ApiTags('Booking / Webhooks')
@ApiBearerAuth()
@Controller('booking/webhooks')
@UseGuards(JwtAuthGuard, ModuleGuard, RolesGuard)
@RequireModule('booking')
@Roles(UserRole.CLUB_ADMIN, UserRole.SUPER_ADMIN)
export class BookingWebhooksController {
  constructor(
    private readonly prisma: PrismaService,
    private readonly dispatcher: WebhookDispatcherService,
  ) {}

  // ─── Endpoints CRUD ──────────────────────────────────────

  @Get('endpoints')
  @ApiOperation({ summary: 'List webhook endpoints' })
  async listEndpoints(@CurrentUser() user: JwtPayload) {
    return this.prisma.bookingWebhookEndpoint.findMany({
      where: { clubId: user.clubId },
      orderBy: { createdAt: 'desc' },
    });
  }

  @Post('endpoints')
  @ApiOperation({ summary: 'Create webhook endpoint' })
  async createEndpoint(@CurrentUser() user: JwtPayload, @Body() dto: CreateWebhookEndpointDto) {
    const secret = randomBytes(32).toString('hex');

    return this.prisma.bookingWebhookEndpoint.create({
      data: {
        clubId: user.clubId,
        name: dto.name,
        url: dto.url,
        secret,
        events: dto.events,
        isActive: dto.isActive ?? true,
      },
    });
  }

  @Patch('endpoints/:id')
  @ApiOperation({ summary: 'Update webhook endpoint' })
  async updateEndpoint(
    @CurrentUser() user: JwtPayload,
    @Param('id', ParseUUIDPipe) id: string,
    @Body() dto: UpdateWebhookEndpointDto,
  ) {
    // Verify ownership
    await this.prisma.bookingWebhookEndpoint.findFirstOrThrow({
      where: { id, clubId: user.clubId },
    });

    return this.prisma.bookingWebhookEndpoint.update({
      where: { id },
      data: {
        ...(dto.name !== undefined ? { name: dto.name } : {}),
        ...(dto.url !== undefined ? { url: dto.url } : {}),
        ...(dto.events !== undefined ? { events: dto.events } : {}),
        ...(dto.isActive !== undefined ? { isActive: dto.isActive } : {}),
      },
    });
  }

  @Delete('endpoints/:id')
  @ApiOperation({ summary: 'Delete webhook endpoint' })
  async deleteEndpoint(@CurrentUser() user: JwtPayload, @Param('id', ParseUUIDPipe) id: string) {
    await this.prisma.bookingWebhookEndpoint.findFirstOrThrow({
      where: { id, clubId: user.clubId },
    });

    await this.prisma.bookingWebhookEndpoint.delete({ where: { id } });
    return { success: true };
  }

  @Post('endpoints/:id/rotate-secret')
  @ApiOperation({ summary: 'Rotate webhook secret' })
  async rotateSecret(@CurrentUser() user: JwtPayload, @Param('id', ParseUUIDPipe) id: string) {
    await this.prisma.bookingWebhookEndpoint.findFirstOrThrow({
      where: { id, clubId: user.clubId },
    });

    const secret = randomBytes(32).toString('hex');
    const updated = await this.prisma.bookingWebhookEndpoint.update({
      where: { id },
      data: { secret },
    });

    return { secret: updated.secret };
  }

  // ─── Logs ────────────────────────────────────────────────

  @Get('logs')
  @ApiOperation({ summary: 'List webhook delivery logs' })
  @ApiQuery({ name: 'endpointId', required: false })
  @ApiQuery({ name: 'event', required: false })
  @ApiQuery({ name: 'cursor', required: false })
  @ApiQuery({ name: 'limit', required: false })
  async listLogs(
    @CurrentUser() user: JwtPayload,
    @Query('endpointId') endpointId?: string,
    @Query('event') event?: string,
    @Query('cursor') cursor?: string,
    @Query('limit') limitStr?: string,
  ) {
    const limit = Math.min(parseInt(limitStr || '50', 10) || 50, 100);

    const where: Record<string, any> = {
      endpoint: { clubId: user.clubId },
    };
    if (endpointId) where.endpointId = endpointId;
    if (event) where.event = event;

    const items = await this.prisma.bookingWebhookLog.findMany({
      where,
      include: { endpoint: { select: { id: true, name: true, url: true } } },
      take: limit + 1,
      ...(cursor ? { cursor: { id: cursor }, skip: 1 } : {}),
      orderBy: { createdAt: 'desc' },
    });

    const hasMore = items.length > limit;
    if (hasMore) items.pop();

    return {
      items,
      nextCursor: hasMore ? items[items.length - 1]?.id : null,
      hasMore,
    };
  }

  @Post('logs/:id/redeliver')
  @ApiOperation({ summary: 'Re-deliver a failed webhook' })
  async redeliver(@CurrentUser() user: JwtPayload, @Param('id', ParseUUIDPipe) id: string) {
    // Verify this log belongs to user's club
    await this.prisma.bookingWebhookLog.findFirstOrThrow({
      where: { id, endpoint: { clubId: user.clubId } },
    });

    return this.dispatcher.redeliver(id);
  }
}
