import type { ColorTheme, ThemeIcon } from 'vscode';
import { version as codeVersion, ColorThemeKind, env, ExtensionMode, Uri, window, workspace } from 'vscode';
import { getPlatform } from '@env/platform.js';
import { joinPaths, normalizePath } from '@gitlens/utils/path.js';
import { getDistributionGroup } from '@gitlens/utils/string.js';
import { satisfies } from '@gitlens/utils/version.js';
import type { IconPath } from '../../@types/vscode.iconpath.d.js';
import type { Container } from '../../container.js';
import { executeCoreCommand } from './command.js';
import { configuration } from './configuration.js';
import { exists } from './vscode/uris.js';

export const deviceCohortGroup = getDistributionGroup(env.machineId);

export function getExtensionModeLabel(mode: ExtensionMode): string {
	switch (mode) {
		case ExtensionMode.Production:
			return 'production';
		case ExtensionMode.Development:
			return 'dev';
		case ExtensionMode.Test:
			return 'test';
		default:
			// oxlint-disable-next-line typescript/restrict-template-expressions
			return `unknown (${mode})`;
	}
}

let _hostAppName: string | undefined | null;
export async function getHostAppName(): Promise<string | undefined> {
	if (_hostAppName !== undefined) return _hostAppName ?? undefined;

	switch (env.appName) {
		case 'Visual Studio Code':
			_hostAppName = 'code';
			break;
		case 'Visual Studio Code - Insiders':
			_hostAppName = 'code-insiders';
			break;
		case 'Visual Studio Code - Exploration':
			_hostAppName = 'code-exploration';
			break;
		case 'VSCodium':
			_hostAppName = 'codium';
			break;
		case 'Cursor':
			_hostAppName = 'cursor';
			break;
		case 'Windsurf':
			_hostAppName = 'windsurf';
			break;
		default: {
			try {
				const bytes = await workspace.fs.readFile(Uri.file(joinPaths(env.appRoot, 'product.json')));
				const product = JSON.parse(new TextDecoder().decode(bytes));
				_hostAppName = product.applicationName;
			} catch {
				_hostAppName = null;
			}

			break;
		}
	}

	return _hostAppName ?? undefined;
}

let _hostExecutablePath: string | undefined;
export async function getHostExecutablePath(): Promise<string> {
	if (_hostExecutablePath != null) return _hostExecutablePath;

	const platform = getPlatform();

	const app = (await getHostAppName()) ?? 'code';

	_hostExecutablePath = app;
	if (env.remoteName) return app;

	async function checkPath(path: string) {
		return (await exists(Uri.file(path))) ? path : undefined;
	}

	switch (platform) {
		case 'windows':
			_hostExecutablePath =
				// VS Code 1.110+ restructured its install directory, adding a commit-hash directory level (Windows only)
				(await checkPath(joinPaths(env.appRoot, '..', '..', '..', 'bin', app))) ??
				(await checkPath(joinPaths(env.appRoot, '..', '..', 'bin', app))) ??
				(await checkPath(joinPaths(env.appRoot, 'bin', app))) ??
				app;
			break;
		case 'linux':
			_hostExecutablePath =
				(await checkPath(joinPaths(env.appRoot, '..', '..', 'bin', app))) ??
				(await checkPath(joinPaths(env.appRoot, 'bin', app))) ??
				app;
			break;
		case 'macOS':
			_hostExecutablePath =
				(await checkPath(joinPaths(env.appRoot, 'bin', app))) ??
				(await checkPath(joinPaths(env.appRoot, '..', '..', 'bin', app))) ??
				app;
			break;
		default:
			throw new Error(`Unsupported platform: ${platform}`);
	}

	return _hostExecutablePath;
}

export async function getHostEditorCommand(includeWorkspaceUri: boolean = false): Promise<string> {
	const path = normalizePath(await getHostExecutablePath()).replace(/ /g, '\\ ');
	if (includeWorkspaceUri) {
		let uri = workspace.workspaceFile;
		if (uri != null) {
			return `${path} --wait --reuse-window --file-uri="${uri.toString()}"`;
		}

		uri = workspace.workspaceFolders?.[0]?.uri;
		if (uri != null) {
			return `${path} --wait --reuse-window --folder-uri="${uri.toString()}"`;
		}
	}

	return `${path} --wait --reuse-window`;
}

export function getIconPathUris(container: Container, filename: string): Exclude<IconPath, ThemeIcon> {
	return {
		dark: Uri.file(container.context.asAbsolutePath(`images/dark/${filename}`)),
		light: Uri.file(container.context.asAbsolutePath(`images/light/${filename}`)),
	};
}

export function getQuickPickIgnoreFocusOut(): boolean {
	return !configuration.get('advanced.quickPick.closeOnFocusOut');
}

export function isDarkTheme(theme: ColorTheme): boolean {
	return theme.kind === ColorThemeKind.Dark || theme.kind === ColorThemeKind.HighContrast;
}

export function isLightTheme(theme: ColorTheme): boolean {
	return theme.kind === ColorThemeKind.Light || theme.kind === ColorThemeKind.HighContrastLight;
}

export function isHostVSCode(hostAppName: string | undefined): boolean {
	return hostAppName === 'code' || hostAppName === 'code-insiders' || hostAppName === 'code-exploration';
}

export async function openWalkthrough(
	extensionId: string,
	walkthroughId: string,
	stepId?: string,
	openToSide: boolean = true,
): Promise<void> {
	// Only open to side if there is an active tab
	if (openToSide && window.tabGroups.activeTabGroup.activeTab == null) {
		openToSide = false;
	}

	// Takes the following params: walkthroughID: string | { category: string, step: string } | undefined, toSide: boolean | undefined
	void (await executeCoreCommand(
		'workbench.action.openWalkthrough',
		{
			category: `${extensionId}#${walkthroughId}`,
			step: stepId,
		},
		openToSide,
	));
}

export async function revealInFileExplorer(uri: Uri): Promise<void> {
	void (await executeCoreCommand('revealFileInOS', uri));
}

type CodeFeatures =
	| 'language-models'
	| 'quickpick-resourceuri'
	| 'quickpick-prompt'
	| 'quickpick-button-location'
	| 'quickpick-button-toggle';

const _supportedFeatureMap = new Map<CodeFeatures, boolean>();

export function supportedInVSCodeVersion(feature: CodeFeatures): boolean {
	let supported = _supportedFeatureMap.get(feature);
	if (supported == null) {
		switch (feature) {
			case 'language-models':
				supported = satisfies(codeVersion, '>= 1.90-insider');
				break;
			case 'quickpick-resourceuri':
			case 'quickpick-prompt':
				supported = satisfies(codeVersion, '>= 1.108');
				break;
			case 'quickpick-button-location':
			case 'quickpick-button-toggle':
				supported = satisfies(codeVersion, '>= 1.109');
				break;
			default:
				return false;
		}

		_supportedFeatureMap.set(feature, supported);
	}

	return supported;
}
