/**
 * Use Case: ApplyForSponsorshipUseCase
 *
 * Allows a sponsor to apply for a sponsorship slot on any entity
 * (driver, team, race, or season/league).
 */

import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { SponsorshipRequest } from '../../domain/entities/SponsorshipRequest';
import { SponsorRepository } from '../../domain/repositories/SponsorRepository';
import { SponsorshipPricingRepository } from '../../domain/repositories/SponsorshipPricingRepository';
import { SponsorshipRequestRepository } from '../../domain/repositories/SponsorshipRequestRepository';
import { Money, isCurrency } from '../../domain/value-objects/Money';

export interface ApplyForSponsorshipInput {
  sponsorId: string;
  entityType: SponsorshipRequest['entityType'];
  entityId: string;
  tier: string;
  offeredAmount: number;
  currency?: string;
  message?: string;
}

export interface ApplyForSponsorshipResult {
  requestId: string;
  status: SponsorshipRequest['status'];
  createdAt: Date;
}

export class ApplyForSponsorshipUseCase {
  constructor(
    private readonly sponsorshipRequestRepo: SponsorshipRequestRepository,
    private readonly sponsorshipPricingRepo: SponsorshipPricingRepository,
    private readonly sponsorRepo: SponsorRepository,
    private readonly logger: Logger,
  ) {}

  async execute(
    input: ApplyForSponsorshipInput,
  ): Promise<
    Result<
      ApplyForSponsorshipResult,
      ApplicationErrorCode<
        | 'SPONSOR_NOT_FOUND'
        | 'SPONSORSHIP_PRICING_NOT_SETUP'
        | 'ENTITY_NOT_ACCEPTING_APPLICATIONS'
        | 'NO_SLOTS_AVAILABLE'
        | 'PENDING_REQUEST_EXISTS'
        | 'OFFERED_AMOUNT_TOO_LOW'
        | 'VALIDATION_ERROR'
      >
    >
  > {
    this.logger.debug('Attempting to apply for sponsorship', { input });

    // Validate sponsor exists
    const sponsor = await this.sponsorRepo.findById(input.sponsorId);
    if (!sponsor) {
      this.logger.error('Sponsor not found', undefined, { sponsorId: input.sponsorId });
      return Result.err({ code: 'SPONSOR_NOT_FOUND' });
    }

    // Check if entity accepts sponsorship applications
    const pricing = await this.sponsorshipPricingRepo.findByEntity(input.entityType, input.entityId);
    if (!pricing) {
      this.logger.warn('Sponsorship pricing not set up for this entity', {
        entityType: input.entityType,
        entityId: input.entityId,
      });
      return Result.err({ code: 'SPONSORSHIP_PRICING_NOT_SETUP' });
    }

    if (!pricing.acceptingApplications) {
      this.logger.warn('Entity not accepting sponsorship applications', {
        entityType: input.entityType,
        entityId: input.entityId,
      });
      return Result.err({ code: 'ENTITY_NOT_ACCEPTING_APPLICATIONS' });
    }

    // Validate tier type
    const tier = input.tier as 'main' | 'secondary';
    if (tier !== 'main' && tier !== 'secondary') {
      this.logger.warn('Invalid tier', { tier: input.tier });
      return Result.err({ code: 'VALIDATION_ERROR' });
    }

    // Check if the requested tier slot is available
    const slotAvailable = pricing.isSlotAvailable(tier);
    if (!slotAvailable) {
      this.logger.warn(`No ${tier} sponsorship slots are available for entity ${input.entityId}`);
      return Result.err({ code: 'NO_SLOTS_AVAILABLE' });
    }

    // Check if sponsor already has a pending request for this entity
    const hasPending = await this.sponsorshipRequestRepo.hasPendingRequest(
      input.sponsorId,
      input.entityType,
      input.entityId,
    );
    if (hasPending) {
      this.logger.warn('Sponsor already has a pending request for this entity', {
        sponsorId: input.sponsorId,
        entityType: input.entityType,
        entityId: input.entityId,
      });
      return Result.err({ code: 'PENDING_REQUEST_EXISTS' });
    }

    // Validate offered amount meets minimum price
    const minPrice = pricing.getPrice(tier);
    if (minPrice && input.offeredAmount < minPrice.amount) {
      this.logger.warn(
        `Offered amount ${input.offeredAmount} is less than minimum ${minPrice.amount} for entity ${input.entityId}, tier ${tier}`,
      );
      return Result.err({ code: 'OFFERED_AMOUNT_TOO_LOW' });
    }

    // Create the sponsorship request
    const requestId = `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    const currency =
      input.currency !== undefined && isCurrency(input.currency) ? input.currency : 'USD';
    const offeredAmount = Money.create(input.offeredAmount, currency);

    const request = SponsorshipRequest.create({
      id: requestId,
      sponsorId: input.sponsorId,
      entityType: input.entityType,
      entityId: input.entityId,
      tier,
      offeredAmount,
      ...(input.message !== undefined ? { message: input.message } : {}),
    });

    await this.sponsorshipRequestRepo.create(request);

    const result: ApplyForSponsorshipResult = {
      requestId: request.id,
      status: request.status,
      createdAt: request.createdAt,
    };

    return Result.ok(result);
  }
}