/**
 * Application Use Case: UpdateMemberPaymentUseCase
 *
 * Updates a member payment record.
 */

import type { IMembershipFeeRepository, IMemberPaymentRepository } from '../../domain/repositories/IMembershipFeeRepository';
import type { MemberPayment } from '../../domain/entities/MemberPayment';
import { MemberPaymentStatus } from '../../domain/entities/MemberPayment';
import type { UseCase } from '@core/shared/application/UseCase';
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';

const PLATFORM_FEE_RATE = 0.10;

export interface UpdateMemberPaymentInput {
  feeId: string;
  driverId: string;
  status?: MemberPaymentStatus;
  paidAt?: Date | string;
}

export interface UpdateMemberPaymentResult {
  payment: MemberPayment;
}

export type UpdateMemberPaymentErrorCode = 'MEMBERSHIP_FEE_NOT_FOUND';

export class UpdateMemberPaymentUseCase
  implements UseCase<UpdateMemberPaymentInput, void, UpdateMemberPaymentErrorCode>
{
  constructor(
    private readonly membershipFeeRepository: IMembershipFeeRepository,
    private readonly memberPaymentRepository: IMemberPaymentRepository,
    private readonly output: UseCaseOutputPort<UpdateMemberPaymentResult>,
  ) {}

  async execute(input: UpdateMemberPaymentInput): Promise<Result<void, ApplicationErrorCode<UpdateMemberPaymentErrorCode>>> {
    const { feeId, driverId, status, paidAt } = input;

    const fee = await this.membershipFeeRepository.findById(feeId);
    if (!fee) {
      return Result.err({ code: 'MEMBERSHIP_FEE_NOT_FOUND' as const });
    }

    let payment = await this.memberPaymentRepository.findByFeeIdAndDriverId(feeId, driverId);

    if (!payment) {
      const platformFee = fee.amount * PLATFORM_FEE_RATE;
      const netAmount = fee.amount - platformFee;

      const paymentId = `mp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
      const newPayment: MemberPayment = {
        id: paymentId,
        feeId,
        driverId,
        amount: fee.amount,
        platformFee,
        netAmount,
        status: MemberPaymentStatus.PENDING,
        dueDate: new Date(),
      };
      payment = await this.memberPaymentRepository.create(newPayment);
    }

    if (status) {
      payment.status = status;
    }
    if (paidAt || status === MemberPaymentStatus.PAID) {
      payment.paidAt = paidAt ? new Date(paidAt as string) : new Date();
    }

    const updatedPayment = await this.memberPaymentRepository.update(payment);

    this.output.present({ payment: updatedPayment });

    return Result.ok(undefined);
  }
}