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

import { SponsorshipRequest } from '../../domain/entities/SponsorshipRequest';
import type { ISponsorshipRequestRepository } from '../../domain/repositories/ISponsorshipRequestRepository';
import type { ISponsorshipPricingRepository } from '../../domain/repositories/ISponsorshipPricingRepository';
import type { ISponsorRepository } from '../../domain/repositories/ISponsorRepository';
import { Money } from '../../domain/value-objects/Money';
import type { AsyncUseCase } from '@core/shared/application';
import { Result } from '@core/shared/application/Result';
import type { Logger } from '@core/shared/application';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { ApplyForSponsorshipDTO } from '../dto/ApplyForSponsorshipDTO';
import type { ApplyForSponsorshipResultDTO } from '../dto/ApplyForSponsorshipResultDTO';

export class ApplyForSponsorshipUseCase
  implements AsyncUseCase<ApplyForSponsorshipDTO, ApplyForSponsorshipResultDTO, string>
{
  constructor(
    private readonly sponsorshipRequestRepo: ISponsorshipRequestRepository,
    private readonly sponsorshipPricingRepo: ISponsorshipPricingRepository,
    private readonly sponsorRepo: ISponsorRepository,
    private readonly logger: Logger,
  ) {}

  async execute(dto: ApplyForSponsorshipDTO): Promise<Result<ApplyForSponsorshipResultDTO, ApplicationErrorCode<string>>> {
    this.logger.debug('Attempting to apply for sponsorship', { dto });

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

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

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

    // Check if the requested tier slot is available
    const slotAvailable = pricing.isSlotAvailable(dto.tier);
    if (!slotAvailable) {
      this.logger.warn(`No ${dto.tier} sponsorship slots are available for entity ${dto.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(
      dto.sponsorId,
      dto.entityType,
      dto.entityId,
    );
    if (hasPending) {
      this.logger.warn('Sponsor already has a pending request for this entity', { sponsorId: dto.sponsorId, entityType: dto.entityType, entityId: dto.entityId });
      return Result.err({ code: 'PENDING_REQUEST_EXISTS' });
    }

    // Validate offered amount meets minimum price
    const minPrice = pricing.getPrice(dto.tier);
    if (minPrice && dto.offeredAmount < minPrice.amount) {
      this.logger.warn(`Offered amount ${dto.offeredAmount} is less than minimum ${minPrice.amount} for entity ${dto.entityId}, tier ${dto.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 offeredAmount = Money.create(dto.offeredAmount, dto.currency ?? 'USD');

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

    await this.sponsorshipRequestRepo.create(request);

    return Result.ok({
      requestId: request.id,
      status: 'pending',
      createdAt: request.createdAt,
    });
  }
}