import { Router, Request, Response } from "express";
import Stripe from "stripe";
import { createProKey, downgradeByCustomer, updateEmailByCustomer } from "../services/keys.js";
import logger from "../services/logger.js";

function escapeHtml(s: string): string {
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
}

let _stripe: Stripe | null = null;
function getStripe(): Stripe {
  if (!_stripe) {
    const key = process.env.STRIPE_SECRET_KEY;
    if (!key) throw new Error("STRIPE_SECRET_KEY not configured");
    _stripe = new Stripe(key, { apiVersion: "2025-01-27.acacia" as any });
  }
  return _stripe;
}

const router = Router();

// Track provisioned session IDs to prevent duplicate key creation
const provisionedSessions = new Set<string>();

const DOCFAST_PRODUCT_ID = "prod_TygeG8tQPtEAdE";

// Returns true if the given Stripe subscription contains a DocFast product.
// Used to filter webhook events — this Stripe account is shared with other projects.
async function isDocFastSubscription(subscriptionId: string): Promise<boolean> {
  try {
    const sub = await getStripe().subscriptions.retrieve(subscriptionId, {
      expand: ["items.data.price.product"],
    });
    return sub.items.data.some((item) => {
      const price = item.price as Stripe.Price | null;
      const productId =
        typeof price?.product === "string"
          ? price.product
          : (price?.product as Stripe.Product | null)?.id;
      return productId === DOCFAST_PRODUCT_ID;
    });
  } catch (err: any) {
    logger.error({ err, subscriptionId }, "isDocFastSubscription: failed to retrieve subscription");
    return false;
  }
}

// Create a Stripe Checkout session for Pro subscription
router.post("/checkout", async (_req: Request, res: Response) => {
  try {
    const priceId = await getOrCreateProPrice();

    const session = await getStripe().checkout.sessions.create({
      mode: "subscription",
      payment_method_types: ["card"],
      line_items: [{ price: priceId, quantity: 1 }],
      success_url: `${process.env.BASE_URL || "https://docfast.dev"}/v1/billing/success?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${process.env.BASE_URL || "https://docfast.dev"}/#pricing`,
    });

    res.json({ url: session.url });
  } catch (err: any) {
    logger.error({ err }, "Checkout error");
    res.status(500).json({ error: "Failed to create checkout session" });
  }
});

// Success page — provision Pro API key after checkout
router.get("/success", async (req: Request, res: Response) => {
  const sessionId = req.query.session_id as string;
  if (!sessionId) {
    res.status(400).json({ error: "Missing session_id" });
    return;
  }

  // Prevent duplicate provisioning from same session
  if (provisionedSessions.has(sessionId)) {
    res.status(409).json({ error: "This checkout session has already been used to provision a key. If you lost your key, use the key recovery feature." });
    return;
  }

  try {
    const session = await getStripe().checkout.sessions.retrieve(sessionId);
    const customerId = session.customer as string;
    const email = session.customer_details?.email || "unknown@docfast.dev";

    if (!customerId) {
      res.status(400).json({ error: "No customer found" });
      return;
    }

    const keyInfo = await createProKey(email, customerId);
      provisionedSessions.add(session.id);

    // Return a nice HTML page instead of raw JSON
    res.send(`<!DOCTYPE html>
<html><head><title>Welcome to DocFast Pro!</title>
<style>
body { font-family: system-ui; background: #0a0a0a; color: #e8e8e8; display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0; }
.card { background: #141414; border: 1px solid #222; border-radius: 16px; padding: 48px; max-width: 500px; text-align: center; }
h1 { color: #4f9; margin-bottom: 8px; }
.key { background: #1a1a1a; border: 1px solid #333; border-radius: 8px; padding: 16px; margin: 24px 0; font-family: monospace; font-size: 0.9rem; word-break: break-all; cursor: pointer; }
.key:hover { border-color: #4f9; }
p { color: #888; line-height: 1.6; }
a { color: #4f9; }
</style></head><body>
<div class="card">
<h1>🎉 Welcome to Pro!</h1>
<p>Your API key:</p>
<div class="key" style="position:relative" data-key="${escapeHtml(keyInfo.key)}">${escapeHtml(keyInfo.key)}<button onclick="navigator.clipboard.writeText(this.parentElement.dataset.key);this.textContent='Copied!';setTimeout(()=>this.textContent='Copy',1500)" style="position:absolute;top:8px;right:8px;background:#4f9;color:#0a0a0a;border:none;border-radius:4px;padding:4px 12px;cursor:pointer;font-size:0.8rem;font-family:system-ui">Copy</button></div>
<p><strong>Save this key!</strong> It won't be shown again.</p>
<p>5,000 PDFs/month • All endpoints • Priority support</p>
<p><a href="/docs">View API docs →</a></p>
</div></body></html>`);
  } catch (err: any) {
    logger.error({ err }, "Success page error");
    res.status(500).json({ error: "Failed to retrieve session" });
  }
});

// Stripe webhook for subscription lifecycle events
router.post("/webhook", async (req: Request, res: Response) => {
  const sig = req.headers["stripe-signature"] as string;
  const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;

  let event: Stripe.Event;

  if (!webhookSecret) {
    logger.error("STRIPE_WEBHOOK_SECRET is not configured — refusing to process unverified webhooks");
    res.status(500).json({ error: "Webhook signature verification is not configured" });
    return;
  } else if (!sig) {
    res.status(400).json({ error: "Missing stripe-signature header" });
    return;
  } else {
    try {
      event = getStripe().webhooks.constructEvent(req.body, sig, webhookSecret);
    } catch (err: any) {
      logger.error({ err }, "Webhook signature verification failed");
      res.status(400).json({ error: "Invalid signature" });
      return;
    }
  }

  switch (event.type) {
    case "checkout.session.completed": {
      const session = event.data.object as Stripe.Checkout.Session;
      const customerId = session.customer as string;
      const email = session.customer_details?.email;

      // Filter by product — this Stripe account is shared with other projects
      try {
        const fullSession = await getStripe().checkout.sessions.retrieve(session.id, {
          expand: ["line_items"],
        });
        const lineItems = fullSession.line_items?.data || [];
        const hasDocfastProduct = lineItems.some((item) => {
          const price = item.price as Stripe.Price | null;
          const productId = typeof price?.product === "string" ? price.product : (price?.product as Stripe.Product)?.id;
          return productId === DOCFAST_PRODUCT_ID;
        });
        if (!hasDocfastProduct) {
          logger.info({ sessionId: session.id }, "Ignoring event for different product");
          break;
        }
      } catch (err: any) {
        logger.error({ err, sessionId: session.id }, "Failed to retrieve session line_items");
        break;
      }

      if (!customerId || !email) {
        logger.warn("checkout.session.completed: missing customerId or email, skipping key provisioning");
        break;
      }

      const keyInfo = await createProKey(email, customerId);
      provisionedSessions.add(session.id);
      logger.info({ email, customerId }, "checkout.session.completed: provisioned pro key");
      break;
    }
    case "customer.subscription.updated": {
      const sub = event.data.object as Stripe.Subscription;
      const customerId = sub.customer as string;
      const shouldDowngrade =
        sub.status === "canceled" ||
        sub.status === "past_due" ||
        sub.status === "unpaid" ||
        sub.cancel_at_period_end === true;

      if (shouldDowngrade) {
        if (!(await isDocFastSubscription(sub.id))) {
          logger.info({ subscriptionId: sub.id }, "customer.subscription.updated: ignoring event for different product");
          break;
        }
        await downgradeByCustomer(customerId);
        logger.info({ customerId, status: sub.status, cancelAtPeriodEnd: sub.cancel_at_period_end },
          "customer.subscription.updated: downgraded key to free tier");
      }
      break;
    }
    case "customer.subscription.deleted": {
      const sub = event.data.object as Stripe.Subscription;
      const customerId = sub.customer as string;

      if (!(await isDocFastSubscription(sub.id))) {
        logger.info({ subscriptionId: sub.id }, "customer.subscription.deleted: ignoring event for different product");
        break;
      }
      await downgradeByCustomer(customerId);
      logger.info({ customerId }, "customer.subscription.deleted: downgraded key to free tier");
      break;
    }
    case "customer.updated": {
      const customer = event.data.object as Stripe.Customer;
      const customerId = customer.id;
      const newEmail = customer.email;
      if (customerId && newEmail) {
        const updated = await updateEmailByCustomer(customerId, newEmail);
        if (updated) {
          logger.info({ customerId, newEmail }, "Customer email synced from Stripe");
        }
      }
      break;
    }
    default:
      break;
  }

  res.json({ received: true });
});

// --- Price management ---
let cachedPriceId: string | null = null;

async function getOrCreateProPrice(): Promise<string> {
  if (cachedPriceId) return cachedPriceId;

  const products = await getStripe().products.search({ query: "name:'DocFast Pro'" });
  let productId: string;

  if (products.data.length > 0) {
    productId = products.data[0].id;
    const prices = await getStripe().prices.list({ product: productId, active: true, limit: 1 });
    if (prices.data.length > 0) {
      cachedPriceId = prices.data[0].id;
      return cachedPriceId;
    }
  } else {
    const product = await getStripe().products.create({
      name: "DocFast Pro",
      description: "5,000 PDFs / month via API. HTML, Markdown, and URL to PDF.",
    });
    productId = product.id;
  }

  const price = await getStripe().prices.create({
    product: productId,
    unit_amount: 900,
    currency: "eur",
    recurring: { interval: "month" },
  });

  cachedPriceId = price.id;
  return cachedPriceId;
}

export { router as billingRouter };
