import { Injectable, Logger } from '@nestjs/common';
import { Program, ProgramSession } from '../entities';
import { RegistrationLevelEnum } from '../enum/program.enums';

/**
 * Interface for seat availability check result
 */
export interface SeatAvailabilityResult {
  // Core capacity information
  limitedSeats: boolean;
  totalSeats: number;
  filledSeats: number;
  seatsRemaining: number;

  // Waitlist configuration
  waitlistApplicable: boolean;
  waitlistTriggerCount: number;

  // Status flags
  isWaitlistTriggered: boolean;
  isRegistrationsExceeded: boolean; // ONLY true when limitedSeats=true, waitlistApplicable=false, and exceeded

  // Decision outcomes
  canRegister: boolean;
  shouldWaitlist: boolean;
  shouldReject: boolean; // ONLY true when exceeded and waitlist NOT applicable

  // Error/status messages
  statusMessage: string;
  capacityStatus: 'NORMAL' | 'WAITLIST' | 'EXCEEDED' | 'UNLIMITED';
}

/**
 * Centralized service for checking seat availability and waitlist logic
 * Implements the comprehensive seat availability rules across all enforcement points:
 * 1. Registration level (Create + Edit)
 * 2. Approval level
 * 3. Payment level
 * 
 * IMPORTANT: isRegistrationsExceeded flag is ONLY used when:
 * - limitedSeats = true
 * - waitlistApplicable = false
 * - filledSeats > totalSeats
 * 
 * When waitlist is applicable, overflow goes to waitlist (never exceeded state)
 */
@Injectable()
export class SeatAvailabilityService {
  private readonly logger = new Logger(SeatAvailabilityService.name);

  /**
   * Main method to check seat availability based on your exact specification
   * 
   * @param program - Program entity with seat configuration
   * @param session - Optional session entity (for session-level registration)
   * @param registrationLevel - 'program' or 'session'
   * @returns SeatAvailabilityResult with all decision flags
   */
  async checkSeatAvailability(
    program: Program,
    session: ProgramSession | null,
    registrationLevel: string,
  ): Promise<SeatAvailabilityResult> {
    this.logger.debug('Checking seat availability', {
      programId: program.id,
      sessionId: session?.id,
      registrationLevel,
    });

    // Determine which entity to check based on registration level
    const seatsInfo = registrationLevel === RegistrationLevelEnum.SESSION ? session : program;

    // Extract input flags
    const limitedSeats = seatsInfo?.limitedSeats === true;
    const waitlistApplicable = seatsInfo?.waitlistApplicable === true;
    const totalSeats = seatsInfo?.totalSeats || 0;
    const waitlistTriggerCount = seatsInfo?.waitlistTriggerCount || 0;

    // Get filled seats (only Program entity has filledSeats, ProgramSession uses reservedSeats)
    const filledSeats =
      registrationLevel === RegistrationLevelEnum.SESSION
        ? session?.reservedSeats || 0
        : program?.filledSeats || 0;

    this.logger.debug('Seat configuration', {
      limitedSeats,
      waitlistApplicable,
      totalSeats,
      waitlistTriggerCount,
      filledSeats,
    });

    // CASE 1: limitedSeats = FALSE (Unlimited seats)
    if (!limitedSeats) {
      return this.buildUnlimitedResult(totalSeats, filledSeats);
    }

    // CASE 2: limitedSeats = TRUE AND waitlistApplicable = FALSE
    if (!waitlistApplicable) {
      return this.buildLimitedNoWaitlistResult(totalSeats, filledSeats);
    }

    // CASE 3: limitedSeats = TRUE AND waitlistApplicable = TRUE
    return this.buildLimitedWithWaitlistResult(
      totalSeats,
      filledSeats,
      waitlistTriggerCount,
      program,
    );
  }

  /**
   * CASE 1: Unlimited seats scenario
   */
  private buildUnlimitedResult(
    totalSeats: number,
    filledSeats: number,
  ): SeatAvailabilityResult {
    return {
      limitedSeats: false,
      totalSeats,
      filledSeats,
      seatsRemaining: -1, // Unlimited

      waitlistApplicable: false,
      waitlistTriggerCount: 0,

      isWaitlistTriggered: false,
      isRegistrationsExceeded: false,

      canRegister: true,
      shouldWaitlist: false,
      shouldReject: false,

      statusMessage: 'Unlimited registration allowed',
      capacityStatus: 'UNLIMITED',
    };
  }

  /**
   * CASE 2: Limited seats WITHOUT waitlist
   * - Normal if filledSeats <= totalSeats
   * - Exceeded if filledSeats > totalSeats (must throw error at approval/payment)
   */
  private buildLimitedNoWaitlistResult(
    totalSeats: number,
    filledSeats: number,
  ): SeatAvailabilityResult {
    const seatsRemaining = totalSeats - filledSeats;
    const isExceeded = filledSeats > totalSeats;

    if (isExceeded) {
      // CASE 2B: Seats exceeded
      return {
        limitedSeats: true,
        totalSeats,
        filledSeats,
        seatsRemaining,

        waitlistApplicable: false,
        waitlistTriggerCount: 0,

        isWaitlistTriggered: false,
        isRegistrationsExceeded: true,

        canRegister: false,
        shouldWaitlist: false,
        shouldReject: true, // Must throw error

        statusMessage: 'Seats exceeded - registration not allowed',
        capacityStatus: 'EXCEEDED',
      };
    }

    // CASE 2A: Seats within limit
    return {
      limitedSeats: true,
      totalSeats,
      filledSeats,
      seatsRemaining,

      waitlistApplicable: false,
      waitlistTriggerCount: 0,

      isWaitlistTriggered: false,
      isRegistrationsExceeded: false,

      canRegister: filledSeats < totalSeats,
      shouldWaitlist: false,
      shouldReject: false,

      statusMessage: 'Normal registration allowed',
      capacityStatus: 'NORMAL',
    };
  }

  /**
   * CASE 3: Limited seats WITH waitlist
   * Three sub-cases based on waitlist trigger and capacity
   * NOTE: When waitlist is applicable, isRegistrationsExceeded is ALWAYS false
   *       Waitlist handles all overflow scenarios
   */
  private buildLimitedWithWaitlistResult(
    totalSeats: number,
    filledSeats: number,
    waitlistTriggerCount: number,
    program?: Program,
  ): SeatAvailabilityResult {
    const seatsRemaining = totalSeats - filledSeats;

    // Check capacity status
    const capacityOK = filledSeats <= totalSeats;
    const capacityExceeded = filledSeats > totalSeats;

    // Check waitlist trigger
    const waitlistTriggered =
      program && program.isWaitlistTriggered
        ? program.isWaitlistTriggered
        : seatsRemaining < waitlistTriggerCount;

    // CASE 3C: Seats exceeded (overflow) - must waitlist
    // When waitlist is applicable, we NEVER mark as exceeded - waitlist handles overflow
    if (capacityExceeded) {
      return {
        limitedSeats: true,
        totalSeats,
        filledSeats,
        seatsRemaining,

        waitlistApplicable: true,
        waitlistTriggerCount,

        isWaitlistTriggered: true,
        isRegistrationsExceeded: false, // ALWAYS false when waitlist applicable

        canRegister: true, // Can still register via waitlist
        shouldWaitlist: true,
        shouldReject: false,

        statusMessage: 'Capacity exceeded - registration will be waitlisted',
        capacityStatus: 'WAITLIST',
      };
    }

    // CASE 3B: Seats within limit BUT waitlist threshold reached
    if (capacityOK && waitlistTriggered) {
      return {
        limitedSeats: true,
        totalSeats,
        filledSeats,
        seatsRemaining,

        waitlistApplicable: true,
        waitlistTriggerCount,

        isWaitlistTriggered: true,
        isRegistrationsExceeded: false,

        canRegister: true,
        shouldWaitlist: true,
        shouldReject: false,

        statusMessage: 'Waitlist triggered - new registrations will be waitlisted',
        capacityStatus: 'WAITLIST',
      };
    }

    // CASE 3A: Seats available AND waitlist not triggered yet
    return {
      limitedSeats: true,
      totalSeats,
      filledSeats,
      seatsRemaining,

      waitlistApplicable: true,
      waitlistTriggerCount,

      isWaitlistTriggered: false,
      isRegistrationsExceeded: false,

      canRegister: true,
      shouldWaitlist: false,
      shouldReject: false,

      statusMessage: 'Normal registration allowed',
      capacityStatus: 'NORMAL',
    };
  }

  /**
   * Validate at Registration Level (Point 1)
   * Called during registration create/edit
   */
  async validateAtRegistrationLevel(
    program: Program,
    session: ProgramSession | null,
    registrationLevel: string,
  ): Promise<SeatAvailabilityResult> {
    this.logger.log('[POINT 1: Registration Level] Validating seat availability');
    return this.checkSeatAvailability(program, session, registrationLevel);
  }

  /**
   * Validate at Approval Level (Point 2)
   * Called during approval action
   * For grouped programs, allocation is decided here
   */
  async validateAtApprovalLevel(
    program: Program,
    session: ProgramSession | null,
    registrationLevel: string,
    allocatedProgram?: Program,
    allocatedSession?: ProgramSession,
  ): Promise<SeatAvailabilityResult> {
    this.logger.log('[POINT 2: Approval Level] Validating seat availability');

    // For grouped programs, check allocated target
    if (allocatedProgram || allocatedSession) {
      const targetProgram = allocatedProgram || program;
      const targetSession = allocatedSession || session;
      const targetLevel = allocatedProgram ? RegistrationLevelEnum.PROGRAM : registrationLevel;

      return this.checkSeatAvailability(targetProgram, targetSession, targetLevel);
    }

    // Normal approval check
    return this.checkSeatAvailability(program, session, registrationLevel);
  }

  /**
   * Validate at Payment Level (Point 3)
   * Called before marking payment success
   */
  async validateAtPaymentLevel(
    program: Program,
    session: ProgramSession | null,
    registrationLevel: string,
  ): Promise<SeatAvailabilityResult> {
    this.logger.log('[POINT 3: Payment Level] Validating seat availability');
    return this.checkSeatAvailability(program, session, registrationLevel);
  }

  /**
   * Helper to determine if error should be thrown
   * Used at Approval and Payment levels
   */
  shouldThrowSeatsExceededError(result: SeatAvailabilityResult): boolean {
    return result.shouldReject && result.isRegistrationsExceeded;
  }

  /**
   * Get error message for seats exceeded scenario
   */
  getSeatsExceededErrorMessage(): string {
    return 'No seats available - capacity exceeded and waitlist not applicable';
  }
}
