import {
  BadRequestException,
  ForbiddenException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { BookingStatus, Prisma, SpaceType, PricingType } from '@prisma/client';
import { PrismaService } from '../../prisma/prisma.service';
import { AuthenticatedUser } from '../../common/decorators/current-user.decorator';

interface CreateSpaceInput {
  complexId: string;
  name: string;
  type?: SpaceType;
  description?: string;
  capacity?: number;
  hourlyFee?: number;
  flatFee?: number;
  priceType?: PricingType;
  imageUrl?: string;
  openTime?: string;
  closeTime?: string;
  isBookable?: boolean;
  amenities?: string[];
  rules?: string;
}

interface RequestBookingInput {
  spaceId: string;
  startAt: Date;
  endAt: Date;
  notes?: string;
}

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

  // ---------- Spaces (admin) ----------

  async createSpace(user: AuthenticatedUser, dto: CreateSpaceInput) {
    if (!user.tenantId) throw new BadRequestException('Tenant required');
    const complex = await this.prisma.complex.findFirst({
      where: { id: dto.complexId, tenantId: user.tenantId },
    });
    if (!complex) throw new NotFoundException('Complex not found');
    return this.prisma.commonSpace.create({
      data: {
        complexId: dto.complexId,
        name: dto.name,
        type: dto.type ?? SpaceType.AUTRE,
        description: dto.description,
        capacity: dto.capacity,
        priceType: dto.priceType ?? PricingType.FREE,
        hourlyFee: dto.hourlyFee != null ? new Prisma.Decimal(dto.hourlyFee) : undefined,
        flatFee: dto.flatFee != null ? new Prisma.Decimal(dto.flatFee) : undefined,
        imageUrl: dto.imageUrl,
        isBookable: dto.isBookable ?? true,
        openTime: dto.openTime,
        closeTime: dto.closeTime,
        amenities: dto.amenities ?? [],
        rules: dto.rules,
      },
    });
  }

  listSpaces(user: AuthenticatedUser, complexId?: string) {
    if (!user.tenantId && !user.isSuperAdmin) return [];
    return this.prisma.commonSpace.findMany({
      where: {
        ...(complexId && { complexId }),
        ...(user.tenantId && { complex: { tenantId: user.tenantId } }),
        enabled: true,
      },
      orderBy: { name: 'asc' },
    });
  }

  // ---------- Bookings ----------

  async request(user: AuthenticatedUser, dto: RequestBookingInput) {
    const space = await this.prisma.commonSpace.findUnique({
      where: { id: dto.spaceId },
      include: { complex: true },
    });
    if (!space) throw new NotFoundException('Space not found');
    if (!user.isSuperAdmin && space.complex.tenantId !== user.tenantId) {
      throw new ForbiddenException();
    }
    if (dto.startAt >= dto.endAt) throw new BadRequestException('startAt must be before endAt');
    if (dto.startAt < new Date()) throw new BadRequestException('Cannot book in the past');

    // Conflict detection
    const conflict = await this.prisma.booking.findFirst({
      where: {
        spaceId: dto.spaceId,
        status: { in: [BookingStatus.REQUESTED, BookingStatus.CONFIRMED] },
        startAt: { lt: dto.endAt },
        endAt: { gt: dto.startAt },
      },
    });
    if (conflict) throw new BadRequestException('Time slot conflicts with an existing booking');

    const hours = (dto.endAt.getTime() - dto.startAt.getTime()) / 3600000;
    
    let fee = 0;
    if (space.priceType === PricingType.HOURLY) {
      fee = Number(space.hourlyFee) * hours;
    } else if (space.priceType === PricingType.FLAT_RATE) {
      fee = Number(space.flatFee || 0);
    }

    return this.prisma.booking.create({
      data: {
        spaceId: dto.spaceId,
        userId: user.userId,
        startAt: dto.startAt,
        endAt: dto.endAt,
        totalFee: new Prisma.Decimal(fee.toFixed(2)),
        notes: dto.notes,
      },
    });
  }

  async confirm(user: AuthenticatedUser, id: string) {
    const b = await this.prisma.booking.findUnique({
      where: { id },
      include: { space: { include: { complex: true } } },
    });
    if (!b) throw new NotFoundException();
    if (!user.isSuperAdmin && b.space.complex.tenantId !== user.tenantId) throw new ForbiddenException();
    return this.prisma.booking.update({
      where: { id },
      data: { status: BookingStatus.CONFIRMED },
    });
  }

  async cancel(user: AuthenticatedUser, id: string) {
    const b = await this.prisma.booking.findUnique({ where: { id } });
    if (!b) throw new NotFoundException();
    const privileged = user.roles.some((r) => ['SUPERADMIN', 'SYNDIC'].includes(r.code));
    if (b.userId !== user.userId && !privileged) throw new ForbiddenException();
    return this.prisma.booking.update({
      where: { id },
      data: { status: BookingStatus.CANCELLED },
    });
  }

  async list(user: AuthenticatedUser, filters: { spaceId?: string; mine?: boolean }) {
    const where: any = {};
    if (filters.spaceId) where.spaceId = filters.spaceId;
    if (filters.mine) where.userId = user.userId;
    else if (!user.roles.some((r) => ['SUPERADMIN', 'SYNDIC'].includes(r.code))) {
      where.userId = user.userId;
    }
    return this.prisma.booking.findMany({
      where,
      orderBy: { startAt: 'desc' },
      include: {
        space: { select: { id: true, name: true, type: true } },
        user: { select: { id: true, email: true, firstName: true } },
      },
      take: 100,
    });
  }
}
