import { currentSettings } from '$lib/settings.js';
import { createIcon, createBanner, ensureDirectory } from './file_utils';
import { platform } from '@tauri-apps/plugin-os';
import { join, localDataDir } from '@tauri-apps/api/path';
import { exists, stat, readDir, writeFile, remove } from '@tauri-apps/plugin-fs';
import { invoke } from '@tauri-apps/api/core';
import { writable, get } from 'svelte/store';
import { LazyStore } from '@tauri-apps/plugin-store';
import { BASE_LIBRARY_DIR } from '$lib/settings.js';
import { updateDownloadProgress } from './download';

/** INSTALLED BLENDER VERSION TYPE
 * @typedef {Object} blenderVersion
 * @property {string} version - Version string (e.g., "5.0.0")
 * @property {string} path - Full path to version directory
 * @property {string} executable - Path to Blender executable
 * @property {string} platform - Detected platform (windows, linux, macos)
 * @property {Date} [installDate] - Installation date (if available)
 * @property {Array<string>} [templates] - List of favourite scenes for this version
 */

export const installedVersionsStore = writable([]);
export const selectedVersionStore = writable(null);
const favouritesStore = new LazyStore('versions.json');

export async function selectVersion(version) {
	selectedVersionStore.set(version);
}

export async function generateTemplateBanner(templateDir) {
	const bannerPath = `${templateDir}/banner.png`;
	if (await exists(bannerPath)) {
		return;
	}
	try {
		const templateName = templateDir.split('/').pop() || templateDir.split('\\').pop();
		const banner = await createBanner(templateName);
		const bannerBuffer = new Uint8Array(await banner.arrayBuffer());
		await writeFile(bannerPath, bannerBuffer);
	} catch (error) {
		console.error(`Failed to generate banner for template: ${templateDir}`, error);
	}
}

export async function generateVersionBanner(version) {
	const bannerPath = await join(version.path, 'banner.png');
	if (await exists(bannerPath)) {
		return;
	}
	try {
		const banner = await createBanner(version.version);
		const bannerBuffer = new Uint8Array(await banner.arrayBuffer());
		await writeFile(bannerPath, bannerBuffer);
	} catch (error) {
		console.error(`Failed to generate banner for version: ${version.version}`, error);
	}
}

/**
 * Create icon and write it to disk
 * @param {blenderVersion} version
 */
export async function generateVersionIcon(version) {
	//
	//
	if (!(await exists(version.path))) {
		console.warn(`Version path does not exist: ${version.path}`);
		return;
	}
	const iconPath = `${version.path}/icon.png`;
	if (await exists(iconPath)) {
		return;
	}
	try {
		const newIcon = await createIcon(version.version);
		const iconBuffer = new Uint8Array(await newIcon.arrayBuffer());
		await writeFile(iconPath, iconBuffer);
	} catch (error) {
		console.warn('Failed to create or save icon:', error);
	}
}

/**
 * Clean up orphaned desktop files for versions that are no longer installed.
 * @param {blenderVersion[]} versions Array of currently installed versions
 */
export async function cleanupOrphanedDesktopFiles(versions) {
	const currentPlatform = await platform();
	if (currentPlatform !== 'linux') {
		return;
	}

	const installedVersionStrings = versions.map((v) => v.version);
	const applicationsDir = await join(await localDataDir(), 'applications');

	if (!(await exists(applicationsDir))) {
		return;
	}

	try {
		const entries = await readDir(applicationsDir);
		for (const entry of entries) {
			if (entry.isDirectory) {
				continue;
			}

			const fileName = entry.name;
			const match = fileName.match(/^blender-smoothie-(.+)\.desktop$/);
			if (match) {
				const version = match[1];
				if (!installedVersionStrings.includes(version)) {
					const desktopFilePath = await join(applicationsDir, fileName);
					try {
						await remove(desktopFilePath);
						console.log(`Removed orphaned desktop file: ${desktopFilePath}`);
					} catch (error) {
						console.warn(`Failed to remove orphaned desktop file ${desktopFilePath}:`, error);
					}
				}
			}
		}
	} catch (error) {
		console.warn('Error cleaning up orphaned desktop files:', error);
	}
}

/**
 * Remove the .desktop file for a Blender version (Linux only).
 * @param {blenderVersion} version
 */
export async function removeDesktopFile(version) {
	const currentPlatform = await platform();
	if (currentPlatform !== 'linux') {
		return;
	}

	const desktopFilePath = await join(
		await localDataDir(),
		`/applications/blender-smoothie-${version.version}.desktop`
	);
	if (!(await exists(desktopFilePath))) {
		return;
	}

	try {
		await remove(desktopFilePath);
	} catch (error) {
		console.warn('Failed to remove .desktop file:', error);
	}
}

/**
 * Remove all .desktop files for the given versions (Linux only).
 * @param {blenderVersion[]} versions
 */
export async function removeAllDesktopFilesForVersions(versions) {
	const currentPlatform = await platform();
	if (currentPlatform !== 'linux') {
		return;
	}

	for (const version of versions) {
		await removeDesktopFile(version);
	}
}

/**
 * Create a .desktop file for a Blender version (Linux only).
 * @param {blenderVersion} version
 */
export async function createDesktopFile(version, forceRecreate = false) {
	const currentPlatform = platform();
	if (currentPlatform !== 'linux') {
		return;
	}

	const desktopFilePath = await join(
		await localDataDir(),
		`/applications/blender-smoothie-${version.version}.desktop`
	);
	if (await exists(desktopFilePath)) {
		if (!forceRecreate) {
			return;
		}
		await remove(desktopFilePath);
	}

	try {
		const desktopFileContent = `[Desktop Entry]
Type=Application
Name=Blender ${version.version}
Comment=Blender ${version.version} - 3D creation suite
Exec=${version.executable.replace(/ /g, '\\ ')} %F
Icon=${version.path.replace(/ /g, '\\ ')}/icon.png
Terminal=false
Categories=Graphics;3DGraphics;
MimeType=application/x-blender;
`;
		console.log('creating desktop file:', desktopFilePath);
		await writeFile(desktopFilePath, new TextEncoder().encode(desktopFileContent));
	} catch (error) {
		console.warn('Failed to create .desktop file:', error);
	}
}

export async function removeTemplate(templatePath) {
	if (await exists(templatePath)) {
		try {
			await remove(templatePath, { recursive: true });
		} catch (error) {
			console.warn(`Failed to remove template path: ${templatePath}`, error);
		}
	} else {
		console.warn(`Template path does not exist: ${templatePath}`);
	}
}

export async function removeVersion(version) {
	if (await exists(version.path)) {
		try {
			await remove(version.path, { recursive: true });
		} catch (error) {
			console.warn(`Failed to remove version path: ${version.path}`, error);
		}
	} else {
		console.warn(`Version path does not exist: ${version.path}`);
	}
}

/**
 * Toggle the favourite status of a version.
 * @param {blenderVersion} version The version to toggle.
 */
export async function toggleFavourite(version) {
	const versions = get(installedVersionsStore);
	console.log();
	const versionObj = versions.find((v) => v.version === version.version);
	if (versionObj) {
		versionObj.favourite = !versionObj.favourite;
	} else {
		console.error('version not found', version);
	}
	installedVersionsStore.set(versions);
	await favouritesStore.set(version.version, versionObj.favourite);
	await favouritesStore.save();
}

/**
 * Remove a template from the library.
 * @param {blenderVersion} version The version to remove the template from.
 * @param {string} template The name of the template to remove.
 */
export async function deleteTemplate(version, template) {
  const templatePath = await join(await getTemplatesDir(version), template);
	try {
		await removeTemplate(templatePath);
		await getInstalledVersions();
		return { success: true, message: `Template "${template}" deleted` };
	} catch (error) {
		console.error('Failed to delete template', error);
		return { success: false, message: `Failed to delete template: ${error.message || error}` };
	}
}

/**
 * Delete a version and remove it from disk.
 * @param {blenderVersion} version The version to delete.
 */
export async function deleteVersion(version) {
	try {
		// Remove the version directory from disk
		await removeVersion(version);
		updateDownloadProgress(version.version, { loaded: 0, total: 0, percent: 0 });
		// Remove the .desktop file if it exists (Linux only)
		await removeDesktopFile(version);

		// Remove from favourites store
		await favouritesStore.delete(version.version);
		await favouritesStore.save();

		// Update the current installed versions list
		const versions = get(installedVersionsStore);
		const filteredVersions = versions.filter((v) => v.version !== version.version);
		installedVersionsStore.set(filteredVersions);

		console.log(`Successfully deleted version ${version.version}`);
		return { success: true, message: `Blender ${version.version} deleted` };
	} catch (error) {
		console.error(`Failed to delete version ${version.version}:`, error);
		return { success: false, message: `Failed to delete Blender ${version.version}: ${error.message || error}` };
	}
}

/**
 * Get all installed Blender versions from the library directory.
 * @returns {Promise<blenderVersion[]>} Array of installed versions
 */
export async function getInstalledVersions() {
	const favouriteEntries = await favouritesStore.entries();
	const favourites = Object.fromEntries(favouriteEntries);

	try {
		const settings = get(currentSettings);
		const libraryDir = settings.libraryDir;
		if (!libraryDir) {
			installedVersionsStore.set([]);
			return [];
		}
		const blenderLibraryPath = await join(libraryDir, BASE_LIBRARY_DIR, 'blender');

		if (!exists(blenderLibraryPath)) {
			installedVersionsStore.set([]);
			return [];
		}

		// Read all version directories
		const entries = await readDir(blenderLibraryPath);
		const versions = [];

		// Filter out non-directory entries
		for (const entry of entries) {
			if (!entry.isDirectory) {
				continue;
			}

			const version = entry.name;
			const versionPath = await join(blenderLibraryPath, version);

			try {
				// Find the Blender executable in this version directory
				const executable = await findBlenderExecutable(versionPath);
				if (executable) {
					const currentPlatform = platform();

					// Get templates for this version


					let newVersion = {
						version,
						path: versionPath,
						executable,
						platform: currentPlatform,
						favourite: favourites[version] || false,
						templates: []
          };

          var templates = await getTemplates(newVersion);

          newVersion.templates = templates;

					//check if version has an icon, if not generate one
					await generateVersionIcon(newVersion);
					await generateVersionBanner(newVersion);
					// create desktop file if autoCreateShortcuts is enabled
					if (settings.autoCreateShortcuts) {
						await createDesktopFile(newVersion);
					}

					versions.push(newVersion);
				} else {
					console.warn(`No executable found deleting folder ${version}`);
					//await remove(versionPath, { recursive: true });
				}
			} catch (error) {
				console.warn(`Error processing version ${version}:`, error);
				// Skip this version if we can't process it
			}
		}

		installedVersionsStore.set(versions);
		await cleanupOrphanedDesktopFiles(versions);
		const selectedVersion = get(selectedVersionStore);
		if (selectedVersion) {
			const refreshed = versions.find((v) => v.version === selectedVersion.version);
			selectedVersionStore.set(refreshed || selectedVersion);
		} else {
			if (versions.length > 0) {
				let favouriteVersion = versions.find((v) => v.favourite);
				if (favouriteVersion) {
					selectedVersionStore.set(favouriteVersion);
				} else {
					selectedVersionStore.set(versions[0]);
				}
			}
		}

		return versions;
	} catch (error) {
		console.error('Error getting installed versions:', error);
		installedVersionsStore.set([]);
		return [];
	}
}



/**
 *
 * @param {blenderVersion} version
 * @returns {Promise<string[]>} List of .blend files paths in the templates directory
 */
export async function getTemplates(version) {
	try {
		const templatesDir = await getTemplatesDir(version)
		await ensureDirectory(templatesDir);

		const entries = await readDir(templatesDir);
		const templates = [];

		const ignoredDirs = ['Storyboarding', '2D_Animation', 'Sculpting', 'VFX', 'Video_Editing'];
		for (const entry of entries) {
			if (entry.isDirectory && !ignoredDirs.includes(entry.name)) {
				const startupFile = await join(templatesDir, entry.name, 'startup.blend');
				await generateTemplateBanner(await join(templatesDir, entry.name));
				if (await exists(startupFile)) {
					templates.push(entry.name);
				}
			}
		}

		return templates;
	} catch (error) {
		console.error(error);
		return [];
	}
}
/**
 * Register a Blender version with the system.
 * @param {blenderVersion} version - The version object to launch
 * @returns {Promise<{success: boolean, message: string}>} Result of launch attempt
 */
export async function registerVersion(version) {
	try {
		if (!version || !version.executable) {
			return {
				success: false,
				message: 'Invalid version or executable not found'
			};
		}

		// Check if executable exists
		try {
			await stat(version.executable);
		} catch (error) {
			console.error(error);
			return {
				success: false,
				message: `Executable not found: ${version.executable}`
			};
		}
		// Launch the executable
		invoke('launch_binary', { path: version.executable, args: ['--register'] });
	} catch (error) {
		console.error('Error registering Blender:', error);
		const errorMessage = error instanceof Error ? error.message : String(error);
		return {
			success: false,
			message: `Registration failed: ${errorMessage}`
		};
	}
}

/**
 * Returns the templates directory for a given Blender version
 * @param {blenderVersion} version
 * @returns {Promise<string>}
 */
export async function getTemplatesDir(version) {

  const versionNumber = version?.version;


	try {
		const templatesDir = await join(
			version.path,
			versionNumber.split('.').slice(0, 2).join('.'),
			'scripts',
			'startup',
			'bl_app_templates_system'
				);

    await ensureDirectory(templatesDir);
		console.log(templatesDir)
		return templatesDir;
	} catch (error) {
		console.error(error);
		return null;
	}
}

/**
 *
 * @param {blenderVersion} version
 * @param {string} template
 * @returns {Promise<string>}
 */
export async function getTemplate(version, template) {
	try {
		// Launch the executable
		const templatesDir = await join(await getTemplatesDir(version),
			template
		);
		return templatesDir;
	} catch (error) {
		console.error(error);
		return null;
	}
}

/**
 * Launch a specific Blender version.
 * @param {blenderVersion} version - The version object to launch
 * @returns {Promise<{success: boolean, message: string}>} Result of launch attempt
 */
export async function launchBlenderVersion(version, template = null) {
	try {
		if (!version || !version.executable) {
			return {
				success: false,
				message: 'Invalid version or executable not found'
			};
		}

		// Check if executable exists
		try {
			await stat(version.executable);
		} catch (error) {
			console.error(error);
			return {
				success: false,
				message: `Executable not found: ${version.executable}`
			};
		}

		const settings = get(currentSettings);
		const libraryDir = settings.libraryDir;

		if (!libraryDir) {
			return {
				success: false,
				message: `couldn't find library`
			};
		}
		const blenderConfigPath = await join(libraryDir, BASE_LIBRARY_DIR, 'config', version.version);

		// Launch the executable
		let bannerPath;
		if (template) {
			const templateDir = await getTemplate(version, template);
			bannerPath = await join(templateDir, 'banner.png');
		} else {
			bannerPath = await join(version.path, 'banner.png');
		}
		const args = template ? ['--app-template', template] : [];

		/*if (template) {

		} else {
			bannerPath = await join(version.path, 'banner.png');
		} */

		console.log('launching', version.executable, args);
		invoke('launch_binary', {
			path: version.executable,
			args: args,
			env: {
				BLENDER_USER_RESOURCES: blenderConfigPath,
				BLENDER_CUSTOM_SPLASH_BANNER: bannerPath
			}
		});
	} catch (error) {
		console.error('Error launching Blender:', error);
		const errorMessage = error instanceof Error ? error.message : String(error);
		return {
			success: false,
			message: `. failed: ${errorMessage}`
		};
	}
}

/**
 *
 * @param {string} versionPath
 * @returns {Promise<string | null>} Path to the Blender executable, or null if not found
 */
async function findBlenderExecutable(versionPath) {
	const settings = get(currentSettings);
	const currentPlatform = settings.defaultPlatform;
	const platformStr = String(currentPlatform);

	// Map platform strings to executable names
	const executableMap = {
		windows: 'blender.exe',
		linux: 'blender',
		macos: 'blender'
		// Add more platforms here as needed
	};

	// Default to 'blender' if platform not in map
	const executableName = executableMap[platformStr] || 'blender';
	const executablePath = await join(versionPath, executableName);

	try {
		const stats = await stat(executablePath);
		if (stats.isFile) {
			return executablePath;
		}
	} catch (error) {
		// Executable not found at the expected path
		console.warn(`Blender executable not found at ${executablePath}:`, error);
	}

	return null;
}
