import {
  Controller,
  Get,
  Post,
  Param,
  Body,
  Query,
  UseGuards,
  BadRequestException,
  NotFoundException,
  Logger,
} from '@nestjs/common';
import { ApiTags, ApiOperation, ApiHeader, ApiQuery } from '@nestjs/swagger';
import { Throttle } from '@nestjs/throttler';
import { ApiKeyGuard } from '../../auth/guards/api-key.guard';
import { PrismaService } from '../../../prisma/prisma.service';
import { AvailabilityService } from '../core/availability.service';
import { BookingRecordsService } from '../services/records.service';
import { WidgetSettingsService } from '../services/widget-settings.service';
import { PublicBookDto, PublicVerifyPhoneDto } from '../dto/book-public.dto';

/**
 * Public booking endpoints for the embeddable widget.
 *
 * No JWT required — authenticated via X-API-Key header.
 * Club is resolved by slug from URL path.
 * Rate-limited to prevent abuse.
 */
@ApiTags('Booking / Public (Widget)')
@ApiHeader({ name: 'X-API-Key', required: true })
@Controller('booking/public')
export class BookingPublicController {
  private readonly logger = new Logger(BookingPublicController.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly availability: AvailabilityService,
    private readonly records: BookingRecordsService,
    private readonly widgetSettings: WidgetSettingsService,
  ) {}

  // ─── Widget Config ─────────────────────────────────────

  @Get(':clubSlug/config')
  @ApiOperation({ summary: 'Get widget config (colors, logo, fields)' })
  @Throttle({ default: { ttl: 60000, limit: 60 } })
  async getConfig(@Param('clubSlug') clubSlug: string) {
    return this.widgetSettings.getSettingsBySlug(clubSlug);
  }

  // ─── Services ──────────────────────────────────────────

  @Get(':clubSlug/services')
  @ApiOperation({ summary: 'List active services with categories' })
  @Throttle({ default: { ttl: 60000, limit: 60 } })
  async getServices(@Param('clubSlug') clubSlug: string) {
    const club = await this.resolveClub(clubSlug);

    const categories = await this.prisma.bookingServiceCategory.findMany({
      where: { clubId: club.id },
      orderBy: { position: 'asc' },
      select: { id: true, name: true, position: true },
    });

    const services = await this.prisma.bookingService.findMany({
      where: { clubId: club.id, isActive: true, isOnline: true },
      orderBy: { position: 'asc' },
      select: {
        id: true,
        categoryId: true,
        name: true,
        description: true,
        duration: true,
        price: true,
        type: true,
        capacity: true,
        membershipOnly: true,
        color: true,
      },
    });

    return { categories, services };
  }

  // ─── Staff for a service ───────────────────────────────

  @Get(':clubSlug/staff')
  @ApiOperation({ summary: 'List staff available for a service' })
  @ApiQuery({ name: 'serviceId', type: String })
  @Throttle({ default: { ttl: 60000, limit: 60 } })
  async getStaff(@Param('clubSlug') clubSlug: string, @Query('serviceId') serviceId: string) {
    if (!serviceId) throw new BadRequestException('serviceId is required');

    const club = await this.resolveClub(clubSlug);

    const staffLinks = await this.prisma.bookingServiceStaff.findMany({
      where: { serviceId, service: { clubId: club.id } },
      include: {
        staff: {
          select: {
            id: true,
            firstName: true,
            lastName: true,
            avatarUrl: true,
          },
        },
      },
    });

    return staffLinks.map((link) => link.staff);
  }

  // ─── Available dates ───────────────────────────────────

  @Get(':clubSlug/dates')
  @ApiOperation({ summary: 'Get dates with available slots' })
  @ApiQuery({ name: 'serviceId', type: String })
  @ApiQuery({ name: 'staffId', type: String })
  @ApiQuery({ name: 'from', type: String, example: '2026-03-01' })
  @ApiQuery({ name: 'to', type: String, example: '2026-03-31' })
  @Throttle({ default: { ttl: 60000, limit: 30 } })
  async getAvailableDates(
    @Param('clubSlug') clubSlug: string,
    @Query('serviceId') serviceId: string,
    @Query('staffId') staffId: string,
    @Query('from') from: string,
    @Query('to') to: string,
  ) {
    if (!serviceId || !staffId || !from || !to) {
      throw new BadRequestException('serviceId, staffId, from, to are required');
    }

    const club = await this.resolveClub(clubSlug);

    return this.availability.getAvailableDates({
      clubId: club.id,
      serviceId,
      staffId,
      from: new Date(from),
      to: new Date(to),
    });
  }

  // ─── Available time slots ──────────────────────────────

  @Get(':clubSlug/times')
  @ApiOperation({ summary: 'Get available time slots for a date' })
  @ApiQuery({ name: 'serviceId', type: String })
  @ApiQuery({ name: 'staffId', type: String })
  @ApiQuery({ name: 'date', type: String, example: '2026-03-15' })
  @Throttle({ default: { ttl: 60000, limit: 30 } })
  async getAvailableSlots(
    @Param('clubSlug') clubSlug: string,
    @Query('serviceId') serviceId: string,
    @Query('staffId') staffId: string,
    @Query('date') date: string,
  ) {
    if (!serviceId || !staffId || !date) {
      throw new BadRequestException('serviceId, staffId, date are required');
    }

    const club = await this.resolveClub(clubSlug);

    return this.availability.getAvailableSlots({
      clubId: club.id,
      serviceId,
      staffId,
      date: new Date(date),
    });
  }

  // ─── Group events ──────────────────────────────────────

  @Get(':clubSlug/group-events')
  @ApiOperation({ summary: 'List upcoming group events' })
  @ApiQuery({ name: 'from', required: false })
  @ApiQuery({ name: 'to', required: false })
  @ApiQuery({ name: 'serviceId', required: false })
  @Throttle({ default: { ttl: 60000, limit: 30 } })
  async getGroupEvents(
    @Param('clubSlug') clubSlug: string,
    @Query('from') from?: string,
    @Query('to') to?: string,
    @Query('serviceId') serviceId?: string,
  ) {
    const club = await this.resolveClub(clubSlug);

    const now = new Date();
    const where: Record<string, any> = {
      clubId: club.id,
      isCancelled: false,
      startAt: { gte: from ? new Date(from) : now },
    };

    // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
    if (to) where.startAt.lte = new Date(to + 'T23:59:59');
    if (serviceId) where.serviceId = serviceId;

    return this.prisma.bookingGroupEvent.findMany({
      where,
      select: {
        id: true,
        title: true,
        startAt: true,
        endAt: true,
        capacity: true,
        bookedCount: true,
        service: { select: { id: true, name: true, duration: true, price: true, color: true } },
        staff: { select: { id: true, firstName: true, lastName: true, avatarUrl: true } },
        room: { select: { id: true, name: true } },
      },
      orderBy: { startAt: 'asc' },
      take: 100,
    });
  }

  // ─── Book appointment ──────────────────────────────────

  @Post(':clubSlug/book')
  @ApiOperation({ summary: 'Create a booking from the widget' })
  @UseGuards(ApiKeyGuard)
  @Throttle({ default: { ttl: 60000, limit: 10 } })
  async book(@Param('clubSlug') clubSlug: string, @Body() dto: PublicBookDto) {
    // Honeypot check — bot auto-fills this field
    if (dto.website) {
      this.logger.warn(`Honeypot triggered for ${dto.clientPhone} on ${clubSlug}`);
      // Return fake success to not tip off the bot
      return {
        success: true,
        recordNumber: Math.floor(Math.random() * 10000),
        message: 'Запись создана',
      };
    }

    const club = await this.resolveClub(clubSlug);

    const record = await this.records.create(club.id, {
      clientName: dto.clientName,
      clientPhone: dto.clientPhone,
      clientEmail: dto.clientEmail,
      serviceId: dto.serviceId,
      staffId: dto.staffId,
      groupEventId: dto.groupEventId,
      startAt: dto.startAt,
      places: dto.places,
      source: 'WIDGET',
      notes: dto.comment,
      metadata: {
        ...dto.metadata,
        source: 'widget',
        clubSlug,
      },
    });

    return {
      success: true,
      recordNumber: record.number,
      recordId: record.id,
      message: 'Запись создана. Ожидайте подтверждения.',
      startAt: record.startAt,
      service: record.service?.name,
      staff: record.staff ? `${record.staff.firstName} ${record.staff.lastName}` : undefined,
    };
  }

  // ─── Phone verification ────────────────────────────────

  @Post(':clubSlug/verify-phone')
  @ApiOperation({ summary: 'Send phone verification code' })
  @Throttle({ default: { ttl: 60000, limit: 3 } })
  async verifyPhone(@Param('clubSlug') clubSlug: string, @Body() dto: PublicVerifyPhoneDto) {
    await this.resolveClub(clubSlug);

    // TODO: integrate with SMS provider (SMS.ru, SMSC, etc.)
    // For now, log and return success
    this.logger.log(`Phone verification requested: ${dto.phone} for ${clubSlug}`);

    return {
      success: true,
      message: 'Код отправлен на указанный номер',
      expiresIn: 300, // 5 minutes
    };
  }

  // ─── Helper ────────────────────────────────────────────

  private async resolveClub(slug: string) {
    const club = await this.prisma.club.findUnique({
      where: { slug },
      select: { id: true, name: true, slug: true },
    });

    if (!club) throw new NotFoundException('Club not found');

    return club;
  }
}
