use std::fs;
use std::path::{Path, PathBuf};
use tauri::{AppHandle, Manager, Emitter};
use walkdir::WalkDir;
use regex::Regex;
use std::ffi::OsStr;
use std::io::Read;

use crate::models::server::{ServerType, ServerInfo, ScanResult, ScanProgress};
use crate::models::plugin::{Plugin, PluginMeta};
use super::metadata_extractor::extract_plugin_metadata;

/// Scan a server directory and emit progress events
pub async fn scan_server_directory(app_handle: AppHandle, path: String) -> Result<(), String> {
    // Get the main window
    let window = app_handle.get_webview_window("main").ok_or("Main window not found")?;

    println!("Starting scan for server directory: {}", path);

    // Start the scan
    match window.emit("scan_started", {}) {
        Ok(_) => println!("Emitted scan_started event successfully"),
        Err(e) => {
            let err_msg = format!("Failed to emit scan_started event: {}", e);
            println!("{}", err_msg);
            return Err(err_msg);
        }
    }

    // Perform the actual scan in a separate function
    match perform_scan(&app_handle, &path).await {
        Ok(result) => {
            println!("Scan completed successfully. Found {} plugins", result.plugins.len());

            // Save the scan result to disk
            match save_plugin_data(app_handle.clone(), result.plugins.clone(), path.clone()).await {
                Ok(_) => println!("Saved plugin data to disk successfully"),
                Err(e) => println!("Failed to save plugin data: {}", e)
            }

            // Emit scan completion event with the result
            println!("Emitting scan_completed event with {} plugins", result.plugins.len());
            match window.emit("scan_completed", result.clone()) {
                Ok(_) => {
                    println!("Emitted scan_completed event successfully");
                    Ok(())
                },
                Err(e) => {
                    let err_msg = format!("Failed to emit scan_completed event: {}", e);
                    println!("{}", err_msg);
                    Err(err_msg)
                }
            }
        },
        Err(e) => {
            println!("Scan failed with error: {}", e);

            // Emit scan error event
            match window.emit("scan_error", e.clone()) {
                Ok(_) => println!("Emitted scan_error event successfully"),
                Err(err) => println!("Failed to emit scan_error event: {}", err)
            }

            Err(e)
        }
    }
}

/// Perform a scan of the server directory
pub async fn perform_scan(app_handle: &AppHandle, path: &str) -> Result<ScanResult, String> {
    // Normalize the path and check if it exists
    let server_path = Path::new(path);
    if !server_path.exists() {
        return Err(format!("Server directory not found: {}", path));
    }

    // Detect server type
    let server_type = detect_server_type(server_path);

    // Find Minecraft version
    let minecraft_version = detect_minecraft_version(server_path, &server_type);

    // Find plugins directory
    let plugins_dir = get_plugins_directory(server_path, &server_type);
    let plugins_path = Path::new(&plugins_dir);

    // Check if plugins directory exists
    if !plugins_path.exists() {
        return Err(format!("Plugins directory not found: {}", plugins_dir));
    }

    // Get all JAR files in plugins directory
    let mut plugin_files = Vec::new();
    for entry in WalkDir::new(plugins_path)
        .max_depth(1) // Only scan the top level
        .into_iter()
        .filter_map(|e| e.ok())
    {
        let path = entry.path();
        if path.is_file() && path.extension() == Some(OsStr::new("jar")) {
            plugin_files.push(path.to_path_buf());
        }
    }

    // Create server info object
    let server_info = ServerInfo {
        server_type,
        minecraft_version,
        plugins_directory: plugins_dir,
        plugins_count: plugin_files.len(),
    };

    // Emit total plugin count
    app_handle.emit("scan_progress", ScanProgress {
        processed: 0,
        total: plugin_files.len(),
        current_file: "Starting scan...".to_string(),
    }).map_err(|e| e.to_string())?;

    // Process each plugin
    let mut plugins = Vec::new();
    for (index, jar_path) in plugin_files.iter().enumerate() {
        // Emit progress update
        let file_name = jar_path.file_name()
            .and_then(|n| n.to_str())
            .unwrap_or("unknown.jar");

        app_handle.emit("scan_progress", ScanProgress {
            processed: index,
            total: plugin_files.len(),
            current_file: file_name.to_string(),
        }).map_err(|e| e.to_string())?;

        // Extract plugin metadata
        match extract_plugin_metadata(jar_path) {
            Ok(meta) => {
                // Convert PluginMeta to Plugin
                let plugin = Plugin {
                    name: meta.name,
                    version: meta.version,
                    latest_version: None, // Will be populated during update check
                    description: meta.description,
                    authors: meta.authors,
                    website: meta.website,
                    has_update: false, // Will be populated during update check
                    api_version: meta.api_version,
                    main_class: meta.main_class,
                    depend: meta.depend,
                    soft_depend: meta.soft_depend,
                    load_before: meta.load_before,
                    commands: meta.commands,
                    permissions: meta.permissions,
                    file_path: meta.file_path,
                    file_hash: meta.file_hash,
                    changelog: None, // Will be populated during update check
                    repository_source: None, // Will be populated during update check
                    repository_id: None, // Will be populated during update check
                    repository_url: None, // Will be populated during update check
                };
                plugins.push(plugin);
            },
            Err(e) => {
                // Log the error but continue processing other plugins
                eprintln!("Error extracting metadata from {}: {}", file_name, e);
            }
        }
    }

    // Emit final progress
    app_handle.emit("scan_progress", ScanProgress {
        processed: plugin_files.len(),
        total: plugin_files.len(),
        current_file: "Scan complete".to_string(),
    }).map_err(|e| e.to_string())?;

    // Return the result
    Ok(ScanResult {
        server_info,
        plugins,
    })
}

/// Detect the type of Minecraft server
fn detect_server_type(server_path: &Path) -> ServerType {
    // Check for server JAR file
    if let Some(jar_path) = find_server_jar(server_path) {
        let jar_filename = jar_path.file_name()
            .and_then(|n| n.to_str())
            .unwrap_or("");

        // Convert filename to lowercase for easier matching
        let filename_lower = jar_filename.to_lowercase();

        // Check for known server types
        if filename_lower.contains("paper") {
            return ServerType::Paper;
        } else if filename_lower.contains("spigot") {
            return ServerType::Spigot;
        } else if filename_lower.contains("bukkit") || filename_lower.contains("craftbukkit") {
            return ServerType::Bukkit;
        } else if filename_lower.contains("forge") {
            return ServerType::Forge;
        } else if filename_lower.contains("fabric") {
            return ServerType::Fabric;
        } else if filename_lower.contains("velocity") {
            return ServerType::Velocity;
        } else if filename_lower.contains("bungeecord") {
            return ServerType::BungeeCord;
        } else if filename_lower.contains("waterfall") {
            return ServerType::Waterfall;
        }

        // If server.properties exists, it's likely a vanilla or modified vanilla server
        if server_path.join("server.properties").exists() {
            // Check if there's a plugins directory
            if server_path.join("plugins").exists() {
                // Assume Spigot if we can't tell more specifically but plugins exist
                return ServerType::Spigot;
            }
            return ServerType::Vanilla;
        }
    }

    // If we can't determine, check for directory structure hints
    if server_path.join("plugins").exists() {
        if server_path.join("cache").exists() && server_path.join("modules").exists() {
            return ServerType::Velocity;
        } else if server_path.join("libraries").exists() && server_path.join("mods").exists() {
            return ServerType::Forge;
        } else {
            return ServerType::Spigot; // Default assumption for server with plugins
        }
    }

    ServerType::Unknown
}

/// Find the server JAR file in the server directory
fn find_server_jar(server_path: &Path) -> Option<PathBuf> {
    // Define pattern for server JAR files
    let server_jar_pattern = Regex::new(r"^(paper|spigot|craftbukkit|minecraft|fabric|forge|velocity|bungeecord|waterfall).*\.jar$").unwrap();

    // Look for JAR files in the directory
    let entries = match fs::read_dir(server_path) {
        Ok(entries) => entries,
        Err(_) => return None,
    };

    // Check each entry
    for entry in entries {
        if let Ok(entry) = entry {
            if let Some(filename) = entry.file_name().to_str() {
                // Check if it matches server JAR pattern
                if filename.to_lowercase().ends_with(".jar") {
                    if server_jar_pattern.is_match(&filename.to_lowercase()) {
                        return Some(entry.path());
                    }

                    // Also check for common naming patterns
                    if filename.contains("server") || filename == "server.jar" {
                        return Some(entry.path());
                    }
                }
            }
        }
    }

    None
}

/// Detect the Minecraft version from server files
fn detect_minecraft_version(server_path: &Path, server_type: &ServerType) -> Option<String> {
    // Try to find server JAR
    if let Some(jar_path) = find_server_jar(server_path) {
        // Try to extract version from JAR filename
        if let Some(filename) = jar_path.file_name().and_then(|n| n.to_str()) {
            // Look for version pattern like 1.19.2 in filename
            let version_pattern = Regex::new(r"(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)").unwrap();
            if let Some(captures) = version_pattern.captures(filename) {
                if let Some(version_match) = captures.get(0) {
                    return Some(version_match.as_str().to_string());
                }
            }
        }

        // If version not found in filename, try to read it from the JAR
        return read_version_from_jar(&jar_path);
    }

    // Try server.properties for vanilla/bukkit/spigot servers
    if matches!(server_type, ServerType::Vanilla | ServerType::Bukkit | ServerType::Spigot | ServerType::Paper) {
        let properties_path = server_path.join("server.properties");
        if properties_path.exists() {
            // Read properties file
            if let Ok(content) = fs::read_to_string(properties_path) {
                // Look for the level-type property
                for line in content.lines() {
                    if line.starts_with("level-name=") {
                        // Try to find version.json in the level directory
                        let level_name = line.trim_start_matches("level-name=").trim();
                        let version_json_path = server_path.join(level_name).join("version.json");
                        if version_json_path.exists() {
                            if let Ok(version_content) = fs::read_to_string(version_json_path) {
                                if let Ok(json) = serde_json::from_str::<serde_json::Value>(&version_content) {
                                    if let Some(name) = json.get("name").and_then(|n| n.as_str()) {
                                        return Some(name.to_string());
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    None
}

/// Read version information from a JAR file
fn read_version_from_jar(jar_path: &Path) -> Option<String> {
    // Open the JAR file
    let file = match fs::File::open(jar_path) {
        Ok(file) => file,
        Err(_) => return None,
    };

    // Create a ZIP archive reader
    let mut archive = match zip::ZipArchive::new(file) {
        Ok(archive) => archive,
        Err(_) => return None,
    };

    // Try to find version.json or similar file
    for i in 0..archive.len() {
        let mut file = match archive.by_index(i) {
            Ok(file) => file,
            Err(_) => continue,
        };

        let name = file.name();

        // Check various version files
        if name.ends_with("version.json") || name.contains("version") && name.ends_with(".json") {
            let mut contents = String::new();
            if let Err(_) = file.read_to_string(&mut contents) {
                continue;
            }

            if let Ok(json) = serde_json::from_str::<serde_json::Value>(&contents) {
                // Try different possible keys for version
                for key in ["name", "version", "minecraft_version", "id"] {
                    if let Some(version) = json.get(key).and_then(|v| v.as_str()) {
                        return Some(version.to_string());
                    }
                }
            }
        }
    }

    None
}

/// Get the plugins directory for the server
fn get_plugins_directory(server_path: &Path, server_type: &ServerType) -> String {
    match server_type {
        ServerType::Velocity | ServerType::BungeeCord | ServerType::Waterfall => {
            server_path.join("plugins").to_string_lossy().to_string()
        },
        _ => server_path.join("plugins").to_string_lossy().to_string(),
    }
}

/// Save plugin data to disk for persistence
async fn save_plugin_data(app_handle: AppHandle, plugins: Vec<Plugin>, server_path: String) -> Result<(), String> {
    // Get plugin data path
    let data_dir = get_plugin_data_path(&app_handle, &server_path)?;

    // Create directory if it doesn't exist
    if !data_dir.exists() {
        fs::create_dir_all(&data_dir)
            .map_err(|e| format!("Failed to create data directory: {}", e))?;
    }

    // Save plugins data
    let data_path = data_dir.join("plugins.json");
    let json_data = serde_json::to_string_pretty(&plugins)
        .map_err(|e| format!("Failed to serialize plugin data: {}", e))?;

    fs::write(&data_path, json_data)
        .map_err(|e| format!("Failed to write plugin data: {}", e))?;

    Ok(())
}

/// Get plugin data directory path
pub fn get_plugin_data_path(app_handle: &AppHandle, server_path: &str) -> Result<PathBuf, String> {
    let app_data_dir = app_handle.path().app_data_dir()
        .map_err(|e| format!("Failed to get app data directory: {}", e))?;

    // Hash the server path to create a unique identifier
    use sha2::Digest;
    let mut hasher = sha2::Sha256::new();
    hasher.update(server_path.as_bytes());
    let server_hash = format!("{:x}", hasher.finalize());

    // Create a directory for this server
    Ok(app_data_dir.join("servers").join(server_hash))
}