import { PaymentStatus, PaymentType } from '../../domain/entities/Payment';
import type { ISeasonSponsorshipRepository } from '@core/racing/domain/repositories/ISeasonSponsorshipRepository';
import type { IPaymentRepository } from '../../domain/repositories/IPaymentRepository';
import type { UseCase } from '@core/shared/application/UseCase';
import { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';

export interface SponsorBillingStats {
  totalSpent: number;
  pendingAmount: number;
  nextPaymentDate: string | null;
  nextPaymentAmount: number | null;
  activeSponsorships: number;
  averageMonthlySpend: number;
}

export interface SponsorInvoiceSummary {
  id: string;
  invoiceNumber: string;
  date: string;
  dueDate: string;
  amount: number;
  vatAmount: number;
  totalAmount: number;
  status: 'paid' | 'pending' | 'overdue' | 'failed';
  description: string;
  sponsorshipType: 'league' | 'team' | 'driver' | 'race' | 'platform';
  pdfUrl: string;
}

export interface SponsorPaymentMethodSummary {
  id: string;
  type: 'card' | 'bank' | 'sepa';
  last4: string;
  brand?: string;
  isDefault: boolean;
  expiryMonth?: number;
  expiryYear?: number;
  bankName?: string;
}

export interface SponsorBillingSummary {
  paymentMethods: SponsorPaymentMethodSummary[];
  invoices: SponsorInvoiceSummary[];
  stats: SponsorBillingStats;
}

export interface GetSponsorBillingInput {
  sponsorId: string;
}

export interface GetSponsorBillingResult {
  paymentMethods: SponsorPaymentMethodSummary[];
  invoices: SponsorInvoiceSummary[];
  stats: SponsorBillingStats;
}

export type GetSponsorBillingErrorCode = never;

export class GetSponsorBillingUseCase
  implements UseCase<GetSponsorBillingInput, GetSponsorBillingResult, GetSponsorBillingErrorCode>
{
  constructor(
    private readonly paymentRepository: IPaymentRepository,
    private readonly seasonSponsorshipRepository: ISeasonSponsorshipRepository,
  ) {}

  async execute(input: GetSponsorBillingInput): Promise<Result<GetSponsorBillingResult, ApplicationErrorCode<GetSponsorBillingErrorCode>>> {
    const { sponsorId } = input;

    // In this in-memory implementation we derive billing data from payments
    // where the sponsor is the payer.
    const payments = await this.paymentRepository.findByFilters({
      payerId: sponsorId,
      type: PaymentType.SPONSORSHIP,
    });

    const sponsorships = await this.seasonSponsorshipRepository.findBySponsorId(sponsorId);

    const invoices: SponsorInvoiceSummary[] = payments.map((payment, index) => {
      const createdAt = payment.createdAt ?? new Date();
      const dueDate = new Date(createdAt.getTime());
      dueDate.setDate(dueDate.getDate() + 14);

      const vatAmount = +(payment.amount * 0.19).toFixed(2);
      const totalAmount = +(payment.amount + vatAmount).toFixed(2);

      let status: 'paid' | 'pending' | 'overdue' | 'failed' = 'pending';
      if (payment.status === PaymentStatus.COMPLETED) status = 'paid';
      else if (payment.status === PaymentStatus.FAILED || payment.status === PaymentStatus.REFUNDED) status = 'failed';

      const now = new Date();
      if (status === 'pending' && dueDate < now) {
        status = 'overdue';
      }

      return {
        id: payment.id,
        invoiceNumber: `GP-${createdAt.getFullYear()}-${String(index + 1).padStart(6, '0')}`,
        date: createdAt.toISOString(),
        dueDate: dueDate.toISOString(),
        amount: payment.amount,
        vatAmount,
        totalAmount,
        status,
        description: 'Sponsorship payment',
        sponsorshipType: 'league',
        pdfUrl: '#',
      };
    });

    const totalSpent = invoices
      .filter(i => i.status === 'paid')
      .reduce((sum, i) => sum + i.totalAmount, 0);

    const pendingAmount = invoices
      .filter(i => i.status === 'pending' || i.status === 'overdue')
      .reduce((sum, i) => sum + i.totalAmount, 0);

    const pendingInvoices = invoices.filter(i => i.status === 'pending' || i.status === 'overdue');
    const nextPending = pendingInvoices.length > 0
      ? pendingInvoices.reduce((earliest, current) => (current.dueDate < earliest.dueDate ? current : earliest))
      : null;

    const stats: SponsorBillingStats = {
      totalSpent,
      pendingAmount,
      nextPaymentDate: nextPending ? nextPending.dueDate : null,
      nextPaymentAmount: nextPending ? nextPending.totalAmount : null,
      activeSponsorships: sponsorships.filter(s => s.status === 'active').length,
      averageMonthlySpend: this.calculateAverageMonthlySpend(invoices),
    };

    // NOTE: Payment methods are not yet persisted in core. For now, we expose
    // an empty list so the UI can still render correctly. A dedicated
    // payment-methods port can be added later when the concept exists in core.
    const paymentMethods: SponsorPaymentMethodSummary[] = [];

    const result: GetSponsorBillingResult = {
      paymentMethods,
      invoices,
      stats,
    };

    return Result.ok(result);
  }

  private calculateAverageMonthlySpend(invoices: SponsorInvoiceSummary[]): number {
    if (invoices.length === 0) return 0;

    const sorted = [...invoices].sort((a, b) => a.date.localeCompare(b.date));
    const first = new Date(sorted[0]!.date);
    const last = new Date(sorted[sorted.length - 1]!.date);

    const months = this.monthDiff(first, last) || 1;
    const total = sorted.reduce((sum, inv) => sum + inv.totalAmount, 0);

    return Math.round((total / months) * 100) / 100;
  }

  private monthDiff(d1: Date, d2: Date): number {
    const years = d2.getFullYear() - d1.getFullYear();
    const months = d2.getMonth() - d1.getMonth();
    return years * 12 + months + 1;
  }
}