/**
 * Stripe Refunds Module
 *
 * Handles refund operations and refund history.
 */

import Stripe from 'stripe';
import { stripe } from './client';
import { logger } from '@/lib/logger';

// =============================================================================
// Types
// =============================================================================

export type RefundReason = 'requested_by_customer' | 'duplicate' | 'fraudulent';

// =============================================================================
// Refund Operations
// =============================================================================

/**
 * Create a refund for a payment
 * Can create full or partial refunds
 */
export async function createRefund(params: {
  paymentIntentId?: string;
  chargeId?: string;
  amount?: number; // In cents. Omit for full refund
  reason?: RefundReason;
  metadata?: Record<string, string>;
}): Promise<Stripe.Refund> {
  const { paymentIntentId, chargeId, amount, reason, metadata } = params;

  if (!paymentIntentId && !chargeId) {
    throw new Error('Either paymentIntentId or chargeId is required');
  }

  try {
    const refundParams: Stripe.RefundCreateParams = {};

    if (paymentIntentId) {
      refundParams.payment_intent = paymentIntentId;
    } else if (chargeId) {
      refundParams.charge = chargeId;
    }

    if (amount !== undefined && amount > 0) {
      refundParams.amount = amount;
    }

    if (reason) {
      refundParams.reason = reason;
    }

    if (metadata) {
      refundParams.metadata = metadata;
    }

    const refund = await stripe.refunds.create(refundParams);
    return refund;
  } catch (error) {
    logger.error('Error creating refund', error);
    throw error;
  }
}

/**
 * Retrieve a refund by ID
 */
export async function getRefund(refundId: string): Promise<Stripe.Refund | null> {
  try {
    const refund = await stripe.refunds.retrieve(refundId, {
      expand: ['charge', 'payment_intent'],
    });
    return refund;
  } catch (error) {
    logger.error('Error retrieving refund', error);
    return null;
  }
}

/**
 * List refunds for a payment intent or charge
 */
export async function listRefunds(params: {
  paymentIntentId?: string;
  chargeId?: string;
  limit?: number;
}): Promise<Stripe.Refund[]> {
  const { paymentIntentId, chargeId, limit = 10 } = params;

  try {
    const listParams: Stripe.RefundListParams = {
      limit,
    };

    if (paymentIntentId) {
      listParams.payment_intent = paymentIntentId;
    }

    if (chargeId) {
      listParams.charge = chargeId;
    }

    const refunds = await stripe.refunds.list(listParams);
    return refunds.data;
  } catch (error) {
    logger.error('Error listing refunds', error);
    return [];
  }
}

/**
 * Cancel a pending refund
 * Only works if the refund status is 'pending'
 */
export async function cancelRefund(refundId: string): Promise<Stripe.Refund | null> {
  try {
    const refund = await stripe.refunds.cancel(refundId);
    return refund;
  } catch (error) {
    logger.error('Error cancelling refund', error);
    return null;
  }
}

/**
 * Get all refundable payments for a customer
 */
export async function getRefundablePayments(
  customerId: string,
  refundWindowDays: number = 180
): Promise<
  Array<{
    paymentIntent: Stripe.PaymentIntent;
    charge: Stripe.Charge | null;
    amountRefundable: number;
    amountRefunded: number;
    isFullyRefunded: boolean;
    withinRefundWindow: boolean;
  }>
> {
  try {
    const paymentIntents = await stripe.paymentIntents.list({
      customer: customerId,
      limit: 100,
      expand: ['data.latest_charge'],
    });

    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - refundWindowDays);
    const cutoffTimestamp = Math.floor(cutoffDate.getTime() / 1000);

    const refundablePayments = paymentIntents.data
      .filter((pi) => pi.status === 'succeeded')
      .map((pi) => {
        const charge = pi.latest_charge as Stripe.Charge | null;
        const amountRefunded = charge?.amount_refunded || 0;
        const amountRefundable = pi.amount - amountRefunded;
        const isFullyRefunded = amountRefundable <= 0;
        const withinRefundWindow = pi.created >= cutoffTimestamp;

        return {
          paymentIntent: pi,
          charge,
          amountRefundable,
          amountRefunded,
          isFullyRefunded,
          withinRefundWindow,
        };
      })
      .filter((p) => !p.isFullyRefunded && p.withinRefundWindow);

    return refundablePayments;
  } catch (error) {
    logger.error('Error getting refundable payments', error);
    return [];
  }
}

/**
 * Get refund history for a customer
 */
export async function getCustomerRefunds(
  customerId: string,
  limit: number = 50
): Promise<Stripe.Refund[]> {
  try {
    // First get all charges for this customer
    const charges = await stripe.charges.list({
      customer: customerId,
      limit: 100,
    });

    // Get refunds for each charge that has been refunded
    const refundedCharges = charges.data.filter((c) => c.refunded || c.amount_refunded > 0);

    const allRefunds: Stripe.Refund[] = [];

    for (const charge of refundedCharges) {
      const refunds = await stripe.refunds.list({
        charge: charge.id,
        limit: 10,
      });
      allRefunds.push(...refunds.data);
    }

    // Sort by created date (most recent first) and limit
    return allRefunds.sort((a, b) => b.created - a.created).slice(0, limit);
  } catch (error) {
    logger.error('Error getting customer refunds', error);
    return [];
  }
}
