import { error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { log } from '$lib/server/logger';
import { backendFetch } from '$lib/server/scraper';
import * as cache from '$lib/server/cache';

const FREE_DAILY_AUDIO_LIMIT = 3;

/**
 * Return the number of audio chapters a user/session has generated today,
 * and increment the counter. Uses a Valkey key that expires at midnight UTC.
 *
 * Key: audio:daily:<userId|sessionId>:<YYYY-MM-DD>
 */
async function incrementDailyAudioCount(identifier: string): Promise<number> {
	const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
	const key = `audio:daily:${identifier}:${today}`;
	// Seconds until end of day UTC
	const now = new Date();
	const endOfDay = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1));
	const ttl = Math.ceil((endOfDay.getTime() - now.getTime()) / 1000);
	// Use raw get/set with increment so we can read + increment atomically
	try {
		const raw = await cache.get<number>(key);
		const current = (raw ?? 0) + 1;
		await cache.set(key, current, ttl);
		return current;
	} catch {
		// On cache failure, fail open (don't block audio for cache errors)
		return 0;
	}
}

/**
 * POST /api/audio/[slug]/[n]
 * Proxies the audio generation request to the backend's /api/audio endpoint.
 * Keeps the backend URL server-side — the browser never needs to know it.
 *
 * Body: { voice?: string }
 *
 * Responses:
 *   200 { status: "done" }  — audio already cached; client should call
 *       GET /api/presign/audio to obtain a direct MinIO presigned URL.
 *   202 { task_id: string, status: "pending"|"generating" }  — generation
 *       enqueued; poll GET /api/audio/status/[slug]/[n]?voice=... until done.
 *   402 { error: "pro_required", limit: 3 }  — free daily limit reached.
 */
export const POST: RequestHandler = async ({ params, request, locals }) => {
	const { slug, n } = params;
	const chapter = parseInt(n, 10);
	if (!slug || !chapter || chapter < 1) {
		error(400, 'Invalid slug or chapter number');
	}

	// ── Paywall: 3 audio chapters/day for free users ───────────────────────────
	if (!locals.isPro) {
		// Check if audio already exists (cached) before counting — no charge for
		// re-requesting something already generated
		const statusRes = await backendFetch(
			`/api/audio/status/${slug}/${chapter}`
		).catch(() => null);
		const statusData = statusRes?.ok
			? ((await statusRes.json().catch(() => ({}))) as { status?: string })
			: {};

		if (statusData.status !== 'done') {
			const identifier = locals.user?.id ?? locals.sessionId;
			const count = await incrementDailyAudioCount(identifier);
			if (count > FREE_DAILY_AUDIO_LIMIT) {
				log.info('polar', 'free audio limit reached', { identifier, count });
				return new Response(
					JSON.stringify({ error: 'pro_required', limit: FREE_DAILY_AUDIO_LIMIT }),
					{ status: 402, headers: { 'Content-Type': 'application/json' } }
				);
			}
		}
	}

	let body: { voice?: string } = {};
	try {
		body = await request.json();
	} catch {
		// empty body is fine — scraper will use defaults
	}

	const scraperRes = await backendFetch(`/api/audio/${slug}/${chapter}`, {
		method: 'POST',
		headers: { 'Content-Type': 'application/json' },
		body: JSON.stringify(body)
	});

	if (!scraperRes.ok) {
		const text = await scraperRes.text().catch(() => '');
		log.error('audio', 'backend audio generation failed', { slug, chapter, status: scraperRes.status, body: text });
		error(scraperRes.status as Parameters<typeof error>[0], text || 'Audio generation failed');
	}

	const data = (await scraperRes.json()) as
		| { url: string; status: 'done' }
		| { task_id: string; status: string };

	// 202 Accepted: generation enqueued — return task_id + status for polling.
	if (scraperRes.status === 202 || 'task_id' in data) {
		return new Response(JSON.stringify(data), {
			status: 202,
			headers: { 'Content-Type': 'application/json' }
		});
	}

	// 200: audio was already cached.
	// Return status only — no url — so the client calls GET /api/presign/audio
	// and streams directly from MinIO instead of through the Node.js server.
	return new Response(
		JSON.stringify({ status: 'done' }),
		{ headers: { 'Content-Type': 'application/json' } }
	);
};
