/**
 * Use Case: AcceptSponsorshipRequestUseCase
 *
 * Allows an entity owner to accept a sponsorship request.
 * This creates an active sponsorship and notifies the sponsor.
 */

import type { NotificationService } from '@core/notifications/application/ports/NotificationService';
import type { IWalletRepository } from '@core/payments/domain/repositories/IWalletRepository';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import { SeasonSponsorship } from '../../domain/entities/season/SeasonSponsorship';
import type { ILeagueWalletRepository } from '../../domain/repositories/ILeagueWalletRepository';
import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository';
import type { ISeasonSponsorshipRepository } from '../../domain/repositories/ISeasonSponsorshipRepository';
import type { ISponsorshipRequestRepository } from '../../domain/repositories/ISponsorshipRequestRepository';

export interface AcceptSponsorshipRequestInput {
  requestId: string;
  respondedBy: string;
}

export interface AcceptSponsorshipResult {
  requestId: string;
  sponsorshipId: string;
  status: 'accepted';
  acceptedAt: Date;
  platformFee: number;
  netAmount: number;
}

export interface ProcessPaymentInput {
  amount: number;
  payerId: string;
  description: string;
  metadata?: Record<string, unknown>;
}

export interface ProcessPaymentResult {
  success: boolean;
  transactionId?: string;
  error?: string;
}

export class AcceptSponsorshipRequestUseCase {
  constructor(
    private readonly sponsorshipRequestRepo: ISponsorshipRequestRepository,
    private readonly seasonSponsorshipRepo: ISeasonSponsorshipRepository,
    private readonly seasonRepository: ISeasonRepository,
    private readonly notificationService: NotificationService,
    private readonly paymentProcessor: (input: ProcessPaymentInput) => Promise<ProcessPaymentResult>,
    private readonly walletRepository: IWalletRepository,
    private readonly leagueWalletRepository: ILeagueWalletRepository,
    private readonly logger: Logger,
    private readonly output: UseCaseOutputPort<AcceptSponsorshipResult>,
  ) {}

  async execute(
    input: AcceptSponsorshipRequestInput,
  ): Promise<
    Result<
      void,
      ApplicationErrorCode<
        | 'SPONSORSHIP_REQUEST_NOT_FOUND'
        | 'SPONSORSHIP_REQUEST_NOT_PENDING'
        | 'SEASON_NOT_FOUND'
        | 'PAYMENT_PROCESSING_FAILED'
        | 'SPONSOR_WALLET_NOT_FOUND'
        | 'LEAGUE_WALLET_NOT_FOUND'
      >
    >
  > {
    this.logger.debug(`Attempting to accept sponsorship request: ${input.requestId}`, {
      requestId: input.requestId,
      respondedBy: input.respondedBy,
    });

    // Find the request
    const request = await this.sponsorshipRequestRepo.findById(input.requestId);
    if (!request) {
      this.logger.warn(`Sponsorship request not found: ${input.requestId}`, { requestId: input.requestId });
      return Result.err({ code: 'SPONSORSHIP_REQUEST_NOT_FOUND' });
    }

    if (!request.isPending()) {
      this.logger.warn(`Cannot accept a ${request.status} sponsorship request: ${input.requestId}`, {
        requestId: input.requestId,
        status: request.status,
      });
      return Result.err({ code: 'SPONSORSHIP_REQUEST_NOT_PENDING' });
    }

    this.logger.info(`Sponsorship request ${input.requestId} found and is pending. Proceeding with acceptance.`, {
      requestId: input.requestId,
    });

    // Accept the request
    const acceptedRequest = request.accept(input.respondedBy);
    await this.sponsorshipRequestRepo.update(acceptedRequest);
    this.logger.debug(`Sponsorship request ${input.requestId} accepted and updated in repository.`, {
      requestId: input.requestId,
    });

    // If this is a season sponsorship, create the SeasonSponsorship record
    let sponsorshipId = `spons_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;

    if (request.entityType === 'season') {
      this.logger.debug(`Sponsorship request ${input.requestId} is for a season. Creating SeasonSponsorship record.`, {
        requestId: input.requestId,
        entityType: request.entityType,
      });
      const season = await this.seasonRepository.findById(request.entityId);
      if (!season) {
        this.logger.warn(
          `Season not found for sponsorship request ${input.requestId} and entityId ${request.entityId}`,
          { requestId: input.requestId, entityId: request.entityId },
        );
        return Result.err({ code: 'SEASON_NOT_FOUND' });
      }

      const sponsorship = SeasonSponsorship.create({
        id: sponsorshipId,
        seasonId: season.id,
        leagueId: season.leagueId,
        sponsorId: request.sponsorId,
        tier: request.tier,
        pricing: request.offeredAmount,
        status: 'active',
      });
      await this.seasonSponsorshipRepo.create(sponsorship);
      this.logger.info(`Season sponsorship ${sponsorshipId} created for request ${input.requestId}.`, {
        sponsorshipId,
        requestId: input.requestId,
      });

      // Notify the sponsor
      await this.notificationService.sendNotification({
        recipientId: request.sponsorId,
        type: 'sponsorship_request_accepted',
        title: 'Sponsorship Accepted',
        body: `Your sponsorship request for ${season.name} has been accepted.`,
        channel: 'in_app',
        urgency: 'toast',
        data: {
          requestId: request.id,
          sponsorshipId,
        },
      });

      // Process payment using clean input/output ports with primitive types
      const paymentInput: ProcessPaymentInput = {
        amount: request.offeredAmount.amount,
        payerId: request.sponsorId,
        description: `Sponsorship payment for ${request.entityType} ${request.entityId}`,
        metadata: { requestId: request.id },
      };

      const paymentResult = await this.paymentProcessor(paymentInput);
      if (!paymentResult.success) {
        this.logger.error(
          `Payment failed for sponsorship request ${request.id}: ${paymentResult.error}`,
          undefined,
          { requestId: request.id },
        );
        return Result.err({ code: 'PAYMENT_PROCESSING_FAILED' });
      }

      // Update wallets
      const sponsorWallet = await this.walletRepository.findById(request.sponsorId);
      if (!sponsorWallet) {
        this.logger.error(`Sponsor wallet not found for ${request.sponsorId}`, undefined, {
          sponsorId: request.sponsorId,
        });
        return Result.err({ code: 'SPONSOR_WALLET_NOT_FOUND' });
      }

      const leagueWallet = await this.leagueWalletRepository.findById(season.leagueId);
      if (!leagueWallet) {
        this.logger.error(`League wallet not found for ${season.leagueId}`, undefined, {
          leagueId: season.leagueId,
        });
        return Result.err({ code: 'LEAGUE_WALLET_NOT_FOUND' });
      }

      const netAmount = acceptedRequest.getNetAmount();

      // Deduct from sponsor wallet
      const updatedSponsorWallet = {
        ...sponsorWallet,
        balance: sponsorWallet.balance - request.offeredAmount.amount,
      };
      await this.walletRepository.update(updatedSponsorWallet);

      // Add to league wallet
      const updatedLeagueWallet = leagueWallet.addFunds(netAmount, paymentResult.transactionId!);
      await this.leagueWalletRepository.update(updatedLeagueWallet);
    }

    this.logger.info(`Sponsorship request ${acceptedRequest.id} successfully accepted.`, {
      requestId: acceptedRequest.id,
      sponsorshipId,
    });

    const result: AcceptSponsorshipResult = {
      requestId: acceptedRequest.id,
      sponsorshipId,
      status: 'accepted',
      acceptedAt: acceptedRequest.respondedAt!,
      platformFee: acceptedRequest.getPlatformFee().amount,
      netAmount: acceptedRequest.getNetAmount().amount,
    };

    this.output.present(result);

    return Result.ok(undefined);
  }
}