import { randomRange } from "../_utils/math.utils";
import { App } from "../";
import { Vector2 } from "../math/vector2";
import { SystemManager } from "../system/systemManager";
import { TrackingManager } from "../tracking/trackingManager";
import { VirtualFile } from "../virtual-drive/file";

export interface WindowOptions {
	id?: string;
	app?: App;
	size?: Vector2;
	position?: Vector2;
	fullscreen?: boolean | string;
	options?: object;
	isFocused?: boolean;
	lastInteraction?: number;
	minimized?: boolean;
	[key: string]: unknown;
}

export class WindowsManager {
	windows: { [id: string]: WindowOptions };
	updateWindows: Function;
	startupComplete: boolean;

	#systemManager: SystemManager;
	#trackingManager: TrackingManager;

	constructor(systemManager: SystemManager, trackingManager: TrackingManager) {
		this.#systemManager = systemManager;
		this.#trackingManager = trackingManager;
		this.windows = {};
		this.updateWindows = () => {};
		this.startupComplete = false;
	}

	open(appId: string, options?: WindowOptions | null): object | null {
		const { appsConfig, windowsConfig, taskbarConfig } = this.#systemManager;
		const app = appsConfig.getAppById(appId);

		if (app == null) {
			console.warn(`Failed to open app ${appId}: app not found`);
			return null;
		}

		const size = options?.size ?? app.windowOptions?.size ?? new Vector2(700, 400);
		
		const availableScreenSpace = new Vector2(
			window.innerWidth - windowsConfig.screenMargin * 2,
			window.innerHeight - windowsConfig.screenMargin * 2 - taskbarConfig.height
		);

		let fullscreen = false;

		if (size.x > availableScreenSpace.x) {
			size.x = availableScreenSpace.x;
			fullscreen = true;
		} else if (size.y > availableScreenSpace.y) {
			size.y = availableScreenSpace.y;
			fullscreen = true;
		}

		const position = new Vector2(
			windowsConfig.screenMargin + randomRange(0, availableScreenSpace.x - size.x),
			windowsConfig.screenMargin + randomRange(0, availableScreenSpace.y - size.y)
		);

		if (options?.fullscreen) {
			if (typeof(options.fullscreen) == "string") {
				fullscreen = options.fullscreen.toLowerCase() === "true";
			} else {
				fullscreen = options.fullscreen;
			}

			delete options.fullscreen;
		}

		let id: number | string = 0;
		while (this.windowIds.includes(id.toString())) {
			id++;
		}

		id = id.toString();

		this.#trackingManager.event({
			category: "Actions",
			action: "Opened window",
			label: app.id
		});

		console.info(`Opening window ${id}:${app.id}`);

		this.windows[id] = {
			id,
			app,
			size,
			position,
			fullscreen,
			options: options as object | undefined,
		};

		this.focus(id);

		app.isActive = true;

		this.updateWindows(this.windows);
		return this.windows[id];
		// console.log(this);
	}

	openFile(file: VirtualFile, options: object = {}): object | null {
		if (file.extension == null) return null;

		const { appsConfig } = this.#systemManager;
		const app = appsConfig.getAppByFileExtension(file?.extension);
		if (app != null) {
			return this.open(app.id, { file, ...options });
		} else {
			return null;
		}
	}

	close(windowId: string) {
		windowId = windowId.toString();

		if (!this.windowIds.includes(windowId)) {
			console.warn(`Failed to close window ${windowId}: window not found`);
			return;
		}

		const { app } = this.windows[windowId];
		if (app != null) app.isActive = this.isAppActive(app.id);
		
		console.info(`Closing window ${windowId}`);
		delete this.windows[windowId];

		this.updateWindows(this.windows);
		// console.log(this);
	}

	focus(windowId: string) {
		windowId = windowId.toString();

		if (!this.windowIds.includes(windowId)) {
			console.warn(`Failed to focus window ${windowId}: window not found`);
			return;
		}

		Object.values(this.windows).forEach((window) => {
			const isFocused = (window.id === windowId);
			window.isFocused = isFocused;

			if (isFocused) {
				window.lastInteraction = Date.now().valueOf();
				window.minimized = false;
			}
		});

		this.updateWindows(this.windows);
	}

	isFocused(windowId: string) {
		return this.windows[windowId].isFocused;
	}

	isAnyFocused() {
		let anyFocused = false;

		Object.values(this.windows).forEach((window) => {
			if (window.isFocused)
				return anyFocused = true;
		});

		return anyFocused;
	}

	/**
	 * @param minimized - Leave as undefined to toggle the window's minimization state
	 */
	setMinimized(windowId: string, minimized?: boolean) {
		windowId = windowId.toString();

		if (!this.windowIds.includes(windowId)) {
			console.warn(`Failed to set minimized on window ${windowId}: window not found`);
			return;
		}

		const window = this.windows[windowId];
		window.minimized = minimized ?? !window.minimized;

		this.updateWindows(this.windows);
	}

	minimizeAll() {
		Object.values(this.windows).forEach((window) => {
			window.minimized = true;
		});

		this.updateWindows(this.windows);
	}

	isAppActive(appId: string): boolean {
		let active = false;

		Object.values(this.windows).forEach((window) => {
			if (window.app?.id === appId) {
				active = true;
				return;
			}
		});

		return active;
	}

	getAppWindowId(appId: string): string | null {
		let windowId: string | null = null;

		Object.values(this.windows).forEach((window) => {
			if (window.app?.id == appId)
				windowId = window.id as string;
		});

		return windowId;
	}

	setUpdateWindows(updateWindows: Function) {
		this.updateWindows = updateWindows;
	}

	startup(appIds: string[], options: {}) {
		if (appIds == null || this.startupComplete)
			return;

		appIds.forEach((appId) => {
			this.open(appId, options);
		});

		this.startupComplete = true;
	}

	get windowIds(): string[] {
		return Object.keys(this.windows);
	}
}