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

/**
 * GET /api/presign/audio?slug=...&n=...&voice=...
 *
 * Returns a presigned MinIO URL for the audio file so the client can stream
 * it directly without going through the server.
 *
 * When the audio has not been generated yet, this endpoint automatically
 * enqueues a TTS generation job and returns 202 Accepted with the job status,
 * so callers can poll GET /api/audio/status/{slug}/{n}?voice=... until done,
 * then call this endpoint again to get the URL.
 *
 * Responses:
 *   200 { url: string }                              — audio ready, stream from MinIO
 *   202 { task_id: string, status: string }          — TTS enqueued, poll for completion
 *   202 { status: "pending"|"generating" }           — TTS already in progress
 *
 * Results are cached in-process for 50 minutes (MinIO URLs are valid 1 hour)
 * to avoid a backend + MinIO round-trip on every "Play" click.
 */
export const GET: RequestHandler = async ({ url }) => {
	const slug = url.searchParams.get('slug');
	// Accept both 'n' (web) and 'chapter' (iOS) as the chapter number param
	const n = parseInt(url.searchParams.get('n') ?? url.searchParams.get('chapter') ?? '', 10);
	const voice = url.searchParams.get('voice') ?? '';

	if (!slug || !n || n < 1) {
		error(400, 'Missing slug or n');
	}

	const cacheKey = cache.audioKey(slug, n, voice);

	// Fast path: return cached URL if still valid.
	const cached = await cache.get(cacheKey);
	if (cached) {
		return json({ url: cached });
	}

	// Slow path: call backend → MinIO presign.
	try {
		const presignedUrl = await presignAudio(slug, n, voice || undefined);
		await cache.set(cacheKey, presignedUrl);
		return json({ url: presignedUrl });
	} catch (e) {
		const status = (e as { status?: number }).status;
		if (status !== 404) {
			log.error('presign', 'presign audio failed', { slug, n, err: String(e) });
			error(500, `Could not get presigned URL: ${e}`);
		}
	}

	// Audio not found — automatically trigger TTS generation so the caller
	// doesn't need a separate POST step. Return 202 with the job status.
	log.info('presign', 'audio not found, triggering TTS generation', { slug, n, voice });
	try {
		const triggerRes = await backendFetch(`/api/audio/${slug}/${n}`, {
			method: 'POST',
			headers: { 'Content-Type': 'application/json' },
			body: JSON.stringify(voice ? { voice } : {})
		});

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

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

		// If the backend says it's already done (race: generated between presign
		// check and the POST), try to presign once more and return 200.
		if (triggerRes.status === 200 || ('status' in data && data.status === 'done')) {
			try {
				const presignedUrl = await presignAudio(slug, n, voice || undefined);
				await cache.set(cacheKey, presignedUrl);
				return json({ url: presignedUrl });
			} catch {
				// Ignore — fall through to 202 below.
			}
		}

		// Generation is in progress — return 202 so the caller can poll.
		return new Response(JSON.stringify(data), {
			status: 202,
			headers: { 'Content-Type': 'application/json' }
		});
	} catch (e) {
		if ((e as { status?: number }).status) throw e; // re-throw SvelteKit errors
		log.error('presign', 'audio trigger error', { slug, n, err: String(e) });
		error(500, `Could not trigger audio generation: ${e}`);
	}
};
