import { getSettings } from './settings.js';
import { ensureDirectory, stripTopLevelDirectory } from '$lib/file_utils.js';
import { join } from '@tauri-apps/api/path';
import { remove, readDir } from '@tauri-apps/plugin-fs';
import { invoke } from '@tauri-apps/api/core';
import { download } from '@tauri-apps/plugin-upload';
import { getInstalledVersions } from './library.js';
import { BASE_LIBRARY_DIR } from './settings.js';
import { writable, get } from 'svelte/store';

export const downloadTasksStore = writable([]);

/**
 * @typedef {Object} DownloadProgress
 * @property {number} loaded - Bytes downloaded
 * @property {number} total - Total bytes
 * @property {number} percent - Progress percentage (0-100)
 */

/**
 * Update the download progress for a specific version in the download tasks store.
 * @param {string} version - The Blender version
 * @param {DownloadProgress} progress - The download progress object
 */
export function updateDownloadProgress(version, progress) {
	downloadTasksStore.update((tasks) => {
		const existingIndex = tasks.findIndex((task) => task.version === version);
		if (existingIndex !== -1) {
			// Update existing task
			const updatedTasks = [...tasks];
			updatedTasks[existingIndex] = { ...updatedTasks[existingIndex], ...progress };
			return updatedTasks;
		} else {
			// Add new task
			return [...tasks, { version, ...progress }];
		}
	});
}

/**
 * Download and extract a Blender version.
 * @param {(progress: DownloadProgress) => void} [onProgress] - Optional progress callback
 * @returns {Promise<{success: boolean, message: string, path: string}>} Result
 */
export async function downloadBlenderVersion(versionInfo) {
	try {
		// Get settings
		const settings = await getSettings();
		const libraryDir = settings.libraryDir;
		if (!libraryDir) {
			return {
				success: false,
				message: 'Library directory not set. Please configure settings.',
				path: ''
			};
		}

		// Create target directory path
		const targetDir = await join(libraryDir, BASE_LIBRARY_DIR, 'blender', versionInfo.version);

		// Ensure the directory exists
		await ensureDirectory(targetDir);

		// Download the archive
		const downloadResult = await downloadFile(versionInfo.downloadUrl, targetDir, (progress) => {
			updateDownloadProgress(versionInfo.version, progress);
		});
		if (!downloadResult.success) {
			// Clean up target directory if it's empty (failed download)
			try {
				const entries = await readDir(targetDir);
				if (entries.length === 0) {
					await remove(targetDir);
				}
			} catch (cleanupError) {
				console.warn('Failed to clean up empty directory:', cleanupError);
			}
			return downloadResult;
		}

		const archivePath = downloadResult.path;

		// Extract the archive
		const extractResult = await extractArchive(
			archivePath,
			targetDir,
			versionInfo.platform,
			versionInfo.fileExtension,
			settings.keepDownloadedArchives ?? true
		);
		if (!extractResult.success) {
			return {
				success: false,
				message: extractResult.message,
				path: extractResult.path || ''
			};
		}

		//refresh installed versions
		await getInstalledVersions();
		return {
			success: true,
			message: `Successfully downloaded and extracted Blender ${versionInfo.version}`,
			path: targetDir
		};
	} catch (error) {
		console.error('Error downloading Blender version:', error);
		const errorMessage = error instanceof Error ? error.message : String(error);
		return {
			success: false,
			message: `Unexpected error: ${errorMessage}`,
			path: ''
		};
	}
}

/**
 * Download a file from URL to destination directory using optimized plugin.
 * @param {string} url - Download URL
 * @param {string} targetDir - Destination directory
 * @param {(progress: DownloadProgress) => void} [onProgress] - Optional progress callback
 * @returns {Promise<{success: boolean, message: string, path: string}>} Result with downloaded file path
 */
async function downloadFile(url, targetDir, onProgress) {
	try {
		// Extract filename from URL
		const urlObj = new URL(url);
		const filename = urlObj.pathname.split('/').pop() || 'blender-archive';
		const filePath = await join(targetDir, filename);

		// Optional: Mimic browser headers for better CDN performance
		const headers = new Map([
			['User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'],
			['Accept-Encoding', 'gzip, deflate, br'],
			['Accept', '*/*'],
			['Connection', 'keep-alive']
		]);

		// Use the optimized download function with proper async handling
		await download(
			url,
			filePath,
			(update) => {
				if (onProgress) {
					// Ensure progress updates are delivered asynchronously to prevent UI blocking
					queueMicrotask(() => {
						onProgress({
							loaded: update.progressTotal,
							total: update.total,
							percent:
								update.total > 0 ? Math.round((update.progressTotal / update.total) * 100) : 0
						});
					});
				}
			},
			headers
		);

		return {
			success: true,
			message: 'Download completed',
			path: filePath
		};
	} catch (error) {
		console.error('Error downloading file:', error);
		const errorMessage = error instanceof Error ? error.message : String(error);

		// Clean up partially downloaded file if it exists
		try {
			const urlObj = new URL(url);
			const filename = urlObj.pathname.split('/').pop() || 'blender-archive';
			const filePath = await join(targetDir, filename);
			await remove(filePath);
		} catch (cleanupError) {
			console.warn('Failed to clean up partially downloaded file:', cleanupError);
		}

		return {
			success: false,
			message: `Download error: ${errorMessage}`,
			path: ''
		};
	}
}

/**
 * Extract an archive using Rust-based extraction (supports .zip, .tar, .tar.gz, .tar.xz, .tar.bz2)
 * @param {string} archivePath - Path to archive file
 * @param {string} targetDir - Directory to extract into
 * @param {string} platform - "linux", "windows", "macos" (unused for Rust extraction)
 * @param {string} fileExtension - Archive extension
 * @param {boolean} keepArchive - Whether to keep the archive after extraction
 * @returns {Promise<{success: boolean, message: string, path?: string}>} Result
 */
async function extractArchive(archivePath, targetDir, platform, fileExtension, keepArchive) {
	try {
		// Use Rust-based extraction via Tauri command
		const result = await invoke('extract_archive', {
			archivePath,
			targetDir
		});

		// Extract succeeded - remove archive if not keeping it
		if (!keepArchive) {
			try {
				await remove(archivePath);
			} catch (error) {
				console.warn('Failed to remove archive:', error);
			}
		}

		// Flatten directory structure if archive created a top-level directory
		await stripTopLevelDirectory(targetDir);

		return {
			success: true,
			message: String(result),
			path: archivePath
		};
	} catch (error) {
		console.error('Error extracting archive:', error);
		const errorMessage = error instanceof Error ? error.message : String(error);

		return {
			success: false,
			message: `Extraction failed: ${errorMessage}`,
			path: archivePath
		};
	}
}

/**
 * Get the path where a Blender version should be installed.
 * @param {string} version - Version string
 * @returns {Promise<string>} Full path to version directory
 */
export async function getVersionPath(version) {
	const settings = await getSettings();
	const libraryDir = settings.libraryDir;
	if (!libraryDir) {
		return '';
	}
	return join(libraryDir, BASE_LIBRARY_DIR, 'blender', version);
}

/**
 * Check if a Blender version is already installed.
 * @param {string} version - Version string
 * @returns {Promise<boolean>} True if version directory exists and has content
 */
export async function isVersionInstalled(version) {
	try {
		const versionPath = await getVersionPath(version);
		if (!versionPath) return false;

		const entries = await readDir(versionPath);
		// Check if directory exists and has at least one entry (not empty)
		return entries.length > 0;
	} catch {
		return false;
	}
}
