const express = require('express');
const axios = require('axios');
const crypto = require('crypto');

const config = require('../config');
const { wrapAsync, commonErrorHandler } = require('../helpers');

const checkoutRouter = express.Router();
const webhookRouter = express.Router();

const STRIPE_API_BASE_URL = 'https://api.stripe.com/v1';
const DEFAULT_CURRENCY = 'usd';
const MAX_PAYMENT_AMOUNT = 50000;

const getStripeSecretKey = () => process.env.STRIPE_SECRET_KEY || config.stripe?.secretKey || '';
const getStripeWebhookSecret = () => process.env.STRIPE_WEBHOOK_SECRET || config.stripe?.webhookSecret || '';

const normalizeBaseUrl = (baseUrl) => {
  if (!baseUrl) return '';

  try {
    return new URL(baseUrl).origin;
  } catch (error) {
    return String(baseUrl).replace(/\/$/, '');
  }
};

const getClientBaseUrl = (req) => {
  const fromHeader = req.get('origin') || req.get('referer');
  const fromEnv = process.env.FRONTEND_URL || config.backUrl;

  return normalizeBaseUrl(fromHeader || fromEnv || '');
};

const toStripeAmount = (amount) => {
  const numericAmount = Number(amount);

  if (!Number.isFinite(numericAmount) || numericAmount <= 0) {
    return null;
  }

  if (numericAmount > MAX_PAYMENT_AMOUNT) {
    return null;
  }

  return Math.round(numericAmount * 100);
};

const appendMetadata = (params, metadata) => {
  Object.entries(metadata).forEach(([key, value]) => {
    if (value !== undefined && value !== null && String(value).trim()) {
      params.append(`metadata[${key}]`, String(value));
    }
  });
};

const stripeHeaders = (secretKey) => ({
  Authorization: `Bearer ${secretKey}`,
  'Content-Type': 'application/x-www-form-urlencoded',
});

checkoutRouter.post(
  '/create-checkout-session',
  wrapAsync(async (req, res) => {
    const stripeSecretKey = getStripeSecretKey();

    if (!stripeSecretKey) {
      return res.status(400).send({
        code: 'stripe_not_configured',
        message: 'Stripe is not configured yet. Add STRIPE_SECRET_KEY to the backend environment, then try again.',
      });
    }

    const {
      reservationId,
      reservationNumber,
      guestName,
      guestEmail,
      roomNumber,
      amount,
      currency = DEFAULT_CURRENCY,
    } = req.body;

    const amountInCents = toStripeAmount(amount);

    if (!amountInCents) {
      return res.status(400).send({
        code: 'invalid_payment_amount',
        message: `Payment amount must be greater than 0 and no more than ${MAX_PAYMENT_AMOUNT}.`,
      });
    }

    const clientBaseUrl = getClientBaseUrl(req);

    if (!clientBaseUrl) {
      return res.status(400).send({
        code: 'missing_client_url',
        message: 'Unable to determine the frontend URL for Stripe success and cancel redirects.',
      });
    }

    const safeReservationId = reservationId ? String(reservationId) : '';
    const safeReservationNumber = reservationNumber ? String(reservationNumber) : 'HotelPilot reservation';
    const safeGuestName = guestName ? String(guestName) : 'Hotel guest';
    const successUrl = `${clientBaseUrl}/stripe/success?session_id={CHECKOUT_SESSION_ID}&reservationId=${encodeURIComponent(safeReservationId)}`;
    const cancelUrl = `${clientBaseUrl}/stripe/cancel?reservationId=${encodeURIComponent(safeReservationId)}`;

    const params = new URLSearchParams();
    params.append('mode', 'payment');
    params.append('success_url', successUrl);
    params.append('cancel_url', cancelUrl);
    params.append('client_reference_id', safeReservationId || safeReservationNumber);
    params.append('line_items[0][price_data][currency]', String(currency).toLowerCase());
    params.append('line_items[0][price_data][unit_amount]', String(amountInCents));
    params.append('line_items[0][price_data][product_data][name]', `HotelPilot PMS · ${safeReservationNumber}`);
    params.append(
      'line_items[0][price_data][product_data][description]',
      `${safeGuestName}${roomNumber ? ` · Room ${roomNumber}` : ''}`,
    );
    params.append('line_items[0][quantity]', '1');

    if (guestEmail && String(guestEmail).includes('@')) {
      params.append('customer_email', String(guestEmail));
    }

    appendMetadata(params, {
      reservationId: safeReservationId,
      reservationNumber: safeReservationNumber,
      guestName: safeGuestName,
      roomNumber,
      createdByUserId: req.currentUser?.id,
      organizationId: req.currentUser?.organizationId,
      source: 'hotelpilot_front_desk',
    });

    try {
      const stripeResponse = await axios.post(
        `${STRIPE_API_BASE_URL}/checkout/sessions`,
        params,
        {
          headers: stripeHeaders(stripeSecretKey),
          timeout: 20000,
        },
      );

      return res.status(200).send({
        id: stripeResponse.data.id,
        url: stripeResponse.data.url,
        status: stripeResponse.data.status,
        payment_status: stripeResponse.data.payment_status,
      });
    } catch (error) {
      console.error('Stripe Checkout session creation failed', {
        status: error.response?.status,
        data: error.response?.data,
      });
      error.message = error.response?.data?.error?.message || error.message;
      throw error;
    }
  }),
);

checkoutRouter.get(
  '/checkout-session/:sessionId',
  wrapAsync(async (req, res) => {
    const stripeSecretKey = getStripeSecretKey();

    if (!stripeSecretKey) {
      return res.status(400).send({
        code: 'stripe_not_configured',
        message: 'Stripe is not configured yet. Add STRIPE_SECRET_KEY to the backend environment, then try again.',
      });
    }

    const { sessionId } = req.params;

    if (!sessionId || !sessionId.startsWith('cs_')) {
      return res.status(400).send({
        code: 'invalid_session_id',
        message: 'A valid Stripe Checkout session id is required.',
      });
    }

    try {
      const stripeResponse = await axios.get(
        `${STRIPE_API_BASE_URL}/checkout/sessions/${encodeURIComponent(sessionId)}`,
        {
          headers: { Authorization: `Bearer ${stripeSecretKey}` },
          timeout: 20000,
        },
      );

      const session = stripeResponse.data;

      return res.status(200).send({
        id: session.id,
        status: session.status,
        payment_status: session.payment_status,
        amount_total: session.amount_total,
        currency: session.currency,
        customer_email: session.customer_details?.email || session.customer_email,
        metadata: session.metadata,
      });
    } catch (error) {
      console.error('Stripe Checkout session retrieval failed', {
        sessionId,
        status: error.response?.status,
        data: error.response?.data,
      });
      error.message = error.response?.data?.error?.message || error.message;
      throw error;
    }
  }),
);

const verifyStripeSignature = (rawBody, signatureHeader, webhookSecret) => {
  if (!signatureHeader || !webhookSecret) return false;

  const timestamp = signatureHeader
    .split(',')
    .find((item) => item.startsWith('t='))
    ?.slice(2);
  const expectedSignature = signatureHeader
    .split(',')
    .find((item) => item.startsWith('v1='))
    ?.slice(3);

  if (!timestamp || !expectedSignature) return false;

  const signedPayload = `${timestamp}.${rawBody.toString('utf8')}`;
  const computedSignature = crypto
    .createHmac('sha256', webhookSecret)
    .update(signedPayload, 'utf8')
    .digest('hex');

  const computedBuffer = Buffer.from(computedSignature, 'hex');
  const expectedBuffer = Buffer.from(expectedSignature, 'hex');

  if (computedBuffer.length !== expectedBuffer.length) {
    return false;
  }

  return crypto.timingSafeEqual(computedBuffer, expectedBuffer);
};

webhookRouter.post(
  '/',
  express.raw({ type: 'application/json' }),
  wrapAsync(async (req, res) => {
    const webhookSecret = getStripeWebhookSecret();
    const signatureHeader = req.get('stripe-signature');

    if (!webhookSecret) {
      console.error('Stripe webhook rejected because STRIPE_WEBHOOK_SECRET is not configured.');
      return res.status(400).send({
        code: 'stripe_webhook_not_configured',
        message: 'Stripe webhook secret is not configured.',
      });
    }

    if (!verifyStripeSignature(req.body, signatureHeader, webhookSecret)) {
      console.error('Stripe webhook signature verification failed.');
      return res.status(400).send({
        code: 'invalid_stripe_signature',
        message: 'Invalid Stripe signature.',
      });
    }

    const event = JSON.parse(req.body.toString('utf8'));

    if (event.type === 'checkout.session.completed') {
      console.log('Stripe Checkout session completed', {
        sessionId: event.data?.object?.id,
        reservationId: event.data?.object?.metadata?.reservationId,
        amountTotal: event.data?.object?.amount_total,
      });
    }

    return res.status(200).send({ received: true, type: event.type });
  }),
);

checkoutRouter.use('/', commonErrorHandler);
webhookRouter.use('/', commonErrorHandler);

module.exports = {
  checkoutRouter,
  webhookRouter,
};
