// Stripe billing — zero-dependency REST integration.
// One account-level subscription, owned by the workspace (superadmin manages).
//
// .env keys:
//   STRIPE_SECRET_KEY=sk_live_...   (or sk_test_... while testing)
//   STRIPE_PRICE_ID=price_...       (the recurring price of your plan)
//   APP_URL=https://your-domain.com (used for checkout/portal return URLs)
//
// Data: data/billing.json stores { stripeCustomerId }.

import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const STORE = path.join(__dirname, '..', 'data', 'billing.json');

export const stripeConfigured = () => Boolean(process.env.STRIPE_SECRET_KEY);

function loadStore() {
  try { return JSON.parse(fs.readFileSync(STORE, 'utf8')); } catch { return {}; }
}
function saveStore(o) {
  fs.mkdirSync(path.dirname(STORE), { recursive: true });
  fs.writeFileSync(STORE, JSON.stringify(o, null, 2));
}

// Minimal form-encoded Stripe API client (Stripe accepts application/x-www-form-urlencoded).
function enc(params, prefix = '') {
  const parts = [];
  for (const [k, v] of Object.entries(params || {})) {
    const key = prefix ? `${prefix}[${k}]` : k;
    if (v == null) continue;
    if (typeof v === 'object' && !Array.isArray(v)) parts.push(enc(v, key));
    else if (Array.isArray(v)) v.forEach((x, i) => parts.push(typeof x === 'object' ? enc(x, `${key}[${i}]`) : `${encodeURIComponent(`${key}[${i}]`)}=${encodeURIComponent(x)}`));
    else parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);
  }
  return parts.filter(Boolean).join('&');
}

async function stripe(pathname, method = 'GET', params = null) {
  const url = 'https://api.stripe.com/v1/' + pathname + (method === 'GET' && params ? '?' + enc(params) : '');
  const res = await fetch(url, {
    method,
    headers: {
      Authorization: `Bearer ${process.env.STRIPE_SECRET_KEY}`,
      ...(method !== 'GET' ? { 'Content-Type': 'application/x-www-form-urlencoded' } : {}),
    },
    body: method !== 'GET' && params ? enc(params) : undefined,
  });
  const data = await res.json();
  if (!res.ok) throw new Error(data?.error?.message || `Stripe API error (${res.status})`);
  return data;
}

// Find-or-create the workspace's Stripe customer.
async function ensureCustomer(email, name) {
  const store = loadStore();
  if (store.stripeCustomerId) {
    try {
      const c = await stripe('customers/' + store.stripeCustomerId);
      if (!c.deleted) return c.id;
    } catch { /* recreate below */ }
  }
  // Reuse an existing customer with this email if one exists (e.g. re-install)
  const found = await stripe('customers/search', 'GET', { query: `email:'${email.replace(/'/g, '')}'` }).catch(() => null);
  const id = found?.data?.[0]?.id || (await stripe('customers', 'POST', { email, name: name || 'SparkSoftware Reporting workspace' })).id;
  saveStore({ ...store, stripeCustomerId: id });
  return id;
}

// Current plan + invoices, ready for the Billing page.
export async function billingStatus(email, name) {
  if (!stripeConfigured()) return { configured: false };
  const customerId = await ensureCustomer(email, name);
  const [subs, invoices] = await Promise.all([
    stripe('subscriptions', 'GET', { customer: customerId, status: 'all', limit: 5, 'expand[]': 'data.items.data.price.product' }),
    stripe('invoices', 'GET', { customer: customerId, limit: 24 }),
  ]);
  const active = (subs.data || []).find((s) => ['active', 'trialing', 'past_due'].includes(s.status)) || subs.data?.[0] || null;
  let plan = null;
  if (active) {
    const item = active.items?.data?.[0];
    const price = item?.price;
    plan = {
      status: active.status,
      cancelAtPeriodEnd: Boolean(active.cancel_at_period_end),
      currentPeriodEnd: active.current_period_end ? new Date(active.current_period_end * 1000).toISOString().slice(0, 10) : null,
      productName: price?.product?.name || 'Subscription',
      amount: price?.unit_amount != null ? price.unit_amount / 100 : null,
      currency: (price?.currency || 'gbp').toUpperCase(),
      interval: price?.recurring?.interval || 'month',
    };
  }
  return {
    configured: true,
    customerId,
    plan,
    invoices: (invoices.data || []).map((inv) => ({
      number: inv.number || inv.id,
      date: new Date(inv.created * 1000).toISOString().slice(0, 10),
      amount: (inv.amount_paid != null && inv.amount_paid > 0 ? inv.amount_paid : inv.amount_due) / 100,
      currency: (inv.currency || 'gbp').toUpperCase(),
      status: inv.status,
      pdf: inv.invoice_pdf || null,          // direct PDF download, served by Stripe
      hosted: inv.hosted_invoice_url || null, // Stripe-hosted invoice page
    })),
  };
}

// Start a subscription — Stripe Checkout session URL.
export async function checkoutUrl(email, name, origin) {
  if (!stripeConfigured()) throw new Error('STRIPE_SECRET_KEY is not set in .env');
  if (!process.env.STRIPE_PRICE_ID) throw new Error('STRIPE_PRICE_ID is not set in .env');
  const customerId = await ensureCustomer(email, name);
  const base = process.env.APP_URL || origin;
  const session = await stripe('checkout/sessions', 'POST', {
    mode: 'subscription',
    customer: customerId,
    'line_items[0][price]': process.env.STRIPE_PRICE_ID,
    'line_items[0][quantity]': 1,
    success_url: `${base}/#/billing?checkout=success`,
    cancel_url: `${base}/#/billing?checkout=cancelled`,
    allow_promotion_codes: 'true',
  });
  return session.url;
}

// Manage the subscription (card, cancel, invoices) — Stripe Billing Portal.
export async function portalUrl(email, name, origin) {
  if (!stripeConfigured()) throw new Error('STRIPE_SECRET_KEY is not set in .env');
  const customerId = await ensureCustomer(email, name);
  const base = process.env.APP_URL || origin;
  const session = await stripe('billing_portal/sessions', 'POST', {
    customer: customerId,
    return_url: `${base}/#/billing`,
  });
  return session.url;
}
