/**
 * @ai-summary Stripe API client wrapper
 * @ai-context Handles all Stripe API interactions with proper error handling
 */

import Stripe from 'stripe';
import { logger } from '../../../../core/logging/logger';
import { appConfig } from '../../../../core/config/config-loader';
import {
  StripeCustomer,
  StripeSubscription,
  StripePaymentIntent,
  StripeWebhookEvent,
} from './stripe.types';

export class StripeClient {
  private stripe: Stripe;
  private webhookSecret: string;

  constructor() {
    const stripeConfig = appConfig.getStripeConfig();

    this.stripe = new Stripe(stripeConfig.secretKey, {
      apiVersion: '2025-12-15.clover',
      typescript: true,
    });

    this.webhookSecret = stripeConfig.webhookSecret;

    logger.info('Stripe client initialized');
  }

  /**
   * Create a new Stripe customer
   */
  async createCustomer(email: string, name?: string): Promise<StripeCustomer> {
    try {
      logger.info('Creating Stripe customer', { email, name });

      const customer = await this.stripe.customers.create({
        email,
        name,
        metadata: {
          source: 'motovaultpro',
        },
      });

      logger.info('Stripe customer created', { customerId: customer.id });

      return {
        id: customer.id,
        email: customer.email || email,
        name: customer.name || undefined,
        created: customer.created,
        metadata: customer.metadata,
      };
    } catch (error: any) {
      logger.error('Failed to create Stripe customer', {
        email,
        error: error.message,
        code: error.code,
      });
      throw error;
    }
  }

  /**
   * Create a new subscription for a customer
   */
  async createSubscription(
    customerId: string,
    priceId: string,
    paymentMethodId?: string
  ): Promise<StripeSubscription> {
    try {
      logger.info('Creating Stripe subscription', { customerId, priceId, paymentMethodId });

      const subscriptionParams: Stripe.SubscriptionCreateParams = {
        customer: customerId,
        items: [{ price: priceId }],
        payment_behavior: 'default_incomplete',
        payment_settings: {
          save_default_payment_method: 'on_subscription',
        },
        expand: ['latest_invoice.payment_intent'],
      };

      if (paymentMethodId) {
        subscriptionParams.default_payment_method = paymentMethodId;
      }

      const subscription = await this.stripe.subscriptions.create(subscriptionParams);

      logger.info('Stripe subscription created', { subscriptionId: subscription.id });

      return {
        id: subscription.id,
        customer: subscription.customer as string,
        status: subscription.status as StripeSubscription['status'],
        items: subscription.items,
        currentPeriodStart: (subscription as any).current_period_start || 0,
        currentPeriodEnd: (subscription as any).current_period_end || 0,
        cancelAtPeriodEnd: subscription.cancel_at_period_end,
        canceledAt: subscription.canceled_at || undefined,
        created: subscription.created,
        metadata: subscription.metadata,
      };
    } catch (error: any) {
      logger.error('Failed to create Stripe subscription', {
        customerId,
        priceId,
        error: error.message,
        code: error.code,
      });
      throw error;
    }
  }

  /**
   * Cancel a subscription
   */
  async cancelSubscription(
    subscriptionId: string,
    cancelAtPeriodEnd: boolean = false
  ): Promise<StripeSubscription> {
    try {
      logger.info('Canceling Stripe subscription', { subscriptionId, cancelAtPeriodEnd });

      let subscription: Stripe.Subscription;

      if (cancelAtPeriodEnd) {
        // Cancel at period end (schedule cancellation)
        subscription = await this.stripe.subscriptions.update(subscriptionId, {
          cancel_at_period_end: true,
        });
        logger.info('Stripe subscription scheduled for cancellation', { subscriptionId });
      } else {
        // Cancel immediately
        subscription = await this.stripe.subscriptions.cancel(subscriptionId);
        logger.info('Stripe subscription canceled immediately', { subscriptionId });
      }

      return {
        id: subscription.id,
        customer: subscription.customer as string,
        status: subscription.status as StripeSubscription['status'],
        items: subscription.items,
        currentPeriodStart: (subscription as any).current_period_start || 0,
        currentPeriodEnd: (subscription as any).current_period_end || 0,
        cancelAtPeriodEnd: subscription.cancel_at_period_end,
        canceledAt: subscription.canceled_at || undefined,
        created: subscription.created,
        metadata: subscription.metadata,
      };
    } catch (error: any) {
      logger.error('Failed to cancel Stripe subscription', {
        subscriptionId,
        error: error.message,
        code: error.code,
      });
      throw error;
    }
  }

  /**
   * Update the payment method for a customer
   */
  async updatePaymentMethod(customerId: string, paymentMethodId: string): Promise<void> {
    try {
      logger.info('Updating Stripe payment method', { customerId, paymentMethodId });

      // Attach payment method to customer
      await this.stripe.paymentMethods.attach(paymentMethodId, {
        customer: customerId,
      });

      // Set as default payment method
      await this.stripe.customers.update(customerId, {
        invoice_settings: {
          default_payment_method: paymentMethodId,
        },
      });

      logger.info('Stripe payment method updated', { customerId, paymentMethodId });
    } catch (error: any) {
      logger.error('Failed to update Stripe payment method', {
        customerId,
        paymentMethodId,
        error: error.message,
        code: error.code,
      });
      throw error;
    }
  }

  /**
   * Create a payment intent for one-time donations
   */
  async createPaymentIntent(amount: number, currency: string = 'usd'): Promise<StripePaymentIntent> {
    try {
      logger.info('Creating Stripe payment intent', { amount, currency });

      const paymentIntent = await this.stripe.paymentIntents.create({
        amount,
        currency,
        metadata: {
          source: 'motovaultpro',
          type: 'donation',
        },
      });

      logger.info('Stripe payment intent created', { paymentIntentId: paymentIntent.id });

      return {
        id: paymentIntent.id,
        amount: paymentIntent.amount,
        currency: paymentIntent.currency,
        status: paymentIntent.status,
        customer: paymentIntent.customer as string | undefined,
        payment_method: paymentIntent.payment_method as string | undefined,
        client_secret: paymentIntent.client_secret,
        created: paymentIntent.created,
        metadata: paymentIntent.metadata,
      };
    } catch (error: any) {
      logger.error('Failed to create Stripe payment intent', {
        amount,
        currency,
        error: error.message,
        code: error.code,
      });
      throw error;
    }
  }

  /**
   * Construct and verify a webhook event from Stripe
   */
  constructWebhookEvent(payload: Buffer, signature: string): StripeWebhookEvent {
    try {
      const event = this.stripe.webhooks.constructEvent(
        payload,
        signature,
        this.webhookSecret
      );

      logger.info('Stripe webhook event verified', { eventId: event.id, type: event.type });

      return {
        id: event.id,
        type: event.type,
        data: event.data,
        created: event.created,
      };
    } catch (error: any) {
      logger.error('Failed to verify Stripe webhook event', {
        error: error.message,
      });
      throw error;
    }
  }

  /**
   * Retrieve a subscription by ID
   */
  async getSubscription(subscriptionId: string): Promise<StripeSubscription> {
    try {
      logger.info('Retrieving Stripe subscription', { subscriptionId });

      const subscription = await this.stripe.subscriptions.retrieve(subscriptionId);

      return {
        id: subscription.id,
        customer: subscription.customer as string,
        status: subscription.status as StripeSubscription['status'],
        items: subscription.items,
        currentPeriodStart: (subscription as any).current_period_start || 0,
        currentPeriodEnd: (subscription as any).current_period_end || 0,
        cancelAtPeriodEnd: subscription.cancel_at_period_end,
        canceledAt: subscription.canceled_at || undefined,
        created: subscription.created,
        metadata: subscription.metadata,
      };
    } catch (error: any) {
      logger.error('Failed to retrieve Stripe subscription', {
        subscriptionId,
        error: error.message,
        code: error.code,
      });
      throw error;
    }
  }

  /**
   * Retrieve a customer by ID
   */
  async getCustomer(customerId: string): Promise<StripeCustomer> {
    try {
      logger.info('Retrieving Stripe customer', { customerId });

      const customer = await this.stripe.customers.retrieve(customerId);

      if (customer.deleted) {
        throw new Error('Customer has been deleted');
      }

      return {
        id: customer.id,
        email: customer.email || '',
        name: customer.name || undefined,
        created: customer.created,
        metadata: customer.metadata,
      };
    } catch (error: any) {
      logger.error('Failed to retrieve Stripe customer', {
        customerId,
        error: error.message,
        code: error.code,
      });
      throw error;
    }
  }

  /**
   * List invoices for a customer
   */
  async listInvoices(customerId: string): Promise<any[]> {
    try {
      logger.info('Listing Stripe invoices', { customerId });

      const invoices = await this.stripe.invoices.list({
        customer: customerId,
        limit: 20,
      });

      logger.info('Stripe invoices retrieved', {
        customerId,
        count: invoices.data.length
      });

      return invoices.data;
    } catch (error: any) {
      logger.error('Failed to list Stripe invoices', {
        customerId,
        error: error.message,
        code: error.code,
      });
      throw error;
    }
  }
}
