/**
 * PAYMENT SERVICE - REFERENCE IMPLEMENTATION
 *
 * @description Servicio de pagos con integración Stripe.
 * Soporta pagos únicos, suscripciones y webhooks.
 *
 * @usage
 * ```typescript
 * // Crear checkout session
 * const session = await this.paymentService.createCheckoutSession({
 *   userId: req.user.id,
 *   priceId: 'price_xxx',
 *   successUrl: 'https://app.com/success',
 *   cancelUrl: 'https://app.com/cancel',
 * });
 * // Redirigir a session.url
 * ```
 *
 * @origin Patrón base para proyectos con pagos
 */

import { Injectable, Logger, BadRequestException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import Stripe from 'stripe';

// Adaptar imports según proyecto
// import { Payment, Subscription, Customer } from '../entities';

@Injectable()
export class PaymentService {
  private readonly logger = new Logger(PaymentService.name);
  private readonly stripe: Stripe;

  constructor(
    private readonly configService: ConfigService,

    @InjectRepository(Payment, 'payments')
    private readonly paymentRepo: Repository<Payment>,

    @InjectRepository(Subscription, 'payments')
    private readonly subscriptionRepo: Repository<Subscription>,

    @InjectRepository(Customer, 'payments')
    private readonly customerRepo: Repository<Customer>,
  ) {
    this.stripe = new Stripe(this.configService.get<string>('STRIPE_SECRET_KEY'), {
      apiVersion: '2023-10-16',
    });
  }

  /**
   * Crear o recuperar customer de Stripe
   */
  async getOrCreateCustomer(userId: string, email: string): Promise<string> {
    // Buscar customer existente
    const existing = await this.customerRepo.findOne({ where: { user_id: userId } });
    if (existing) return existing.stripe_customer_id;

    // Crear en Stripe
    const stripeCustomer = await this.stripe.customers.create({
      email,
      metadata: { userId },
    });

    // Guardar en BD
    const customer = this.customerRepo.create({
      user_id: userId,
      stripe_customer_id: stripeCustomer.id,
      email,
    });
    await this.customerRepo.save(customer);

    return stripeCustomer.id;
  }

  /**
   * Crear sesión de checkout
   */
  async createCheckoutSession(dto: CreateCheckoutDto): Promise<{ sessionId: string; url: string }> {
    const customerId = await this.getOrCreateCustomer(dto.userId, dto.email);

    const session = await this.stripe.checkout.sessions.create({
      customer: customerId,
      payment_method_types: ['card'],
      line_items: [
        {
          price: dto.priceId,
          quantity: dto.quantity || 1,
        },
      ],
      mode: dto.mode || 'payment',
      success_url: dto.successUrl,
      cancel_url: dto.cancelUrl,
      metadata: {
        userId: dto.userId,
        ...dto.metadata,
      },
    });

    return {
      sessionId: session.id,
      url: session.url,
    };
  }

  /**
   * Crear suscripción directa
   */
  async createSubscription(dto: CreateSubscriptionDto): Promise<Subscription> {
    const customerId = await this.getOrCreateCustomer(dto.userId, dto.email);

    const stripeSubscription = await this.stripe.subscriptions.create({
      customer: customerId,
      items: [{ price: dto.priceId }],
      payment_behavior: 'default_incomplete',
      expand: ['latest_invoice.payment_intent'],
    });

    const subscription = this.subscriptionRepo.create({
      user_id: dto.userId,
      stripe_subscription_id: stripeSubscription.id,
      stripe_customer_id: customerId,
      status: stripeSubscription.status,
      current_period_start: new Date(stripeSubscription.current_period_start * 1000),
      current_period_end: new Date(stripeSubscription.current_period_end * 1000),
    });

    return this.subscriptionRepo.save(subscription);
  }

  /**
   * Cancelar suscripción
   */
  async cancelSubscription(subscriptionId: string, userId: string): Promise<void> {
    const subscription = await this.subscriptionRepo.findOne({
      where: { id: subscriptionId, user_id: userId },
    });

    if (!subscription) {
      throw new BadRequestException('Subscription not found');
    }

    await this.stripe.subscriptions.update(subscription.stripe_subscription_id, {
      cancel_at_period_end: true,
    });

    subscription.cancel_at_period_end = true;
    await this.subscriptionRepo.save(subscription);
  }

  /**
   * Obtener suscripción activa del usuario
   */
  async getActiveSubscription(userId: string): Promise<Subscription | null> {
    return this.subscriptionRepo.findOne({
      where: { user_id: userId, status: 'active' },
    });
  }

  /**
   * Procesar webhook de Stripe
   */
  async handleWebhook(signature: string, payload: Buffer): Promise<void> {
    const webhookSecret = this.configService.get<string>('STRIPE_WEBHOOK_SECRET');

    let event: Stripe.Event;
    try {
      event = this.stripe.webhooks.constructEvent(payload, signature, webhookSecret);
    } catch (err) {
      this.logger.error(`Webhook signature verification failed: ${err.message}`);
      throw new BadRequestException('Invalid webhook signature');
    }

    this.logger.log(`Processing webhook event: ${event.type}`);

    switch (event.type) {
      case 'checkout.session.completed':
        await this.handleCheckoutCompleted(event.data.object as Stripe.Checkout.Session);
        break;

      case 'invoice.paid':
        await this.handleInvoicePaid(event.data.object as Stripe.Invoice);
        break;

      case 'invoice.payment_failed':
        await this.handlePaymentFailed(event.data.object as Stripe.Invoice);
        break;

      case 'customer.subscription.updated':
      case 'customer.subscription.deleted':
        await this.handleSubscriptionUpdate(event.data.object as Stripe.Subscription);
        break;

      default:
        this.logger.debug(`Unhandled webhook event: ${event.type}`);
    }
  }

  // ============ WEBHOOK HANDLERS ============

  private async handleCheckoutCompleted(session: Stripe.Checkout.Session) {
    const userId = session.metadata?.userId;
    if (!userId) return;

    const payment = this.paymentRepo.create({
      user_id: userId,
      stripe_payment_id: session.payment_intent as string,
      amount: session.amount_total,
      currency: session.currency,
      status: 'completed',
    });

    await this.paymentRepo.save(payment);
    this.logger.log(`Payment completed for user: ${userId}`);
  }

  private async handleInvoicePaid(invoice: Stripe.Invoice) {
    const subscriptionId = invoice.subscription as string;
    if (!subscriptionId) return;

    await this.subscriptionRepo.update(
      { stripe_subscription_id: subscriptionId },
      { status: 'active' },
    );
  }

  private async handlePaymentFailed(invoice: Stripe.Invoice) {
    const subscriptionId = invoice.subscription as string;
    if (!subscriptionId) return;

    await this.subscriptionRepo.update(
      { stripe_subscription_id: subscriptionId },
      { status: 'past_due' },
    );
  }

  private async handleSubscriptionUpdate(stripeSubscription: Stripe.Subscription) {
    await this.subscriptionRepo.update(
      { stripe_subscription_id: stripeSubscription.id },
      {
        status: stripeSubscription.status,
        current_period_end: new Date(stripeSubscription.current_period_end * 1000),
        cancel_at_period_end: stripeSubscription.cancel_at_period_end,
      },
    );
  }
}

// ============ TIPOS ============

interface CreateCheckoutDto {
  userId: string;
  email: string;
  priceId: string;
  quantity?: number;
  mode?: 'payment' | 'subscription';
  successUrl: string;
  cancelUrl: string;
  metadata?: Record<string, string>;
}

interface CreateSubscriptionDto {
  userId: string;
  email: string;
  priceId: string;
}

interface Payment {
  id: string;
  user_id: string;
  stripe_payment_id: string;
  amount: number;
  currency: string;
  status: string;
}

interface Subscription {
  id: string;
  user_id: string;
  stripe_subscription_id: string;
  stripe_customer_id: string;
  status: string;
  current_period_start: Date;
  current_period_end: Date;
  cancel_at_period_end?: boolean;
}

interface Customer {
  id: string;
  user_id: string;
  stripe_customer_id: string;
  email: string;
}
