// src/routes/api/[...path]/+server.ts
import { BACKEND_API_URL } from '$lib/server/config';
import { json, error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';

// Helper function to handle both JSON and file responses
async function handleResponse(response: Response) {
	if (!response.ok) {
		throw error(response.status, await response.text());
	}

	const contentType = response.headers.get('Content-Type') || '';

	if (contentType.includes('application/json')) {
		// Handle JSON response
		const data = await response.json();
		return json(data);
	} else {
		// Handle file or other non-JSON response
		const responseBody = await response.arrayBuffer();

		return new Response(responseBody, {
			status: response.status,
			headers: {
				'Content-Type': contentType,
				'Content-Disposition': response.headers.get('Content-Disposition') || '',
				'Content-Length': response.headers.get('Content-Length') || ''
			}
		});
	}
}

/**
 * The body to forward, as fetch init options.
 *
 * Passed through as a stream rather than read into memory first: this process should
 * never hold a whole upload, which for a zipped Calibre library could be many
 * gigabytes. A request with no body contributes nothing, since `duplex` without a body
 * is rejected.
 */
function bodyOf(request: Request) {
	if (!request.body) return {};

	return { body: request.body, duplex: 'half' } as RequestInit;
}

// Shared function to prepare the request with authentication
function prepareRequest(locals: App.Locals, request: Request) {
	const token = locals.authToken || 'server-default-token';

	const headers = new Headers({
		Authorization: `Bearer ${token}`
	});

	// Forward important headers from the original request
	const contentType = request.headers.get('Content-Type');
	if (contentType) headers.set('Content-Type', contentType);

	const accept = request.headers.get('Accept');
	if (accept) headers.set('Accept', accept);

	return headers;
}

// GET handler
export const GET: RequestHandler = async ({ params, locals, fetch, request, url }) => {
	try {
		const path = params.path;
		const queryString = url.search;
		const backendUrl = `${BACKEND_API_URL}/${path}${queryString}`;
		const headers = prepareRequest(locals, request);

		const response = await fetch(backendUrl, {
			method: 'GET',
			headers
		});

		return handleResponse(response);
	} catch (e) {
		console.error('API proxy error (GET):', e);
		throw error(500, 'Failed to fetch from API');
	}
};

// POST handler
export const POST: RequestHandler = async ({ params, locals, fetch, request, url }) => {
	try {
		const path = params.path;
		const queryString = url.search;
		const backendUrl = `${BACKEND_API_URL}/${path}${queryString}`;
		const headers = prepareRequest(locals, request);

		const response = await fetch(backendUrl, {
			method: 'POST',
			headers,
			// Streamed, not buffered. `arrayBuffer()` held the whole upload in this
			// process before forwarding a byte of it, which is survivable for one book
			// and not for a zipped Calibre library. `duplex: 'half'` is required by the
			// fetch spec whenever the body is a stream.
			...bodyOf(request)
		});

		return handleResponse(response);
	} catch (e) {
		console.error('API proxy error (POST):', e);
		throw error(500, 'Failed to post to API');
	}
};

// PATCH handler
export const PATCH: RequestHandler = async ({ params, locals, fetch, request, url }) => {
	try {
		const path = params.path;
		const queryString = url.search;
		const backendUrl = `${BACKEND_API_URL}/${path}${queryString}`;
		const headers = prepareRequest(locals, request);

		const response = await fetch(backendUrl, {
			method: 'PATCH',
			headers,
			...bodyOf(request)
		});

		return handleResponse(response);
	} catch (e) {
		console.error('API proxy error (PATCH):', e);
		throw error(500, 'Failed to update API resource');
	}
};

// DELETE handler
export const DELETE: RequestHandler = async ({ params, locals, fetch, request, url }) => {
	try {
		const path = params.path;
		const queryString = url.search;
		const backendUrl = `${BACKEND_API_URL}/${path}${queryString}`;
		const headers = prepareRequest(locals, request);

		// DELETE may or may not have a body
		const options: RequestInit = {
			method: 'DELETE',
			headers
		};

		// Add body if the request has one
		if (
			request.headers.get('Content-Length') &&
			parseInt(request.headers.get('Content-Length') || '0') > 0
		) {
			options.body = await request.arrayBuffer();
		}

		const response = await fetch(backendUrl, options);

		return response;
	} catch (e) {
		console.error('API proxy error (DELETE):', e);
		throw error(500, 'Failed to delete API resource');
	}
};
