import { stripe, SubscriptionTier, tierConfig } from '../config/billing.config';
import { z } from 'zod';

// Subscription service
export class SubscriptionService {
  /**
   * Create a new subscription for a customer
   */
  async createSubscription(
    customerId: string,
    tier: SubscriptionTier,
    metadata?: Record<string, string>
  ): Promise<Stripe.Subscription> {
    const priceId = tierConfig[tier].priceId;

    const subscription = await stripe.subscriptions.create({
      customer: customerId,
      items: [{ price: priceId }],
      metadata: metadata,
      proration_behavior: 'create_prorations',
    });

    return subscription;
  }

  /**
   * Update a customer's subscription tier
   */
  async updateSubscriptionTier(
    subscriptionId: string,
    newTier: SubscriptionTier
  ): Promise<Stripe.Subscription> {
    const newPriceId = tierConfig[newTier].priceId;

    const subscription = await stripe.subscriptions.update(subscriptionId, {
      items: [
        {
          price: newPriceId,
          quantity: 1,
        },
      ],
      proration_behavior: 'create_prorations',
    });

    return subscription;
  }

  /**
   * Cancel a subscription
   */
  async cancelSubscription(
    subscriptionId: string,
    atPeriodEnd: boolean = true
  ): Promise<Stripe.Subscription> {
    const subscription = await stripe.subscriptions.update(subscriptionId, {
      cancel_at_period_end: atPeriodEnd,
    });

    return subscription;
  }

  /**
   * Get subscription by ID
   */
  async getSubscription(subscriptionId: string): Promise<Stripe.Subscription | null> {
    try {
      const subscription = await stripe.subscriptions.retrieve(subscriptionId);
      return subscription;
    } catch (error) {
      if (error instanceof Stripe.errors.StripeInvalidRequestError) {
        return null;
      }
      throw error;
    }
  }

  /**
   * Get customer's current subscription
   */
  async getCustomerSubscription(customerId: string): Promise<Stripe.Subscription | null> {
    const subscriptions = await stripe.subscriptions.list({
      customer: customerId,
      status: 'active',
      limit: 1,
    });

    return subscriptions.data[0] || null;
  }
}

// Customer service
export class CustomerService {
  /**
   * Create a new Stripe customer
   */
  async createCustomer(
    email: string,
    name?: string,
    metadata?: Record<string, string>
  ): Promise<Stripe.Customer> {
    const customer = await stripe.customers.create({
      email,
      name,
      metadata,
    });

    return customer;
  }

  /**
   * Get or create customer by email
   */
  async getOrCreateCustomer(
    email: string,
    name?: string
  ): Promise<Stripe.Customer> {
    const existingCustomers = await stripe.customers.list({
      email,
      limit: 1,
    });

    if (existingCustomers.data.length > 0) {
      return existingCustomers.data[0];
    }

    return this.createCustomer(email, name);
  }

  /**
   * Create a billing portal session
   */
  async createBillingPortalSession(
    customerId: string,
    returnUrl: string
  ): Promise<Stripe.BillingPortal.Session> {
    const session = await stripe.billingPortal.sessions.create({
      customer: customerId,
      return_url: returnUrl,
    });

    return session;
  }

  /**
   * Get customer by ID
   */
  async getCustomer(customerId: string): Promise<Stripe.Customer | null> {
    try {
      const customer = await stripe.customers.retrieve(customerId);
      return customer as Stripe.Customer;
    } catch (error) {
      if (error instanceof Stripe.errors.StripeInvalidRequestError) {
        return null;
      }
      throw error;
    }
  }
}

// Webhook service
export class WebhookService {
  /**
   * Construct webhook event from raw body
   */
  constructEvent(
    rawBody: Buffer | string,
    signature: string
  ): Stripe.Event {
    return stripe.webhooks.constructEvent(
      rawBody,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET!
    );
  }

  /**
   * Handle webhook event
   */
  async handleWebhook(event: Stripe.Event): Promise<void> {
    switch (event.type) {
      case 'customer.subscription.created':
      case 'customer.subscription.updated':
        await this.handleSubscriptionChange(event.data.object);
        break;
      case 'customer.subscription.deleted':
        await this.handleSubscriptionDeleted(event.data.object);
        break;
      case 'invoice.payment_succeeded':
        await this.handlePaymentSucceeded(event.data.object);
        break;
      case 'invoice.payment_failed':
        await this.handlePaymentFailed(event.data.object);
        break;
      default:
        console.log(`Unhandled event type: ${event.type}`);
    }
  }

  private async handleSubscriptionChange(subscription: Stripe.Subscription) {
    console.log(`Subscription ${subscription.id} changed to ${subscription.status}`);
    // TODO: Update local database
  }

  private async handleSubscriptionDeleted(subscription: Stripe.Subscription) {
    console.log(`Subscription ${subscription.id} deleted`);
    // TODO: Update local database
  }

  private async handlePaymentSucceeded(invoice: Stripe.Invoice) {
    console.log(`Payment succeeded for invoice ${invoice.id}`);
    // TODO: Update usage tracking
  }

  private async handlePaymentFailed(invoice: Stripe.Invoice) {
    console.log(`Payment failed for invoice ${invoice.id}`);
    // TODO: Send notification to customer
  }
}

// Export instances
export const subscriptionService = new SubscriptionService();
export const customerService = new CustomerService();
export const webhookService = new WebhookService();
