import express from 'express';
import { stripe, webhookSecret } from '../config/stripe.js';
import { Order } from '../models/Order.js';
import { createWrenOffsetOrder, getWrenPortfolios } from '../utils/wrenClient.js';
import { sendReceiptEmail, sendAdminNotification } from '../utils/emailService.js';
import { selectComparisons } from '../utils/carbonComparisons.js';
import { formatPortfolioProjects } from '../utils/portfolioColors.js';
import { nocodbClient } from '../utils/nocodbClient.js';
import { mapStripeSessionToNocoDBOrder, mapWrenFulfillmentData } from '../utils/nocodbMapper.js';

const router = express.Router();

/**
 * Log all available Stripe session data for schema verification
 * This helps us verify what fields are actually available from Stripe
 */
function logStripeSessionData(session) {
  console.log('\n╔════════════════════════════════════════════════════════════════╗');
  console.log('║         STRIPE CHECKOUT SESSION - COMPREHENSIVE DATA          ║');
  console.log('╚════════════════════════════════════════════════════════════════╝\n');

  // Core Session Info
  console.log('📋 SESSION INFORMATION:');
  console.log('  Session ID:', session.id || 'N/A');
  console.log('  Payment Intent:', session.payment_intent || 'N/A');
  console.log('  Payment Status:', session.payment_status || 'N/A');
  console.log('  Status:', session.status || 'N/A');
  console.log('  Mode:', session.mode || 'N/A');
  console.log('  Created:', session.created ? new Date(session.created * 1000).toISOString() : 'N/A');
  console.log('  Expires At:', session.expires_at ? new Date(session.expires_at * 1000).toISOString() : 'N/A');

  // Amount Details
  console.log('\n💰 PAYMENT AMOUNT:');
  console.log('  Amount Total:', session.amount_total ? `${session.amount_total} cents ($${(session.amount_total / 100).toFixed(2)})` : 'N/A');
  console.log('  Amount Subtotal:', session.amount_subtotal ? `${session.amount_subtotal} cents ($${(session.amount_subtotal / 100).toFixed(2)})` : 'N/A');
  console.log('  Currency:', session.currency ? session.currency.toUpperCase() : 'N/A');

  // Customer Details
  console.log('\n👤 CUSTOMER DETAILS:');
  if (session.customer_details) {
    console.log('  Name (Display):', session.customer_details.name || 'N/A');
    console.log('  Business Name:', session.customer_details.business_name || 'N/A');
    console.log('  Individual Name:', session.customer_details.individual_name || 'N/A');
    console.log('  Email:', session.customer_details.email || 'N/A');
    console.log('  Phone:', session.customer_details.phone || 'N/A');
    console.log('  Tax Exempt:', session.customer_details.tax_exempt || 'N/A');

    // Billing Address
    console.log('\n📬 BILLING ADDRESS:');
    if (session.customer_details.address) {
      const addr = session.customer_details.address;
      console.log('  Line 1:', addr.line1 || 'N/A');
      console.log('  Line 2:', addr.line2 || 'N/A');
      console.log('  City:', addr.city || 'N/A');
      console.log('  State:', addr.state || 'N/A');
      console.log('  Postal Code:', addr.postal_code || 'N/A');
      console.log('  Country:', addr.country || 'N/A');
    } else {
      console.log('  No address provided');
    }
  } else {
    console.log('  No customer details provided');
  }

  // Customer Object Reference
  console.log('\n🔗 CUSTOMER OBJECT:');
  console.log('  Customer ID:', session.customer || 'N/A');

  // Payment Method Details
  console.log('\n💳 PAYMENT METHOD:');
  if (session.payment_method_types && session.payment_method_types.length > 0) {
    console.log('  Types:', session.payment_method_types.join(', '));
  } else {
    console.log('  Types: N/A');
  }

  // Metadata (our custom data)
  console.log('\n🏷️  METADATA (Our Custom Fields):');
  if (session.metadata && Object.keys(session.metadata).length > 0) {
    Object.entries(session.metadata).forEach(([key, value]) => {
      console.log(`  ${key}:`, value);
    });
  } else {
    console.log('  No metadata');
  }

  // Line Items (what they purchased)
  console.log('\n🛒 LINE ITEMS:');
  if (session.line_items) {
    console.log('  Available in session object');
  } else {
    console.log('  Not expanded (need to fetch separately)');
  }

  // Additional Fields
  console.log('\n🔧 ADDITIONAL FIELDS:');
  console.log('  Client Reference ID:', session.client_reference_id || 'N/A');
  console.log('  Locale:', session.locale || 'N/A');
  console.log('  Success URL:', session.success_url || 'N/A');
  console.log('  Cancel URL:', session.cancel_url || 'N/A');

  // Shipping (if collected)
  if (session.shipping) {
    console.log('\n📦 SHIPPING (if collected):');
    console.log('  Name:', session.shipping.name || 'N/A');
    if (session.shipping.address) {
      const addr = session.shipping.address;
      console.log('  Address Line 1:', addr.line1 || 'N/A');
      console.log('  Address Line 2:', addr.line2 || 'N/A');
      console.log('  City:', addr.city || 'N/A');
      console.log('  State:', addr.state || 'N/A');
      console.log('  Postal Code:', addr.postal_code || 'N/A');
      console.log('  Country:', addr.country || 'N/A');
    }
  }

  // Tax IDs (if collected)
  if (session.customer_details?.tax_ids && session.customer_details.tax_ids.length > 0) {
    console.log('\n🆔 TAX IDS (if collected):');
    session.customer_details.tax_ids.forEach((taxId, index) => {
      console.log(`  Tax ID ${index + 1}:`, taxId.type, '-', taxId.value);
    });
  }

  console.log('\n╔════════════════════════════════════════════════════════════════╗');
  console.log('║                    END OF STRIPE DATA LOG                     ║');
  console.log('╚════════════════════════════════════════════════════════════════╝\n');
}

/**
 * POST /api/webhooks/stripe
 * Handle Stripe webhook events
 *
 * IMPORTANT: This endpoint requires raw body, not JSON parsed
 */
router.post('/stripe', express.raw({ type: 'application/json' }), async (req, res) => {
  const sig = req.headers['stripe-signature'];

  let event;

  try {
    // Verify webhook signature
    event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);
  } catch (err) {
    console.error('❌ Webhook signature verification failed:', err.message);
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  console.log(`📬 Received webhook: ${event.type}`);

  // Log comprehensive Stripe data for schema verification
  if (event.type === 'checkout.session.completed') {
    logStripeSessionData(event.data.object);
  }

  // Log full webhook payload for debugging and data extraction
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
  console.log('📦 Full Stripe Webhook Payload:');
  console.log(JSON.stringify(event, null, 2));
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');

  // Handle different event types
  switch (event.type) {
    case 'checkout.session.completed':
      await handleCheckoutSessionCompleted(event.data.object);
      break;

    case 'checkout.session.async_payment_succeeded':
      await handleAsyncPaymentSucceeded(event.data.object);
      break;

    case 'checkout.session.async_payment_failed':
      await handleAsyncPaymentFailed(event.data.object);
      break;

    case 'checkout.session.expired':
      await handleCheckoutSessionExpired(event.data.object);
      break;

    default:
      console.log(`ℹ️  Unhandled event type: ${event.type}`);
  }

  // Return 200 to acknowledge receipt
  res.json({ received: true });
});

/**
 * Handle checkout.session.completed event
 * This fires when payment succeeds (or session completes for delayed payment methods)
 */
async function handleCheckoutSessionCompleted(session) {
  console.log(`✅ Checkout session completed: ${session.id}`);

  try {
    // Find order in database
    const order = Order.findBySessionId(session.id);

    if (!order) {
      console.error(`❌ Order not found for session: ${session.id}`);
      return;
    }

    // 🔒 IDEMPOTENCY CHECK: Prevent duplicate processing
    if (order.status === 'fulfilled' || order.wren_order_id) {
      console.log(`⚠️  Order ${order.id} already fulfilled (status: ${order.status}), skipping duplicate webhook`);
      return;
    }

    if (order.status === 'paid') {
      console.log(`⚠️  Order ${order.id} already marked as paid, skipping payment update`);
      // Still attempt fulfillment if not fulfilled yet
      await fulfillOrder(order, session);
      return;
    }

    // Atomically update payment intent and status to prevent race conditions
    if (session.payment_intent) {
      Order.updatePaymentAndStatus(order.id, session.payment_intent, 'paid');
    } else {
      Order.updateStatus(order.id, 'paid');
    }

    console.log(`💳 Payment confirmed for order: ${order.id}`);
    console.log(`   Amount: $${(order.total_amount / 100).toFixed(2)}`);
    // (Detailed session data already logged above via logStripeSessionData())

    // Save order to NocoDB
    await saveOrderToNocoDB(order, session);

    // Fulfill order via Wren API
    await fulfillOrder(order, session);

  } catch (error) {
    console.error('❌ Error handling checkout session completed:', error);
  }
}

/**
 * Handle async payment succeeded (e.g., ACH, bank transfer)
 */
async function handleAsyncPaymentSucceeded(session) {
  console.log(`✅ Async payment succeeded: ${session.id}`);

  try {
    const order = Order.findBySessionId(session.id);

    if (!order) {
      console.error(`❌ Order not found for session: ${session.id}`);
      return;
    }

    // 🔒 IDEMPOTENCY CHECK: Prevent duplicate processing
    if (order.status === 'fulfilled' || order.wren_order_id) {
      console.log(`⚠️  Order ${order.id} already fulfilled, skipping duplicate webhook`);
      return;
    }

    if (order.status === 'paid') {
      console.log(`⚠️  Order ${order.id} already marked as paid, attempting fulfillment only`);
      await fulfillOrder(order, session);
      return;
    }

    // Update status to paid
    Order.updateStatus(order.id, 'paid');

    // Fulfill order
    await fulfillOrder(order, session);

  } catch (error) {
    console.error('❌ Error handling async payment succeeded:', error);
  }
}

/**
 * Handle async payment failed
 */
async function handleAsyncPaymentFailed(session) {
  console.log(`❌ Async payment failed: ${session.id}`);

  try {
    const order = Order.findBySessionId(session.id);

    if (!order) {
      console.error(`❌ Order not found for session: ${session.id}`);
      return;
    }

    // 🔒 IDEMPOTENCY CHECK: Only update if not already failed
    if (order.status === 'failed') {
      console.log(`⚠️  Order ${order.id} already marked as failed, skipping duplicate webhook`);
      return;
    }

    // Update status to failed
    Order.updateStatus(order.id, 'failed');

    console.log(`💔 Order ${order.id} marked as failed`);

    // TODO: Send notification to customer about failed payment

  } catch (error) {
    console.error('❌ Error handling async payment failed:', error);
  }
}

/**
 * Handle checkout session expired
 * This fires when a session expires (24 hours) without payment
 */
async function handleCheckoutSessionExpired(session) {
  console.log(`⏰ Checkout session expired: ${session.id}`);

  try {
    const order = Order.findBySessionId(session.id);

    if (!order) {
      console.error(`❌ Order not found for session: ${session.id}`);
      return;
    }

    // 🔒 IDEMPOTENCY CHECK: Only update if still pending
    if (order.status !== 'pending') {
      console.log(`⚠️  Order ${order.id} already processed (status: ${order.status}), skipping expiration`);
      return;
    }

    // Update status to expired
    Order.updateStatus(order.id, 'expired');

    console.log(`🕐 Order ${order.id} marked as expired (abandoned cart)`);
    console.log(`   Session was created but never completed`);

    // TODO: Optional - Send abandoned cart reminder email
    // TODO: Optional - Track abandonment metrics

  } catch (error) {
    console.error('❌ Error handling checkout session expired:', error);
  }
}

/**
 * Fulfill order by creating carbon offset via Wren API
 * @param {Object} order - Database order object
 * @param {Object} session - Stripe session object
 */
async function fulfillOrder(order, session) {
  try {
    // 🔒 IDEMPOTENCY CHECK: Don't fulfill if already fulfilled
    if (order.wren_order_id) {
      console.log(`⚠️  Order ${order.id} already has Wren order ID: ${order.wren_order_id}, skipping fulfillment`);
      return;
    }

    console.log(`🌱 Fulfilling order ${order.id} via Wren API...`);

    // Determine if we should use dry run mode based on environment
    const isDryRun = process.env.WREN_DRY_RUN === 'true';

    if (isDryRun) {
      console.log('⚠️  DRY RUN MODE: No real offset will be created');
    }

    // Create Wren offset order
    const wrenOrder = await createWrenOffsetOrder({
      tons: order.tons,
      portfolioId: order.portfolio_id,
      customerEmail: session.customer_details?.email || order.customer_email,
      dryRun: isDryRun,
    });

    // Update order with Wren order ID
    Order.updateWrenOrder(order.id, wrenOrder.id, 'fulfilled');

    console.log(`✅ Order ${order.id} fulfilled successfully`);
    console.log(`   Wren Order ID: ${wrenOrder.id}`);
    console.log(`   Tons offset: ${order.tons}`);

    // Update NocoDB with fulfillment data
    await updateNocoDBFulfillment(order.id, wrenOrder);

    // Send receipt email to customer
    const customerEmail = session.customer_details?.email || order.customer_email;
    if (customerEmail) {
      try {
        // Fetch portfolio data from Wren API
        let portfolioProjects = [];
        try {
          const portfolios = await getWrenPortfolios();
          const portfolio = portfolios.find(p => p.id === order.portfolio_id);

          if (portfolio && portfolio.projects) {
            // Format projects with colors and percentages
            portfolioProjects = formatPortfolioProjects(portfolio.projects);
            console.log(`✅ Portfolio data fetched: ${portfolioProjects.length} projects`);
          }
        } catch (portfolioError) {
          console.warn('⚠️  Failed to fetch portfolio data (non-fatal):', portfolioError.message);
          // Continue without portfolio data
        }

        // Calculate carbon impact comparisons
        const carbonComparisons = selectComparisons(order.tons, 3);
        console.log(`✅ Carbon comparisons calculated: ${carbonComparisons.length} items`);

        await sendReceiptEmail(customerEmail, {
          tons: order.tons,
          portfolioId: order.portfolio_id,
          baseAmount: (order.base_amount / 100).toFixed(2),
          processingFee: (order.processing_fee / 100).toFixed(2),
          totalAmount: (order.total_amount / 100).toFixed(2),
          orderId: order.id,
          stripeSessionId: session.id,
          projects: portfolioProjects,
          comparisons: carbonComparisons,
        });
        console.log(`📧 Receipt email sent to ${customerEmail}`);

        // Send admin notification (non-blocking)
        sendAdminNotification({
          tons: order.tons,
          portfolioId: order.portfolio_id,
          totalAmount: (order.total_amount / 100).toFixed(2),
          orderId: order.id,
        }, customerEmail).catch(err => {
          console.error('⚠️  Admin notification failed (non-fatal):', err.message);
        });
      } catch (emailError) {
        console.error('❌ Failed to send receipt email:', emailError);
        // Don't fail the order fulfillment if email fails
      }
    } else {
      console.warn('⚠️  No customer email available, skipping receipt email');
    }

  } catch (error) {
    console.error(`❌ Order fulfillment failed for order ${order.id}:`, error);

    // Mark order as paid but unfulfilled (manual intervention needed)
    Order.updateStatus(order.id, 'paid');

    // Send alert to admin about failed fulfillment
    const customerEmail = session.customer_details?.email || order.customer_email || 'unknown@example.com';
    sendAdminNotification({
      tons: order.tons,
      portfolioId: order.portfolio_id,
      totalAmount: (order.total_amount / 100).toFixed(2),
      orderId: order.id,
    }, customerEmail).catch(err => {
      console.error('❌ Failed to send admin alert for failed fulfillment:', err.message);
    });
  }
}

/**
 * Save order to NocoDB
 * @param {Object} order - Local database order object
 * @param {Object} session - Stripe session object
 */
async function saveOrderToNocoDB(order, session) {
  // Skip if NocoDB is not configured
  if (!nocodbClient.isConfigured()) {
    console.log('ℹ️  NocoDB not configured, skipping database save');
    return;
  }

  try {
    console.log(`💾 Saving order ${order.id} to NocoDB...`);

    // Map Stripe session data to NocoDB format
    const nocodbOrderData = mapStripeSessionToNocoDBOrder(session, order);

    // Create record in NocoDB
    const response = await nocodbClient.createOrder(nocodbOrderData);

    console.log(`✅ Order saved to NocoDB successfully`);
    console.log(`   NocoDB Record ID: ${response.Id}`);
    console.log(`   Order ID: ${nocodbOrderData.orderId}`);
    console.log(`   Customer: ${nocodbOrderData.customerName} (${nocodbOrderData.customerEmail})`);

    if (nocodbOrderData.businessName) {
      console.log(`   Business: ${nocodbOrderData.businessName}`);
    }

    if (nocodbOrderData.taxIdType && nocodbOrderData.taxIdValue) {
      console.log(`   Tax ID: ${nocodbOrderData.taxIdType} - ${nocodbOrderData.taxIdValue}`);
    }

  } catch (error) {
    console.error('❌ Failed to save order to NocoDB:', error.message);
    console.error('   This is non-fatal - order is still saved locally');
    // Don't throw - we don't want to fail the webhook if NocoDB is down
  }
}

/**
 * Update NocoDB with fulfillment data
 * @param {string} orderId - Order ID
 * @param {Object} wrenOrder - Wren order response
 */
async function updateNocoDBFulfillment(orderId, wrenOrder) {
  // Skip if NocoDB is not configured
  if (!nocodbClient.isConfigured()) {
    return;
  }

  try {
    console.log(`💾 Updating NocoDB with fulfillment data for order ${orderId}...`);

    // Map Wren fulfillment data
    const fulfillmentData = mapWrenFulfillmentData(orderId, wrenOrder);

    // Update NocoDB record
    await nocodbClient.updateOrderFulfillment(orderId, fulfillmentData);

    console.log(`✅ NocoDB updated with fulfillment data`);
    console.log(`   Wren Order ID: ${fulfillmentData.wrenOrderId}`);
    console.log(`   Status: ${fulfillmentData.status}`);

  } catch (error) {
    console.error('❌ Failed to update NocoDB with fulfillment:', error.message);
    // Non-fatal - don't throw
  }
}

export default router;
