import type { Logger } from '@core/shared/application/Logger';
import { Inject, Injectable } from '@nestjs/common';

// Use cases
import type { AwardPrizeUseCase } from '@core/payments/application/use-cases/AwardPrizeUseCase';
import type { CreatePaymentUseCase } from '@core/payments/application/use-cases/CreatePaymentUseCase';
import type { CreatePrizeUseCase } from '@core/payments/application/use-cases/CreatePrizeUseCase';
import type { DeletePrizeUseCase } from '@core/payments/application/use-cases/DeletePrizeUseCase';
import type { GetMembershipFeesUseCase } from '@core/payments/application/use-cases/GetMembershipFeesUseCase';
import type { GetPaymentsUseCase } from '@core/payments/application/use-cases/GetPaymentsUseCase';
import type { GetPrizesUseCase } from '@core/payments/application/use-cases/GetPrizesUseCase';
import type { GetWalletUseCase } from '@core/payments/application/use-cases/GetWalletUseCase';
import type { ProcessWalletTransactionUseCase } from '@core/payments/application/use-cases/ProcessWalletTransactionUseCase';
import type { UpdateMemberPaymentUseCase } from '@core/payments/application/use-cases/UpdateMemberPaymentUseCase';
import type { UpdatePaymentStatusUseCase } from '@core/payments/application/use-cases/UpdatePaymentStatusUseCase';
import type { UpsertMembershipFeeUseCase } from '@core/payments/application/use-cases/UpsertMembershipFeeUseCase';

// Presenters
import { AwardPrizePresenter } from './presenters/AwardPrizePresenter';
import { CreatePaymentPresenter } from './presenters/CreatePaymentPresenter';
import { CreatePrizePresenter } from './presenters/CreatePrizePresenter';
import { DeletePrizePresenter } from './presenters/DeletePrizePresenter';
import { GetMembershipFeesPresenter } from './presenters/GetMembershipFeesPresenter';
import { GetPaymentsPresenter } from './presenters/GetPaymentsPresenter';
import { GetPrizesPresenter } from './presenters/GetPrizesPresenter';
import { GetWalletPresenter } from './presenters/GetWalletPresenter';
import { ProcessWalletTransactionPresenter } from './presenters/ProcessWalletTransactionPresenter';
import { UpdateMemberPaymentPresenter } from './presenters/UpdateMemberPaymentPresenter';
import { UpdatePaymentStatusPresenter } from './presenters/UpdatePaymentStatusPresenter';
import { UpsertMembershipFeePresenter } from './presenters/UpsertMembershipFeePresenter';

// DTOs
import type {
  AwardPrizeInput,
  AwardPrizeOutput,
  CreatePaymentInput,
  CreatePaymentOutput,
  CreatePrizeInput,
  CreatePrizeOutput,
  DeletePrizeInput,
  DeletePrizeOutput,
  GetMembershipFeesOutput,
  GetMembershipFeesQuery,
  GetPaymentsOutput,
  GetPaymentsQuery,
  GetPrizesOutput,
  GetPrizesQuery,
  GetWalletOutput,
  GetWalletQuery,
  ProcessWalletTransactionInput,
  ProcessWalletTransactionOutput,
  UpdateMemberPaymentInput,
  UpdateMemberPaymentOutput,
  UpdatePaymentStatusInput,
  UpdatePaymentStatusOutput,
  UpsertMembershipFeeInput,
  UpsertMembershipFeeOutput,
} from './dtos/PaymentsDto';

// Injection tokens
import {
  AWARD_PRIZE_USE_CASE_TOKEN,
  CREATE_PAYMENT_USE_CASE_TOKEN,
  CREATE_PRIZE_USE_CASE_TOKEN,
  DELETE_PRIZE_USE_CASE_TOKEN,
  GET_MEMBERSHIP_FEES_USE_CASE_TOKEN,
  GET_PAYMENTS_USE_CASE_TOKEN,
  GET_PRIZES_USE_CASE_TOKEN,
  GET_WALLET_USE_CASE_TOKEN,
  LOGGER_TOKEN,
  PROCESS_WALLET_TRANSACTION_USE_CASE_TOKEN,
  UPDATE_MEMBER_PAYMENT_USE_CASE_TOKEN,
  UPDATE_PAYMENT_STATUS_USE_CASE_TOKEN,
  UPSERT_MEMBERSHIP_FEE_USE_CASE_TOKEN,
} from './PaymentsTokens';

@Injectable()
export class PaymentsService {
  constructor(
    @Inject(GET_PAYMENTS_USE_CASE_TOKEN) private readonly getPaymentsUseCase: GetPaymentsUseCase,
    @Inject(CREATE_PAYMENT_USE_CASE_TOKEN) private readonly createPaymentUseCase: CreatePaymentUseCase,
    @Inject(UPDATE_PAYMENT_STATUS_USE_CASE_TOKEN) private readonly updatePaymentStatusUseCase: UpdatePaymentStatusUseCase,
    @Inject(GET_MEMBERSHIP_FEES_USE_CASE_TOKEN) private readonly getMembershipFeesUseCase: GetMembershipFeesUseCase,
    @Inject(UPSERT_MEMBERSHIP_FEE_USE_CASE_TOKEN) private readonly upsertMembershipFeeUseCase: UpsertMembershipFeeUseCase,
    @Inject(UPDATE_MEMBER_PAYMENT_USE_CASE_TOKEN) private readonly updateMemberPaymentUseCase: UpdateMemberPaymentUseCase,
    @Inject(GET_PRIZES_USE_CASE_TOKEN) private readonly getPrizesUseCase: GetPrizesUseCase,
    @Inject(CREATE_PRIZE_USE_CASE_TOKEN) private readonly createPrizeUseCase: CreatePrizeUseCase,
    @Inject(AWARD_PRIZE_USE_CASE_TOKEN) private readonly awardPrizeUseCase: AwardPrizeUseCase,
    @Inject(DELETE_PRIZE_USE_CASE_TOKEN) private readonly deletePrizeUseCase: DeletePrizeUseCase,
    @Inject(GET_WALLET_USE_CASE_TOKEN) private readonly getWalletUseCase: GetWalletUseCase,
    @Inject(PROCESS_WALLET_TRANSACTION_USE_CASE_TOKEN) private readonly processWalletTransactionUseCase: ProcessWalletTransactionUseCase,
    @Inject(LOGGER_TOKEN) private readonly logger: Logger,
    private readonly getPaymentsPresenter: GetPaymentsPresenter,
    private readonly createPaymentPresenter: CreatePaymentPresenter,
    private readonly updatePaymentStatusPresenter: UpdatePaymentStatusPresenter,
    private readonly getMembershipFeesPresenter: GetMembershipFeesPresenter,
    private readonly upsertMembershipFeePresenter: UpsertMembershipFeePresenter,
    private readonly updateMemberPaymentPresenter: UpdateMemberPaymentPresenter,
    private readonly getPrizesPresenter: GetPrizesPresenter,
    private readonly createPrizePresenter: CreatePrizePresenter,
    private readonly awardPrizePresenter: AwardPrizePresenter,
    private readonly deletePrizePresenter: DeletePrizePresenter,
    private readonly getWalletPresenter: GetWalletPresenter,
    private readonly processWalletTransactionPresenter: ProcessWalletTransactionPresenter,
  ) {}

  async getPayments(query: GetPaymentsQuery): Promise<GetPaymentsOutput> {
    this.logger.debug('[PaymentsService] Getting payments', { query });

    const result = await this.getPaymentsUseCase.execute(query);
    if (result.isErr()) {
      throw new Error(result.unwrapErr().code ?? 'Failed to get payments');
    }
    return this.getPaymentsPresenter.getResponseModel();
  }

  async createPayment(input: CreatePaymentInput): Promise<CreatePaymentOutput> {
    this.logger.debug('[PaymentsService] Creating payment', { input });

    const result = await this.createPaymentUseCase.execute(input);
    if (result.isErr()) {
      throw new Error(result.unwrapErr().code ?? 'Failed to create payment');
    }
    return this.createPaymentPresenter.getResponseModel();
  }

  async updatePaymentStatus(input: UpdatePaymentStatusInput): Promise<UpdatePaymentStatusOutput> {
    this.logger.debug('[PaymentsService] Updating payment status', { input });

    const result = await this.updatePaymentStatusUseCase.execute(input);
    if (result.isErr()) {
      throw new Error(result.unwrapErr().code ?? 'Failed to update payment status');
    }
    return this.updatePaymentStatusPresenter.getResponseModel();
  }

  async getMembershipFees(query: GetMembershipFeesQuery): Promise<GetMembershipFeesOutput> {
    this.logger.debug('[PaymentsService] Getting membership fees', { query });

    const result = await this.getMembershipFeesUseCase.execute(query);
    if (result.isErr()) {
      throw new Error(result.unwrapErr().code ?? 'Failed to get membership fees');
    }
    return this.getMembershipFeesPresenter.viewModel;
  }

  async upsertMembershipFee(input: UpsertMembershipFeeInput): Promise<UpsertMembershipFeeOutput> {
    this.logger.debug('[PaymentsService] Upserting membership fee', { input });

    const result = await this.upsertMembershipFeeUseCase.execute(input);
    if (result.isErr()) {
      // Since UpsertMembershipFeeUseCase has never as error type, this should never happen
      // but we keep the check for consistency
      throw new Error('Failed to upsert membership fee');
    }
    return this.upsertMembershipFeePresenter.viewModel;
  }

  async updateMemberPayment(input: UpdateMemberPaymentInput): Promise<UpdateMemberPaymentOutput> {
    this.logger.debug('[PaymentsService] Updating member payment', { input });

    const result = await this.updateMemberPaymentUseCase.execute(input);
    if (result.isErr()) {
      throw new Error(result.unwrapErr().code ?? 'Failed to update member payment');
    }
    return this.updateMemberPaymentPresenter.viewModel;
  }

  async getPrizes(query: GetPrizesQuery): Promise<GetPrizesOutput> {
    this.logger.debug('[PaymentsService] Getting prizes', { query });

    const input: { leagueId: string; seasonId?: string } = {
      leagueId: query.leagueId!,
    };
    if (query.seasonId !== undefined) {
      input.seasonId = query.seasonId;
    }
    await this.getPrizesUseCase.execute(input);
    return this.getPrizesPresenter.viewModel;
  }

  async createPrize(input: CreatePrizeInput): Promise<CreatePrizeOutput> {
    this.logger.debug('[PaymentsService] Creating prize', { input });

    await this.createPrizeUseCase.execute(input);
    return this.createPrizePresenter.viewModel;
  }

  async awardPrize(input: AwardPrizeInput): Promise<AwardPrizeOutput> {
    this.logger.debug('[PaymentsService] Awarding prize', { input });

    await this.awardPrizeUseCase.execute(input);
    return this.awardPrizePresenter.viewModel;
  }

  async deletePrize(input: DeletePrizeInput): Promise<DeletePrizeOutput> {
    this.logger.debug('[PaymentsService] Deleting prize', { input });

    await this.deletePrizeUseCase.execute(input);
    return this.deletePrizePresenter.viewModel;
  }

  async getWallet(query: GetWalletQuery): Promise<GetWalletOutput> {
    this.logger.debug('[PaymentsService] Getting wallet', { query });

    await this.getWalletUseCase.execute({ leagueId: query.leagueId! });
    return this.getWalletPresenter.viewModel;
  }

  async processWalletTransaction(input: ProcessWalletTransactionInput): Promise<ProcessWalletTransactionOutput> {
    this.logger.debug('[PaymentsService] Processing wallet transaction', { input });

    await this.processWalletTransactionUseCase.execute(input);
    return this.processWalletTransactionPresenter.viewModel;
  }
}