import { AdminVoteOutcome, AdminVoteSession } from '../../domain/entities/AdminVoteSession';
import { AdminVoteSessionRepository } from '../../domain/repositories/AdminVoteSessionRepository';
import { RatingEventRepository } from '../../domain/repositories/RatingEventRepository';
import { UserRatingRepository } from '../../domain/repositories/UserRatingRepository';
import { RatingEventFactory } from '../../domain/services/RatingEventFactory';
import { RatingSnapshotCalculator } from '../../domain/services/RatingSnapshotCalculator';
import { CloseAdminVoteSessionInput, CloseAdminVoteSessionOutput } from '../dtos/AdminVoteSessionDto';

/**
 * Use Case: CloseAdminVoteSessionUseCase
 * 
 * Closes an admin vote session and generates rating events.
 * This is the key use case that triggers events on close per plans section 7.1.1.
 * 
 * Flow:
 * 1. Load and validate session
 * 2. Close session and calculate outcome
 * 3. Create rating events from outcome
 * 4. Append events to ledger for each affected admin
 * 5. Recompute snapshots
 * 
 * Per plans section 7.1.1 and 10.2
 */
export class CloseAdminVoteSessionUseCase {
  constructor(
    private readonly adminVoteSessionRepository: AdminVoteSessionRepository,
    private readonly ratingEventRepository: RatingEventRepository,
    private readonly userRatingRepository: UserRatingRepository,
  ) {}

  async execute(input: CloseAdminVoteSessionInput): Promise<CloseAdminVoteSessionOutput> {
    try {
      // Validate input
      const errors = this.validateInput(input);
      if (errors.length > 0) {
        return {
          success: false,
          voteSessionId: input.voteSessionId,
          errors,
        };
      }

      // Load the vote session
      const session = await this.adminVoteSessionRepository.findById(input.voteSessionId);
      if (!session) {
        return {
          success: false,
          voteSessionId: input.voteSessionId,
          errors: ['Vote session not found'],
        };
      }

      // Validate admin ownership
      if (session.adminId !== input.adminId) {
        return {
          success: false,
          voteSessionId: input.voteSessionId,
          errors: ['Admin does not own this vote session'],
        };
      }

      // Check if already closed
      if (session.closed) {
        return {
          success: false,
          voteSessionId: input.voteSessionId,
          errors: ['Vote session is already closed'],
        };
      }

      // Check if within voting window
      const now = new Date();
      if (now < session.startDate || now > session.endDate) {
        return {
          success: false,
          voteSessionId: input.voteSessionId,
          errors: ['Cannot close session outside the voting window'],
        };
      }

      // Close session and calculate outcome
      const outcome = session.close();

      // Save closed session
      await this.adminVoteSessionRepository.save(session);

      // Create rating events from outcome
      // Per plans: events are created for the admin being voted on
      const eventsCreated = await this.createRatingEvents(session, outcome);

      return {
        success: true,
        voteSessionId: input.voteSessionId,
        outcome: {
          percentPositive: outcome.percentPositive,
          count: outcome.count,
          eligibleVoterCount: outcome.eligibleVoterCount,
          participationRate: outcome.participationRate,
          outcome: outcome.outcome,
        },
        eventsCreated,
      };

    } catch (error) {
      const errorMsg = error instanceof Error ? error.message : 'Unknown error';
      return {
        success: false,
        voteSessionId: input.voteSessionId,
        errors: [`Failed to close vote session: ${errorMsg}`],
      };
    }
  }

  /**
   * Create rating events from vote outcome
   * Events are created for the admin being voted on
   * Per plans: no events are created for tie outcomes
   */
  private async createRatingEvents(session: AdminVoteSession, outcome: AdminVoteOutcome): Promise<number> {
    let eventsCreated = 0;

    // Don't create events for tie outcomes
    if (outcome.outcome === 'tie') {
      return 0;
    }

    // Use RatingEventFactory to create vote outcome events
    const voteInput = {
      userId: session.adminId, // The admin being voted on
      voteSessionId: session.id,
      outcome: outcome.outcome as 'positive' | 'negative',
      voteCount: outcome.count.total,
      eligibleVoterCount: outcome.eligibleVoterCount,
      percentPositive: outcome.percentPositive,
    };

    const events = RatingEventFactory.createFromVote(voteInput);

    // Save each event to ledger
    for (const event of events) {
      await this.ratingEventRepository.save(event);
      eventsCreated++;
    }

    // Recompute snapshot for the admin
    if (eventsCreated > 0) {
      const allEvents = await this.ratingEventRepository.getAllByUserId(session.adminId);
      const snapshot = RatingSnapshotCalculator.calculate(session.adminId, allEvents);
      await this.userRatingRepository.save(snapshot);
    }

    return eventsCreated;
  }

  private validateInput(input: CloseAdminVoteSessionInput): string[] {
    const errors: string[] = [];

    if (!input.voteSessionId || input.voteSessionId.trim().length === 0) {
      errors.push('voteSessionId is required');
    }

    if (!input.adminId || input.adminId.trim().length === 0) {
      errors.push('adminId is required');
    }

    return errors;
  }
}
