/**
 * Polar.sh integration — server-side only.
 *
 * Responsibilities:
 *  - Verify webhook signatures (HMAC-SHA256)
 *  - Patch app_users.polar_customer_id / polar_subscription_id / role on subscription events
 *  - Expose isPro(userId) helper for gating
 *
 * Product IDs (Polar dashboard):
 *   Monthly : 1376fdf5-b6a9-492b-be70-7c905131c0f9
 *   Annual  : b6190307-79aa-4905-80c8-9ed941378d21
 *
 * Webhook event data shapes (Polar v1 API):
 *   subscription.*  → data.customer_id, data.product_id, data.status, data.customer.email
 *   order.created   → data.customer_id, data.product_id, data.customer.email, data.billing_reason
 */

import { createHmac, timingSafeEqual } from 'node:crypto';
import { env } from '$env/dynamic/private';
import { log } from '$lib/server/logger';
import { getUserByPolarCustomerId, patchUser } from '$lib/server/pocketbase';

export const POLAR_PRO_PRODUCT_IDS = new Set([
	'1376fdf5-b6a9-492b-be70-7c905131c0f9', // monthly
	'b6190307-79aa-4905-80c8-9ed941378d21'  // annual
]);

// ─── Webhook signature verification ──────────────────────────────────────────

/**
 * Verify the Polar webhook signature.
 * Polar signs with HMAC-SHA256 over the raw body; header is "webhook-signature".
 * Header format: "v1=<hex>"  (may be comma-separated list of sigs)
 */
export function verifyPolarWebhook(rawBody: string, signatureHeader: string): boolean {
	const secret = env.POLAR_WEBHOOK_SECRET;
	if (!secret) {
		log.warn('polar', 'POLAR_WEBHOOK_SECRET not set — rejecting webhook');
		return false;
	}

	const expected = createHmac('sha256', secret).update(rawBody).digest('hex');
	const expectedBuf = Buffer.from(`v1=${expected}`);

	// Header may contain multiple sigs separated by ", "
	const sigs = signatureHeader.split(',').map((s) => s.trim());
	for (const sig of sigs) {
		try {
			const sigBuf = Buffer.from(sig);
			if (sigBuf.length === expectedBuf.length && timingSafeEqual(sigBuf, expectedBuf)) {
				return true;
			}
		} catch {
			// length mismatch etc — try next
		}
	}
	return false;
}

// ─── Subscription event handler ───────────────────────────────────────────────

interface PolarCustomer {
	email?: string;
	external_id?: string; // our app_users.id if set on the customer
}

interface PolarSubscription {
	id: string;
	status: string; // "active" | "canceled" | "past_due" | "unpaid" | ...
	product_id: string;
	customer_id: string;
	customer?: PolarCustomer; // nested object — email lives here
}

/**
 * Resolve the app_user for a Polar customer.
 * Priority: polar_customer_id → email → customer.external_id (our user ID)
 */
async function resolveUser(customer_id: string, customer?: PolarCustomer) {
	const { getUserByEmail, getUserById } = await import('$lib/server/pocketbase');

	// 1. By stored polar_customer_id (fastest on repeat events)
	const byCustomerId = await getUserByPolarCustomerId(customer_id).catch(() => null);
	if (byCustomerId) return byCustomerId;

	// 2. By email (most common first-time path)
	const email = customer?.email;
	if (email) {
		const byEmail = await getUserByEmail(email).catch(() => null);
		if (byEmail) return byEmail;
	}

	// 3. By external_id = our user ID (if set via Polar API on customer creation)
	const externalId = customer?.external_id;
	if (externalId) {
		const byId = await getUserById(externalId).catch(() => null);
		if (byId) return byId;
	}

	return null;
}

/**
 * Handle a Polar subscription event.
 * Finds the matching app_user and updates role + polar fields.
 */
export async function handleSubscriptionEvent(
	eventType: string,
	subscription: PolarSubscription
): Promise<void> {
	const { id: subId, status, product_id, customer_id, customer } = subscription;

	log.info('polar', 'subscription event', {
		eventType, subId, status, product_id,
		customer_email: customer?.email
	});

	const user = await resolveUser(customer_id, customer);

	if (!user) {
		log.warn('polar', 'no app_user found for polar customer', {
			customer_email: customer?.email,
			customer_id
		});
		return;
	}

	const isProProduct = POLAR_PRO_PRODUCT_IDS.has(product_id);
	const isActive = status === 'active';
	const newRole = isProProduct && isActive ? 'pro' : (user.role === 'admin' ? 'admin' : 'user');

	await patchUser(user.id, {
		role: newRole,
		polar_customer_id: customer_id,
		polar_subscription_id: isActive ? subId : ''
	});

	log.info('polar', 'user role updated', {
		userId: user.id, username: user.username, newRole, status
	});
}

// ─── Order event handler ──────────────────────────────────────────────────────

interface PolarOrder {
	id: string;
	status: string;
	billing_reason: string; // "purchase" | "subscription_create" | "subscription_cycle" | "subscription_update"
	product_id: string | null;
	customer_id: string;
	subscription_id: string | null;
	customer?: PolarCustomer;
}

/**
 * Handle order.created — used for initial subscription purchases.
 * We only act on subscription_create billing_reason to avoid double-processing
 * (subscription.active will also fire, but this ensures we catch edge cases).
 */
export async function handleOrderCreated(order: PolarOrder): Promise<void> {
	const { id: orderId, billing_reason, product_id, customer_id, customer } = order;

	log.info('polar', 'order.created', { orderId, billing_reason, product_id, customer_email: customer?.email });

	// Only handle new subscription purchases here; renewals are handled by subscription.updated
	if (billing_reason !== 'purchase' && billing_reason !== 'subscription_create') {
		log.debug('polar', 'order.created — skipping non-purchase billing_reason', { billing_reason });
		return;
	}

	if (!product_id || !POLAR_PRO_PRODUCT_IDS.has(product_id)) {
		log.debug('polar', 'order.created — product not a pro product', { product_id });
		return;
	}

	const user = await resolveUser(customer_id, customer);
	if (!user) {
		log.warn('polar', 'order.created — no app_user found', {
			customer_email: customer?.email, customer_id
		});
		return;
	}

	// Only upgrade if not already pro/admin — subscription.active will do a full sync too
	if (user.role !== 'pro' && user.role !== 'admin') {
		await patchUser(user.id, {
			role: 'pro',
			polar_customer_id: customer_id
		});
		log.info('polar', 'order.created — user upgraded to pro', {
			userId: user.id, username: user.username
		});
	}
}
