/**
 * Quotation Payments Service
 * Stripe checkout, manual payment recording, webhook handling, status recalculation
 */

import { randomUUID } from 'crypto';
import Stripe from 'stripe';
import { prisma } from '@/lib/db';
import { stripe } from '@/lib/stripe';
import { logger } from '@/lib/logger';
import type { ManualPaymentInput } from '@/lib/validators/quotation-payments.validator';

const log = logger.scope('QuotationPaymentsService');

// ═══════════════════════════════════════════════════════════════════════
// Stripe Checkout
// ═══════════════════════════════════════════════════════════════════════

/**
 * Create a Stripe Checkout session for paying a quotation
 */
export async function createQuotationCheckoutSession(
  userId: string,
  quotationId: string,
  successUrl: string,
  cancelUrl: string,
) {
  const quotation = await prisma.quotations.findFirst({
    where: { id: quotationId, userId },
    include: { customer: true },
  });

  if (!quotation) {
    throw new Error('Quotation not found');
  }

  const remaining = Math.round((quotation.total + quotation.penaltyAmount - quotation.amountPaid) * 100);
  if (remaining <= 0) {
    throw new Error('Quotation is already fully paid');
  }

  const session = await stripe.checkout.sessions.create({
    mode: 'payment',
    payment_method_types: ['card'],
    line_items: [
      {
        price_data: {
          currency: quotation.currency.toLowerCase(),
          unit_amount: remaining,
          product_data: {
            name: `Quotation ${quotation.quotationNumber}`,
            description: quotation.title,
          },
        },
        quantity: 1,
      },
    ],
    metadata: {
      type: 'quotation_payment',
      quotation_id: quotationId,
      user_id: userId,
    },
    success_url: successUrl,
    cancel_url: cancelUrl,
  });

  // Store session ref on quotation
  await prisma.quotations.update({
    where: { id: quotationId },
    data: {
      stripeSessionId: session.id,
      stripeCheckoutUrl: session.url,
      updatedAt: new Date(),
    },
  });

  log.info('Checkout session created', { quotationId, sessionId: session.id });

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

// ═══════════════════════════════════════════════════════════════════════
// Manual Payment
// ═══════════════════════════════════════════════════════════════════════

/**
 * Record a manual (cash/bank/check) payment against a quotation
 */
export async function recordManualPayment(
  userId: string,
  quotationId: string,
  input: ManualPaymentInput,
) {
  const quotation = await prisma.quotations.findFirst({
    where: { id: quotationId, userId },
  });

  if (!quotation) {
    throw new Error('Quotation not found');
  }

  const payment = await prisma.quotation_payments.create({
    data: {
      id: randomUUID(),
      quotationId,
      amount: input.amount,
      currency: quotation.currency,
      method: input.method,
      reference: input.reference ?? null,
      manualNotes: input.manualNotes ?? null,
      recordedBy: userId,
      paidAt: input.paidAt ? new Date(input.paidAt) : new Date(),
    },
  });

  await recalculatePaymentStatus(quotationId);

  log.info('Manual payment recorded', { quotationId, paymentId: payment.id, amount: input.amount });
  return payment;
}

// ═══════════════════════════════════════════════════════════════════════
// Webhook Handler
// ═══════════════════════════════════════════════════════════════════════

/**
 * Idempotent handler for Stripe checkout.session.completed with type=quotation_payment
 */
export async function handleQuotationPaymentWebhook(session: Stripe.Checkout.Session) {
  const quotationId = session.metadata?.quotation_id ?? session.metadata?.quotationId;
  if (!quotationId) {
    log.warn('Quotation payment webhook missing quotation_id', { sessionId: session.id });
    return;
  }

  const paymentIntentId = (session.payment_intent as string) || session.id;

  // Idempotency: check if payment already recorded
  const existing = await prisma.quotation_payments.findFirst({
    where: { stripePaymentIntentId: paymentIntentId },
  });
  if (existing) {
    log.info('Quotation payment already processed (idempotent)', {
      quotationId,
      paymentIntentId,
    });
    return;
  }

  const quotation = await prisma.quotations.findUnique({ where: { id: quotationId } });
  if (!quotation) {
    log.warn('Quotation not found for payment webhook', { quotationId });
    return;
  }

  const amountPaid = (session.amount_total ?? 0) / 100;

  await prisma.quotation_payments.create({
    data: {
      id: randomUUID(),
      quotationId,
      amount: amountPaid,
      currency: session.currency?.toUpperCase() ?? quotation.currency,
      method: 'STRIPE',
      stripePaymentIntentId: paymentIntentId,
      recordedBy: session.metadata?.user_id ?? null,
      paidAt: new Date(),
    },
  });

  await recalculatePaymentStatus(quotationId);

  log.info('Quotation payment recorded from webhook', { quotationId, paymentIntentId, amountPaid });
}

// ═══════════════════════════════════════════════════════════════════════
// Payment Status Recalculation
// ═══════════════════════════════════════════════════════════════════════

/**
 * Recalculate payment status based on total payments vs total owed
 */
export async function recalculatePaymentStatus(quotationId: string) {
  const quotation = await prisma.quotations.findUnique({ where: { id: quotationId } });
  if (!quotation) return;

  const payments = await prisma.quotation_payments.findMany({
    where: { quotationId },
  });

  const amountPaid = payments.reduce((sum, p) => sum + p.amount, 0);
  const totalOwed = quotation.total + quotation.penaltyAmount;

  let paymentStatus: 'PAID' | 'PARTIAL' | 'OVERDUE' | 'UNPAID';
  let paidAt: Date | null = null;

  if (amountPaid >= totalOwed) {
    paymentStatus = 'PAID';
    // Use the latest payment date as paidAt
    const latest = payments.sort((a, b) => b.paidAt.getTime() - a.paidAt.getTime())[0];
    paidAt = latest?.paidAt ?? new Date();
  } else if (amountPaid > 0) {
    // Check if overdue
    if (quotation.dueDate && new Date() > quotation.dueDate) {
      paymentStatus = 'OVERDUE';
    } else {
      paymentStatus = 'PARTIAL';
    }
  } else {
    if (quotation.dueDate && new Date() > quotation.dueDate) {
      paymentStatus = 'OVERDUE';
    } else {
      paymentStatus = 'UNPAID';
    }
  }

  await prisma.quotations.update({
    where: { id: quotationId },
    data: {
      amountPaid: Math.round(amountPaid * 100) / 100,
      paymentStatus,
      paidAt,
      updatedAt: new Date(),
    },
  });

  log.info('Payment status recalculated', { quotationId, paymentStatus, amountPaid, totalOwed });
}

// ═══════════════════════════════════════════════════════════════════════
// Payment History
// ═══════════════════════════════════════════════════════════════════════

/**
 * Get payment history for a quotation
 */
export async function getPaymentHistory(userId: string, quotationId: string) {
  // Verify ownership
  const quotation = await prisma.quotations.findFirst({
    where: { id: quotationId, userId },
  });
  if (!quotation) return null;

  return prisma.quotation_payments.findMany({
    where: { quotationId },
    orderBy: { paidAt: 'desc' },
  });
}
