import { ffi } from "../proc/native";
import sparkBunEventEmitter from "../events/eventEmitter";
import { type Pointer } from "bun:ffi";
import { WGPUView } from "./WGPUView";
import { BuildConfig } from "./BuildConfig";


export type GpuWindowOptionsType = {
	trafficLightOffset?: {
		x: number;
		y: number;
	};
	activate?: boolean;
	title: string;
	frame: {
		x: number;
		y: number;
		width: number;
		height: number;
	};
	styleMask?: {};
	titleBarStyle: "hidden" | "hiddenInset" | "default";
	transparent: boolean;
};

const defaultOptions: GpuWindowOptionsType = {
	title: "SparkBun",
	frame: {
		x: 0,
		y: 0,
		width: 800,
		height: 600,
	},
	titleBarStyle: "default",
	transparent: false,
};

const buildConfig = BuildConfig.getSync();
ffi.request.setExitOnLastWindowClosed({
	enabled: buildConfig.runtime?.exitOnLastWindowClosed ?? true,
});

export const GpuWindowMap: {
	[id: number]: GpuWindow;
} = {};

// Clean up JS wrapper state when a window closes. Native child cleanup is core-owned.
sparkBunEventEmitter.on("close", (event: { data: { id: number } }) => {
	const windowId = event.data.id;
	delete GpuWindowMap[windowId];

	// Clean up all WGPU views associated with this window
	for (const view of WGPUView.getAll()) {
		if (view.windowId === windowId) {
			view.remove();
		}
	}

});

export class GpuWindow {
	id = 0;
	title: string = "SparkBun";
	state: "creating" | "created" = "creating";
	transparent: boolean = false;
	trafficLightOffset: { x: number; y: number } = { x: 0, y: 0 };
	frame: {
		x: number;
		y: number;
		width: number;
		height: number;
	} = {
		x: 0,
		y: 0,
		width: 800,
		height: 600,
	};
	wgpuViewId!: number;

	get ptr(): Pointer | null {
		return ffi.request.getWindowPointer({ winId: this.id }) as Pointer | null;
	}

	constructor(options: Partial<GpuWindowOptionsType> = defaultOptions) {
		this.title = options.title || "New Window";
		this.frame = options.frame
			? { ...defaultOptions.frame, ...options.frame }
			: { ...defaultOptions.frame };
		this.transparent = options.transparent ?? false;
		this.trafficLightOffset = {
			x: options.trafficLightOffset?.x ?? 0,
			y: options.trafficLightOffset?.y ?? 0,
		};

		this.init(options);
	}

	init({
		styleMask,
		titleBarStyle,
		transparent,
		activate,
	}: Partial<GpuWindowOptionsType>) {
		const windowId = ffi.request.createWindow({
			title: this.title,
			url: "",
			frame: {
				width: this.frame.width,
				height: this.frame.height,
				x: this.frame.x,
				y: this.frame.y,
			},
			styleMask: {
				Borderless: false,
				Titled: true,
				Closable: true,
				Miniaturizable: true,
				Resizable: true,
				UnifiedTitleAndToolbar: false,
				FullScreen: false,
				FullSizeContentView: false,
				UtilityWindow: false,
				DocModalWindow: false,
				NonactivatingPanel: false,
				HUDWindow: false,
				...(styleMask || {}),
				// hiddenInset: transparent titlebar with inset native controls
				...(titleBarStyle === "hiddenInset"
					? {
							Titled: true,
							FullSizeContentView: true,
						}
					: {}),
				// hidden: no titlebar, no native controls (for fully custom chrome)
				...(titleBarStyle === "hidden"
					? {
							Titled: false,
							FullSizeContentView: true,
						}
					: {}),
			},
			titleBarStyle: titleBarStyle || "default",
			transparent: transparent ?? false,
			activate: activate ?? true,
			trafficLightOffset: this.trafficLightOffset,
		});

		if (!windowId) {
			throw "Failed to create window";
		}

		this.id = windowId as number;

		GpuWindowMap[this.id] = this;

		const wgpuView = new WGPUView({
			frame: {
				x: 0,
				y: 0,
				width: this.frame.width,
				height: this.frame.height,
			},
			windowId: this.id,
			autoResize: true,
			startTransparent: false,
			startPassthrough: false,
		});

		this.wgpuViewId = wgpuView.id;
	}

	get wgpuView() {
		return WGPUView.getById(this.wgpuViewId) as WGPUView;
	}

	static getById(id: number) {
		return GpuWindowMap[id];
	}

	setTitle(title: string) {
		this.title = title;
		return ffi.request.setTitle({ winId: this.id, title });
	}

	close() {
		return ffi.request.closeWindow({ winId: this.id });
	}

	activate() {
		return ffi.request.activateWindow({ winId: this.id });
	}

	focus() {
		console.log(
			"[sparkbun] GpuWindow.focus() is deprecated. Use window.activate() instead.",
		);
		return this.activate();
	}

	show() {
		return ffi.request.showWindow({ winId: this.id, activate: true });
	}

	showInactive() {
		return ffi.request.showWindow({ winId: this.id, activate: false });
	}

	minimize() {
		return ffi.request.minimizeWindow({ winId: this.id });
	}

	unminimize() {
		return ffi.request.restoreWindow({ winId: this.id });
	}

	isMinimized(): boolean {
		return ffi.request.isWindowMinimized({ winId: this.id });
	}

	maximize() {
		return ffi.request.maximizeWindow({ winId: this.id });
	}

	unmaximize() {
		return ffi.request.unmaximizeWindow({ winId: this.id });
	}

	isMaximized(): boolean {
		return ffi.request.isWindowMaximized({ winId: this.id });
	}

	setFullScreen(fullScreen: boolean) {
		return ffi.request.setWindowFullScreen({ winId: this.id, fullScreen });
	}

	isFullScreen(): boolean {
		return ffi.request.isWindowFullScreen({ winId: this.id });
	}

	setAlwaysOnTop(alwaysOnTop: boolean) {
		return ffi.request.setWindowAlwaysOnTop({ winId: this.id, alwaysOnTop });
	}

	isAlwaysOnTop(): boolean {
		return ffi.request.isWindowAlwaysOnTop({ winId: this.id });
	}

	setPosition(x: number, y: number) {
		this.frame.x = x;
		this.frame.y = y;
		return ffi.request.setWindowPosition({ winId: this.id, x, y });
	}

	setWindowButtonPosition(x: number, y: number) {
		return ffi.request.setWindowButtonPosition({ winId: this.id, x, y });
	}

	setSize(width: number, height: number) {
		this.frame.width = width;
		this.frame.height = height;
		return ffi.request.setWindowSize({ winId: this.id, width, height });
	}

	setFrame(x: number, y: number, width: number, height: number) {
		this.frame = { x, y, width, height };
		return ffi.request.setWindowFrame({ winId: this.id, x, y, width, height });
	}

	getFrame(): { x: number; y: number; width: number; height: number } {
		const frame = ffi.request.getWindowFrame({ winId: this.id });
		this.frame = frame;
		return frame;
	}

	getPosition(): { x: number; y: number } {
		const frame = this.getFrame();
		return { x: frame.x, y: frame.y };
	}

	getSize(): { width: number; height: number } {
		const frame = this.getFrame();
		return { width: frame.width, height: frame.height };
	}

	on(name: string, handler: (event: unknown) => void) {
		const specificName = `${name}-${this.id}`;
		sparkBunEventEmitter.on(specificName, handler);
	}
}
