import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
import { PrismaService } from '../../../prisma/prisma.service';
import {
  CreateBookingServiceDto,
  UpdateBookingServiceDto,
  CreateServiceCategoryDto,
  UpdateServiceCategoryDto,
  ReorderServicesDto,
} from '../dto/create-service.dto';

const SERVICE_INCLUDE = {
  category: { select: { id: true, name: true } },
  staff: {
    select: {
      staff: { select: { id: true, firstName: true, lastName: true, avatarUrl: true } },
    },
  },
  _count: { select: { records: true, events: true } },
};

const CATEGORY_INCLUDE = {
  children: { select: { id: true, name: true, position: true } },
  _count: { select: { services: true } },
};

@Injectable()
export class BookingServicesService {
  constructor(private readonly prisma: PrismaService) {}

  // ─── Categories ─────────────────────────────────────────────

  async findAllCategories(clubId: string) {
    return this.prisma.bookingServiceCategory.findMany({
      where: { clubId, parentId: null },
      include: CATEGORY_INCLUDE,
      orderBy: { position: 'asc' },
    });
  }

  async createCategory(clubId: string, dto: CreateServiceCategoryDto) {
    const maxPos = await this.prisma.bookingServiceCategory.aggregate({
      where: { clubId, parentId: dto.parentId ?? null },
      _max: { position: true },
    });
    return this.prisma.bookingServiceCategory.create({
      data: {
        clubId,
        name: dto.name,
        parentId: dto.parentId,
        position: (maxPos._max.position ?? -1) + 1,
      },
      include: CATEGORY_INCLUDE,
    });
  }

  async updateCategory(id: string, dto: UpdateServiceCategoryDto) {
    await this.findCategoryOrFail(id);
    return this.prisma.bookingServiceCategory.update({
      where: { id },
      data: dto,
      include: CATEGORY_INCLUDE,
    });
  }

  async deleteCategory(id: string) {
    const cat = await this.prisma.bookingServiceCategory.findUnique({
      where: { id },
      include: { _count: { select: { services: true, children: true } } },
    });
    if (!cat) throw new NotFoundException(`Category ${id} not found`);
    if (cat._count.services > 0 || cat._count.children > 0) {
      throw new BadRequestException('Cannot delete category with services or subcategories');
    }
    await this.prisma.bookingServiceCategory.delete({ where: { id } });
  }

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

  async findAll(clubId: string, activeOnly = false) {
    return this.prisma.bookingService.findMany({
      where: { clubId, ...(activeOnly ? { isActive: true } : {}) },
      include: SERVICE_INCLUDE,
      orderBy: [{ position: 'asc' }, { name: 'asc' }],
    });
  }

  async findById(id: string) {
    const service = await this.prisma.bookingService.findUnique({
      where: { id },
      include: SERVICE_INCLUDE,
    });
    if (!service) throw new NotFoundException(`Service ${id} not found`);
    return service;
  }

  async create(clubId: string, dto: CreateBookingServiceDto) {
    const { staffIds, ...data } = dto;

    const maxPos = await this.prisma.bookingService.aggregate({
      where: { clubId },
      _max: { position: true },
    });

    const service = await this.prisma.bookingService.create({
      data: {
        clubId,
        ...data,
        position: (maxPos._max.position ?? -1) + 1,
        ...(staffIds?.length
          ? {
              staff: {
                createMany: {
                  data: staffIds.map((staffId) => ({ staffId })),
                },
              },
            }
          : {}),
      },
      include: SERVICE_INCLUDE,
    });
    return service;
  }

  async update(id: string, dto: UpdateBookingServiceDto) {
    await this.findById(id);
    const { staffIds, ...data } = dto;

    if (staffIds !== undefined) {
      await this.prisma.bookingServiceStaff.deleteMany({ where: { serviceId: id } });
      if (staffIds.length > 0) {
        await this.prisma.bookingServiceStaff.createMany({
          data: staffIds.map((staffId) => ({ serviceId: id, staffId })),
        });
      }
    }

    return this.prisma.bookingService.update({
      where: { id },
      data,
      include: SERVICE_INCLUDE,
    });
  }

  async delete(id: string) {
    const service = await this.prisma.bookingService.findUnique({
      where: { id },
      include: { _count: { select: { records: true, events: true } } },
    });
    if (!service) throw new NotFoundException(`Service ${id} not found`);
    if (service._count.records > 0) {
      throw new BadRequestException('Cannot delete service with existing bookings');
    }
    await this.prisma.bookingService.delete({ where: { id } });
  }

  async reorder(clubId: string, dto: ReorderServicesDto) {
    const updates = dto.serviceIds.map((id, index) =>
      this.prisma.bookingService.update({
        where: { id },
        data: { position: index },
      }),
    );
    return this.prisma.$transaction(updates);
  }

  // ─── Helpers ────────────────────────────────────────────────

  private async findCategoryOrFail(id: string) {
    const cat = await this.prisma.bookingServiceCategory.findUnique({ where: { id } });
    if (!cat) throw new NotFoundException(`Category ${id} not found`);
    return cat;
  }
}
