import { join } from 'path';

import { app, shell, session, BrowserWindow, screen } from 'electron';
import { electronApp, optimizer, is } from '@electron-toolkit/utils';
import { createIPCHandler } from 'electron-trpc/main';
import Logger from 'electron-log/main';

import icon from '~build/icon.png?asset';
import { PreferencesSchema } from '~common/schemas';

import { appRouter } from './api/root';
import { stopSyncing, stopSeeding } from './modules/aria2';
import Preferences from './modules/preferences';
import Updater from './modules/updater';
import Addons from './modules/addons';
import Mods from './modules/mods';
import { initSelfUpdater } from './modules/selfUpdater';
import {
	detectHardware,
	recommendFarClip,
	HARDWARE_SCHEMA_VERSION
} from './modules/hardware';

Logger.initialize();
Logger.errorHandler.startCatching();
Logger.transports.ipc.level = false;
Logger.info('Launcher starting...');

app.disableHardwareAcceleration();

export let mainWindow: BrowserWindow | null = null;

const isOnScreen = (
	pos?: {
		x: number;
		y: number;
		width: number;
		height: number;
	} | null
) => {
	if (!pos) return false;
	return screen.getAllDisplays().some(d => {
		const a = d.workArea;
		return (
			pos.x < a.x + a.width &&
			pos.x + pos.width > a.x &&
			pos.y < a.y + a.height &&
			pos.y + pos.height > a.y
		);
	});
};

const createWindow = async () => {
	const saved =
		Preferences.data.rememberPosition &&
		isOnScreen(Preferences.data.windowPosition)
			? Preferences.data.windowPosition
			: undefined;
	const position = saved ?? { width: 1000, height: 700 };

	mainWindow = new BrowserWindow({
		...position,
		minWidth: 1000,
		minHeight: 700,
		icon,
		frame: false,
		maximizable: false,
		fullscreenable: false,
		webPreferences: {
			preload: join(__dirname, '../preload/index.js'),
			contextIsolation: true,
			sandbox: false,
			devTools: true
		}
	});

	mainWindow.webContents.on('render-process-gone', (_e, details) => {
		Logger.error('Renderer process gone:', details);
	});
	mainWindow.webContents.on('unresponsive', () => {
		Logger.error('Renderer unresponsive');
	});

	mainWindow.webContents.on(
		'console-message',
		(_e, level, message, line, sourceId) => {
			const lvl = level === 3 ? 'error' : level === 2 ? 'warn' : 'info';
			Logger[lvl](`[renderer:${lvl}] ${message} (${sourceId}:${line})`);
		}
	);

	mainWindow.webContents.on('before-input-event', (_e, input) => {
		if (input.type !== 'keyDown') return;
		if (input.key === 'F12') {
			mainWindow?.webContents.toggleDevTools();
			return;
		}
		if ((input.control || input.meta) && input.key.toLowerCase() === 'c')
			mainWindow?.webContents.copy();
	});

	createIPCHandler({ router: appRouter, windows: [mainWindow] });

	mainWindow.on('ready-to-show', () => {
		mainWindow?.show();
	});
	mainWindow.webContents.setWindowOpenHandler(details => {
		shell.openExternal(details.url);
		return { action: 'deny' };
	});
	mainWindow.on('close', () => {
		if (!mainWindow) return;
		const [x = 0, y = 0] = mainWindow.getPosition();
		const [width = 0, height = 0] = mainWindow.getSize();
		Preferences.data = { windowPosition: { x, y, width, height } };
	});

	if (is.dev && process.env.ELECTRON_RENDERER_URL) {
		mainWindow.loadURL(process.env.ELECTRON_RENDERER_URL);
	} else {
		mainWindow.loadFile(join(__dirname, '../renderer/index.html'));
	}
};

const gotSingleInstanceLock = is.dev || app.requestSingleInstanceLock();

if (!gotSingleInstanceLock) {
	app.quit();
} else {
	app.on('second-instance', () => {
		if (!mainWindow) return;
		if (mainWindow.isMinimized()) mainWindow.restore();
		if (!mainWindow.isVisible()) mainWindow.show();
		mainWindow.focus();
	});

	app.whenReady().then(async () => {
		// defaults on failure so createWindow() below still runs
		try {
			Preferences.data = await Preferences.load();
		} catch (e) {
			Logger.error('Preferences.load() failed; starting on defaults', e);
			Preferences.data = PreferencesSchema.parse({});
		}

		Addons.verify();
		Updater.verify();
		Mods.verify();
		initSelfUpdater();

		void (async () => {
			try {
				let hardware = Preferences.data.hardware;
				if (!hardware || hardware.schemaVersion < HARDWARE_SCHEMA_VERSION) {
					hardware = await detectHardware();
					Preferences.data = { hardware };
				}
				const rec = recommendFarClip(hardware ?? null);
				if (
					Preferences.data.farClipUserSet !== true &&
					Preferences.data.config.farClip !== rec
				)
					Preferences.data = {
						config: { ...Preferences.data.config, farClip: rec }
					};
			} catch (e) {
				Logger.error('Hardware detection / farClip recommendation failed', e);
			}
		})();

		electronApp.setAppUserModelId('st.octowow.launcher');

		if (app.isPackaged) {
			const serverOrigin = new URL(
				import.meta.env.MAIN_VITE_SERVER_URL || 'https://octowow.st'
			).origin;
			session.defaultSession.webRequest.onHeadersReceived((details, cb) => {
				cb({
					responseHeaders: {
						...details.responseHeaders,
						'Content-Security-Policy': [
							[
								"default-src 'self'",
								"script-src 'self'",
								"style-src 'self' 'unsafe-inline'",
								`img-src 'self' data: https://octowow.st https://forum.octowow.st ${serverOrigin}`,
								"font-src 'self' data:",
								"connect-src 'self'"
							].join('; ')
						]
					}
				});
			});
		}

		app.on('browser-window-created', (_, window) => {
			optimizer.watchWindowShortcuts(window);
		});

		await createWindow();
	});

	let settingsFlushed = false;
	app.on('before-quit', event => {
		stopSyncing();
		stopSeeding();
		if (settingsFlushed) return;
		settingsFlushed = true;
		event.preventDefault();
		Promise.race([Preferences.save(), new Promise(r => setTimeout(r, 3000))])
			.catch(e => Logger.error('Failed to flush settings before quit', e))
			.finally(() => app.quit());
	});

	app.on('window-all-closed', () => {
		app.quit();
	});
}
