const {
	app,
	BrowserWindow,
	dialog,
	ipcMain,
	shell,
	Tray,
	Menu,
	clipboard,
	session,
} = require("electron");
const {autoUpdater} = require("electron-updater");
const fs = require("fs").promises;
const {existsSync, readFileSync} = require("fs");
const path = require("path");
const DownloadHistory = require("./src/history");

process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = "true";
autoUpdater.autoDownload = false;

const USER_DATA_PATH = app.getPath("userData");
const CONFIG_FILE_PATH = path.join(USER_DATA_PATH, "ytdownloader.json");

const appState = {
	/** @type {BrowserWindow | null} */
	mainWindow: null,
	/** @type {BrowserWindow | null} */
	secondaryWindow: null,
	/** @type {Tray | null} */
	tray: null,
	isQuitting: false,
	indexPageIsOpen: true,
	trayEnabled: false,
	loadedLanguage: {},
	config: {},
	downloadHistory: new DownloadHistory(),
	autoUpdateEnabled: false,
};

const gotTheLock = app.requestSingleInstanceLock();

if (!gotTheLock) {
	app.quit();
} else {
	app.on("second-instance", () => {
		if (appState.mainWindow) {
			if (appState.mainWindow.isMinimized())
				appState.mainWindow.restore();
			appState.mainWindow.show();
			appState.mainWindow.focus();
		}
	});
}

app.whenReady().then(async () => {
	await initialize();

	app.on("activate", () => {
		if (BrowserWindow.getAllWindows().length === 0) {
			createWindow();
		}
	});
});

app.on("before-quit", async () => {
	appState.isQuitting = true;
	try {
		// Save the final config state before exiting.
		await saveConfiguration();
	} catch (error) {
		console.error("Failed to save configuration during quit:", error);
	}
});

app.on("window-all-closed", () => {
	if (process.platform !== "darwin") {
		app.quit();
	}
});

/**
 * Initializes the application by loading config, translations,
 * and setting up handlers.
 */
async function initialize() {
	await loadConfiguration();
	await loadTranslations();

	registerIpcHandlers();
	registerAutoUpdaterEvents();

	createWindow();

	if (process.platform === "win32") {
		app.setAppUserModelId(app.name);
	}
}

function createWindow() {
	const bounds = appState.config.bounds || {};

	appState.mainWindow = new BrowserWindow({
		...bounds,
		minWidth: 800,
		minHeight: 600,
		autoHideMenuBar: true,
		show: false,
		icon: path.join(__dirname, "/assets/images/icon.png"),
		webPreferences: {
			nodeIntegration: true,
			contextIsolation: false,
			spellcheck: false,
		},
	});

	appState.mainWindow.loadFile("html/index.html");

	appState.mainWindow.once("ready-to-show", () => {
		if (appState.config.isMaximized) {
			appState.mainWindow.maximize();
		}
		appState.mainWindow.show();
	});

	const saveBounds = () => {
		if (appState.mainWindow && !appState.mainWindow.isMaximized()) {
			appState.config.bounds = appState.mainWindow.getBounds();
		}
	};

	appState.mainWindow.on("resize", saveBounds);
	appState.mainWindow.on("move", saveBounds);

	appState.mainWindow.on("maximize", () => {
		appState.config.isMaximized = true;
	});

	appState.mainWindow.on("unmaximize", () => {
		appState.config.isMaximized = false;
	});

	appState.mainWindow.on("close", (event) => {
		if (!appState.isQuitting && appState.trayEnabled) {
			event.preventDefault();
			appState.mainWindow.hide();
			if (app.dock) app.dock.hide();
		}
	});
}

/**
 * @param {string} file The HTML file to load.
 */
function createSecondaryWindow(file) {
	if (appState.secondaryWindow) {
		appState.secondaryWindow.focus();
		return;
	}

	appState.secondaryWindow = new BrowserWindow({
		parent: appState.mainWindow,
		modal: true,
		show: false,
		webPreferences: {
			nodeIntegration: true,
			contextIsolation: false,
		},
		width: 1000,
		height: 800,
	});

	// appState.secondaryWindow.webContents.openDevTools();
	appState.secondaryWindow.loadFile(file);
	appState.secondaryWindow.setMenu(null);
	appState.secondaryWindow.once("ready-to-show", () => {
		appState.secondaryWindow.show();
	});

	appState.secondaryWindow.on("closed", () => {
		appState.secondaryWindow = null;
	});
}

/**
 * Creates the system tray icon
 */
function createTray() {
	if (appState.tray) return;

	let iconPath;
	if (process.platform === "win32") {
		iconPath = path.join(__dirname, "resources/icon.ico");
	} else if (process.platform === "darwin") {
		iconPath = path.join(__dirname, "resources/icons/16x16.png");
	} else {
		iconPath = path.join(__dirname, "resources/icons/256x256.png");
	}

	appState.tray = new Tray(iconPath);

	const contextMenu = Menu.buildFromTemplate([
		{
			label: i18n("openApp"),
			click: () => {
				appState.mainWindow?.show();
				if (app.dock) app.dock.show();
			},
		},
		{
			label: i18n("pasteVideoLink"),
			click: async () => {
				const text = clipboard.readText();
				appState.mainWindow?.show();
				if (app.dock) app.dock.show();
				if (appState.indexPageIsOpen) {
					appState.mainWindow.webContents.send("link", text);
				} else {
					await appState.mainWindow.loadFile("html/index.html");
					appState.indexPageIsOpen = true;
					appState.mainWindow.webContents.once(
						"did-finish-load",
						() => {
							appState.mainWindow.webContents.send("link", text);
						},
					);
				}
			},
		},
		{
			label: i18n("downloadPlaylistButton"),
			click: () => {
				appState.indexPageIsOpen = false;
				appState.mainWindow?.loadFile("html/playlist.html");
				appState.mainWindow?.show();
				if (app.dock) app.dock.show();
			},
		},
		{
			label: i18n("quit"),
			click: () => {
				app.quit();
			},
		},
	]);

	appState.tray.setToolTip("ytDownloader");
	appState.tray.setContextMenu(contextMenu);
	appState.tray.on("click", () => {
		appState.mainWindow?.show();

		if (app.dock) app.dock.show();
	});
}

function registerIpcHandlers() {
	ipcMain.on("autoUpdate", (_event, status) => {
		appState.autoUpdateEnabled = status;

		if (status) {
			autoUpdater.checkForUpdates();
		}
	});

	ipcMain.on("reload", () => {
		appState.mainWindow?.reload();
		appState.secondaryWindow?.reload();
	});

	ipcMain.on("get-version", (event) => {
		event.sender.send("version", app.getVersion());
	});

	ipcMain.on("show-file", async (_event, fullPath) => {
		try {
			await fs.stat(fullPath);
			shell.showItemInFolder(fullPath);
		} catch (error) {}
	});

	ipcMain.handle("show-file", async (_event, fullPath) => {
		try {
			await fs.stat(fullPath);
			shell.showItemInFolder(fullPath);

			return {success: true};
		} catch (error) {
			return {success: false, error: error.message};
		}
	});

	ipcMain.handle("open-folder", async (_event, folderPath) => {
		try {
			await fs.stat(folderPath);
			const result = await shell.openPath(folderPath);
			if (result) {
				return {success: false, error: result};
			} else {
				return {success: true};
			}
		} catch (error) {
			return {success: false, error: error.message};
		}
	});

	ipcMain.on("load-win", (_event, file) => {
		appState.indexPageIsOpen = file.includes("index.html");
		appState.mainWindow?.loadFile(file);
	});

	ipcMain.on("load-page", (_event, file) => {
		createSecondaryWindow(file);
	});

	ipcMain.on("close-secondary", () => {
		appState.secondaryWindow?.close();
	});

	ipcMain.on("quit", () => {
		app.quit();
	});

	ipcMain.on("select-location-main", async () => {
		if (!appState.mainWindow) return;
		const {canceled, filePaths} = await dialog.showOpenDialog(
			appState.mainWindow,
			{properties: ["openDirectory"]},
		);
		if (!canceled && filePaths.length > 0) {
			appState.mainWindow.webContents.send("downloadPath", filePaths);
		}
	});

	ipcMain.on("select-location-secondary", async () => {
		if (!appState.secondaryWindow) return;
		const {canceled, filePaths} = await dialog.showOpenDialog(
			appState.secondaryWindow,
			{properties: ["openDirectory"]},
		);
		if (!canceled && filePaths.length > 0) {
			appState.secondaryWindow.webContents.send(
				"downloadPath",
				filePaths,
			);
		}
	});

	ipcMain.on("get-directory", async () => {
		if (!appState.mainWindow) return;
		const {canceled, filePaths} = await dialog.showOpenDialog(
			appState.mainWindow,
			{properties: ["openDirectory"]},
		);
		if (!canceled && filePaths.length > 0) {
			appState.mainWindow.webContents.send("directory-path", filePaths);
		}
	});

	ipcMain.on("select-config", async () => {
		if (!appState.secondaryWindow) return;
		const {canceled, filePaths} = await dialog.showOpenDialog(
			appState.secondaryWindow,
			{properties: ["openFile"]},
		);
		if (!canceled && filePaths.length > 0) {
			appState.secondaryWindow.webContents.send("configPath", filePaths);
		}
	});

	ipcMain.on("useTray", (_event, enabled) => {
		appState.trayEnabled = enabled;
		if (enabled) createTray();
		else {
			appState.tray?.destroy();
			appState.tray = null;
		}
	});

	ipcMain.on("progress", (_event, percentage) => {
		if (appState.mainWindow) appState.mainWindow.setProgressBar(percentage);
	});

	ipcMain.on("error_dialog", async (_event, message) => {
		const {response} = await dialog.showMessageBox(appState.mainWindow, {
			type: "error",
			title: "Error",
			message: message,
			buttons: ["Ok", i18n("clickToCopy")],
		});
		if (response === 1) clipboard.writeText(message);
	});

	ipcMain.handle("get-system-locale", async (_event) => {
		return app.getSystemLocale();
	});

	ipcMain.handle("get-translation", (_event, locale) => {
		const fallbackFile = path.join(__dirname, "translations", "en.json");
		const localeFile = path.join(
			__dirname,
			"translations",
			`${locale}.json`,
		);

		const fallbackData = JSON.parse(readFileSync(fallbackFile, "utf8"));

		let localeData = {};
		if (locale !== "en" && existsSync(localeFile)) {
			try {
				localeData = JSON.parse(readFileSync(localeFile, "utf8"));
			} catch (e) {
				console.error(`Could not parse ${localeFile}`, e);
			}
		}

		const mergedTranslations = {...fallbackData, ...localeData};

		return mergedTranslations;
	});

	ipcMain.handle("get-download-history", () =>
		appState.downloadHistory.getHistory(),
	);
	ipcMain.handle("add-to-history", (_, info) =>
		appState.downloadHistory.addDownload(info),
	);
	ipcMain.handle("get-download-stats", () =>
		appState.downloadHistory.getStats(),
	);
	ipcMain.handle("delete-history-item", (_, id) =>
		appState.downloadHistory.removeHistoryItem(id),
	);
	ipcMain.handle("clear-all-history", async () => {
		await appState.downloadHistory.clearHistory();
		return true;
	});
	ipcMain.handle("export-history-json", () =>
		appState.downloadHistory.exportAsJSON(),
	);
	ipcMain.handle("export-history-csv", () =>
		appState.downloadHistory.exportAsCSV(),
	);

	ipcMain.handle(
		"get-system-proxy",
		async (_event, targetUrl = "https://youtube.com") => {
			try {
				const proxyInfo =
					await session.defaultSession.resolveProxy(targetUrl);

				const rule = String(proxyInfo)
					.split(";")
					.map((s) => s.trim())
					.find((s) => s && s.toUpperCase() !== "DIRECT");

				if (!rule) {
					return null;
				}

				const [type, hostPort] = rule.split(/\s+/, 2);

				if (!type || !hostPort) {
					return null;
				}

				const protocol = {
					PROXY: "http://",
					HTTP: "http://",
					HTTPS: "https://",
					SOCKS: "socks5://",
					SOCKS5: "socks5://",
					SOCKS4: "socks4://",
				}[type.toUpperCase()];

				return protocol ? `${protocol}${hostPort}` : null;
			} catch (error) {
				console.error("Failed to get system proxy:", error);
				return null;
			}
		},
	);
}

function registerAutoUpdaterEvents() {
	autoUpdater.on("update-available", async (info) => {
		const dialogOpts = {
			type: "info",
			buttons: [i18n("update"), i18n("no")],
			title: "Update Available",
			message: i18n("updateAvailablePrompt"),
			detail:
				info.releaseNotes?.toString().replace(/<[^>]*>?/gm, "") ||
				"No details available.",
		};
		const {response} = await dialog.showMessageBox(
			appState.mainWindow,
			dialogOpts,
		);
		if (response === 0) {
			autoUpdater.downloadUpdate();
		}
	});

	autoUpdater.on("update-downloaded", async () => {
		appState.mainWindow.webContents.send("update-downloaded", "");
		const dialogOpts = {
			type: "info",
			buttons: [i18n("restart"), i18n("later")],
			title: "Update Ready",
			message: i18n("installAndRestartPrompt"),
		};
		const {response} = await dialog.showMessageBox(
			appState.mainWindow,
			dialogOpts,
		);
		if (response === 0) {
			autoUpdater.quitAndInstall(true, true);
		} else {
			// TODO: Consider if its worth enabling
			// autoUpdater.autoInstallOnAppQuit = true;
		}
	});

	autoUpdater.on("download-progress", async (info) => {
		appState.mainWindow.webContents.send("download-progress", info.percent);
	});

	autoUpdater.on("error", (error) => {
		console.error("Auto-update error:", error);
		// dialog.showErrorBox("Update Error", i18n("updateError"));
	});
}

/**
 * @param {string} phrase The key to translate.
 * @returns {string} The translated string or the key itself.
 */
function i18n(phrase) {
	return appState.loadedLanguage[phrase] || phrase;
}

/**
 * Loads the configuration from the config file.
 */
async function loadConfiguration() {
	try {
		const fileContent = await fs.readFile(CONFIG_FILE_PATH, "utf8");
		appState.config = JSON.parse(fileContent);
	} catch (error) {
		console.log(
			"Could not load config file, using defaults.",
			error.message,
		);
		appState.config = {
			bounds: {width: 1024, height: 768},
			isMaximized: false,
		};
	}
}

async function saveConfiguration() {
	try {
		await fs.writeFile(CONFIG_FILE_PATH, JSON.stringify(appState.config));
	} catch (error) {
		console.error("Failed to save configuration:", error);
	}
}

async function loadTranslations() {
	const locale = app.getSystemLocale();
	console.log({locale});
	const defaultLangPath = path.join(__dirname, "translations", "en.json");
	let langPath = path.join(__dirname, "translations", `${locale}.json`);

	try {
		await fs.access(langPath);
	} catch {
		langPath = defaultLangPath;
	}

	try {
		const fileContent = await fs.readFile(langPath, "utf8");
		appState.loadedLanguage = JSON.parse(fileContent);
	} catch (error) {
		console.error("Failed to load translation file:", error);
		appState.loadedLanguage = {};
	}
}
