import { app, protocol, session } from 'electron';
import { URL } from 'url';
import { createHash } from 'crypto';
import { mkdir, readFile, writeFile } from 'fs/promises';
import { join } from 'path';
import { existsSync } from 'node:fs';

const CACHE_DIR = join(process.env.XDG_CACHE_HOME || app.getPath('appData'), 'eve-cache');

const shouldCache = (url: URL) => url.protocol === 'https';
const key = (url: string) => createHash('sha256').update(url).digest('hex');
const metaPath = (k: string) => join(CACHE_DIR, `${k}.meta`);
const bodyPath = (k: string) => join(CACHE_DIR, `${k}.body`);

export async function addCacheHandling() {
	await mkdir(CACHE_DIR, { recursive: true });
	session.defaultSession.webRequest.onBeforeRequest(async (details, callback) => {
		const k = key(details.url);
		if (existsSync(bodyPath(k))) {
			const redir = `cache://${Buffer.from(details.url).toString('base64')}`;
			return callback({ redirectURL: redir });
		}

		const url = new URL(details.url);
		const allowedProtocols = ['file:', 'devtools:', 'eve:'];
		const allowedLocalPorts = ['4269', '6942', '5173'];
		const allowedImageExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.svg'];
		const isAllowed =
			allowedProtocols.includes(url.protocol) ||
			(url.hostname === 'localhost' && allowedLocalPorts.includes(url.port)) ||
			allowedImageExtensions.some((ext) => url.pathname.endsWith(ext)) ||
			['api.unisvg.com'].includes(url.hostname) ||
			(url.hostname === 'primal.b-cdn.net' &&
				url.pathname === '/media-upload' &&
				url.searchParams.get('u')?.endsWith('png')) ||
			url.hostname === 'cdn.tailwindcss.com' ||
			(url.hostname === 'api.iconify.design' && url.pathname.endsWith('.json')) ||
			url.host === 'esm.sh'; // TODO: esm.sh should NOT be used

		if (!isAllowed) {
			console.log(url);
			console.log('🚫 Blocked:', details.url);
		}
		callback({ cancel: !isAllowed });
	});

	session.defaultSession.webRequest.onCompleted({ urls: ['<all_urls>'] }, async (details) => {
		if (!shouldCache(new URL(details.url))) return;
		try {
			const res = await fetch(details.url);
			if (!res.ok) return;
			const buf = Buffer.from(await res.arrayBuffer());
			const k = key(details.url);
			await writeFile(metaPath(k), res.headers.get('content-type') || 'application/octet-stream');
			await writeFile(bodyPath(k), buf);
		} catch {}
	});

	protocol.registerSchemesAsPrivileged([{ scheme: 'eve-cache', privileges: { bypassCSP: true } }]);
	app.whenReady().then(() => {
		protocol.handle('eve-cache', async (req) => {
			const url = Buffer.from(req.url.slice('eve-cache://'.length), 'base64').toString();
			const k = key(url);
			const [mime, body] = await Promise.all([
				readFile(metaPath(k), 'utf8').catch(() => 'application/octet-stream'),
				readFile(bodyPath(k)).catch(() => null)
			]);
			if (!body) return new Response('Not found', { status: 404 });
			return new Response(body as unknown as BodyInit, { headers: { 'content-type': mime } });
		});
	});
}
