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;

function dailyAudioKey(identifier: string): string {
	const today = new Date().toISOString().slice(0, 10);
	return `audio:daily:${identifier}:${today}`;
}

/**
 * Return the number of audio chapters a user/session has generated today,
 * and increment the counter. Shared logic with POST /api/audio/[slug]/[n].
 *
 * Key: audio:daily:<userId|sessionId>:<YYYY-MM-DD>
 */
async function incrementDailyAudioCount(identifier: string): Promise<number> {
	const key = dailyAudioKey(identifier);
	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);
	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;
	}
}

/**
 * GET /api/audio-stream/[slug]/[n]?voice=...
 *
 * Proxies the backend's streaming TTS endpoint to the browser.
 *
 * Fast path: if audio already in MinIO, backend sends a 302 → fetch follows it
 * and we stream the MinIO audio through.
 *
 * Slow path (Kokoro/PocketTTS): backend streams audio bytes as they are
 * generated. The browser receives the first bytes within seconds and can start
 * playing before generation completes. MinIO upload happens concurrently
 * server-side so subsequent requests use the cached fast path.
 *
 * Slow path (CF AI): backend buffers the full response (batch API limitation)
 * before sending — effectively the same as the old POST+poll approach but
 * without the separate progress-bar flow. Prefer the traditional POST route
 * for CF AI voices to preserve the generating UI.
 */
export const GET: RequestHandler = async ({ params, url, locals }) => {
	const { slug, n } = params;
	const chapter = parseInt(n, 10);
	if (!slug || !chapter || chapter < 1) {
		error(400, 'Invalid slug or chapter number');
	}

	const voice = url.searchParams.get('voice') ?? '';

	// ── Paywall: 3 audio chapters/day for free users ─────────────────────────
	// Only count when the audio is not already cached — same rule as POST route.
	if (!locals.isPro) {
		const statusRes = await backendFetch(
			`/api/audio/status/${slug}/${chapter}${voice ? `?voice=${encodeURIComponent(voice)}` : ''}`
		).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 stream limit reached', { identifier, count });
				return new Response(
					JSON.stringify({ error: 'pro_required', limit: FREE_DAILY_AUDIO_LIMIT }),
					{ status: 402, headers: { 'Content-Type': 'application/json' } }
				);
			}
		}
	}

	const qs = new URLSearchParams({ format: 'mp3' });
	if (voice) qs.set('voice', voice);

	// fetch() follows the backend's 302 (MinIO fast path) automatically.
	const backendRes = await backendFetch(`/api/audio-stream/${slug}/${chapter}?${qs}`);

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

	// Stream the response body directly — no buffering.
	return new Response(backendRes.body, {
		status: 200,
		headers: {
			'Content-Type': backendRes.headers.get('Content-Type') ?? 'audio/mpeg',
			'Cache-Control': 'no-store',
			'X-Accel-Buffering': 'no'
		}
	});
};

/**
 * HEAD /api/audio-stream/[slug]/[n]?voice=...
 *
 * Paywall pre-check without incrementing the daily counter or triggering
 * any generation. The AudioPlayer uses this to surface the upgrade CTA
 * before pointing the <audio> element at the streaming URL.
 *
 * Returns 402 if the user has already hit their daily limit, 200 otherwise.
 */
export const HEAD: RequestHandler = async ({ locals }) => {
	if (locals.isPro) {
		return new Response(null, { status: 200 });
	}
	const identifier = locals.user?.id ?? locals.sessionId;
	const count = (await cache.get<number>(dailyAudioKey(identifier))) ?? 0;
	// count >= limit means the next GET would exceed the limit after increment
	if (count >= FREE_DAILY_AUDIO_LIMIT) {
		return new Response(null, { status: 402 });
	}
	return new Response(null, { status: 200 });
};
