//! Package manager for Lux
//!
//! Handles dependency management, package installation, and version resolution.

use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use std::io::{self, Write};
use std::cmp::Ordering;

// =============================================================================
// Semantic Versioning
// =============================================================================

/// A semantic version (major.minor.patch)
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Version {
    pub major: u32,
    pub minor: u32,
    pub patch: u32,
    pub prerelease: Option<String>,
}

impl Version {
    pub fn new(major: u32, minor: u32, patch: u32) -> Self {
        Self { major, minor, patch, prerelease: None }
    }

    pub fn parse(s: &str) -> Result<Self, String> {
        let s = s.trim();

        // Handle prerelease suffix (e.g., "1.0.0-alpha")
        let (version_part, prerelease) = if let Some(pos) = s.find('-') {
            (&s[..pos], Some(s[pos + 1..].to_string()))
        } else {
            (s, None)
        };

        let parts: Vec<&str> = version_part.split('.').collect();
        if parts.len() < 2 || parts.len() > 3 {
            return Err(format!("Invalid version format: {}", s));
        }

        let major = parts[0].parse::<u32>()
            .map_err(|_| format!("Invalid major version: {}", parts[0]))?;
        let minor = parts[1].parse::<u32>()
            .map_err(|_| format!("Invalid minor version: {}", parts[1]))?;
        let patch = if parts.len() > 2 {
            parts[2].parse::<u32>()
                .map_err(|_| format!("Invalid patch version: {}", parts[2]))?
        } else {
            0
        };

        Ok(Self { major, minor, patch, prerelease })
    }
}

impl std::fmt::Display for Version {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if let Some(ref pre) = self.prerelease {
            write!(f, "{}.{}.{}-{}", self.major, self.minor, self.patch, pre)
        } else {
            write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
        }
    }
}

impl Ord for Version {
    fn cmp(&self, other: &Self) -> Ordering {
        match self.major.cmp(&other.major) {
            Ordering::Equal => {}
            ord => return ord,
        }
        match self.minor.cmp(&other.minor) {
            Ordering::Equal => {}
            ord => return ord,
        }
        match self.patch.cmp(&other.patch) {
            Ordering::Equal => {}
            ord => return ord,
        }
        // Prerelease versions are less than release versions
        match (&self.prerelease, &other.prerelease) {
            (None, None) => Ordering::Equal,
            (Some(_), None) => Ordering::Less,
            (None, Some(_)) => Ordering::Greater,
            (Some(a), Some(b)) => a.cmp(b),
        }
    }
}

impl PartialOrd for Version {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

/// Version constraint for dependencies
#[derive(Debug, Clone)]
pub enum VersionConstraint {
    /// Exact version: "1.2.3"
    Exact(Version),
    /// Caret: "^1.2.3" - compatible updates (>=1.2.3, <2.0.0)
    Caret(Version),
    /// Tilde: "~1.2.3" - patch updates only (>=1.2.3, <1.3.0)
    Tilde(Version),
    /// Greater than or equal: ">=1.2.3"
    GreaterEq(Version),
    /// Less than: "<2.0.0"
    Less(Version),
    /// Range: ">=1.0.0, <2.0.0"
    Range { min: Version, max: Version },
    /// Any version: "*"
    Any,
}

impl VersionConstraint {
    pub fn parse(s: &str) -> Result<Self, String> {
        let s = s.trim();

        if s == "*" {
            return Ok(VersionConstraint::Any);
        }

        // Check for range (comma-separated constraints)
        if s.contains(',') {
            let parts: Vec<&str> = s.split(',').collect();
            if parts.len() != 2 {
                return Err("Range must have exactly two constraints".to_string());
            }
            let first = VersionConstraint::parse(parts[0].trim())?;
            let second = VersionConstraint::parse(parts[1].trim())?;

            match (first, second) {
                (VersionConstraint::GreaterEq(min), VersionConstraint::Less(max)) => {
                    Ok(VersionConstraint::Range { min, max })
                }
                _ => Err("Range must be >=version, <version".to_string())
            }
        } else if let Some(rest) = s.strip_prefix('^') {
            Ok(VersionConstraint::Caret(Version::parse(rest)?))
        } else if let Some(rest) = s.strip_prefix('~') {
            Ok(VersionConstraint::Tilde(Version::parse(rest)?))
        } else if let Some(rest) = s.strip_prefix(">=") {
            Ok(VersionConstraint::GreaterEq(Version::parse(rest)?))
        } else if let Some(rest) = s.strip_prefix('<') {
            Ok(VersionConstraint::Less(Version::parse(rest)?))
        } else {
            // Try to parse as exact version
            Ok(VersionConstraint::Exact(Version::parse(s)?))
        }
    }

    /// Check if a version satisfies this constraint
    pub fn satisfies(&self, version: &Version) -> bool {
        match self {
            VersionConstraint::Exact(v) => version == v,
            VersionConstraint::Caret(v) => {
                // ^1.2.3 means >=1.2.3, <2.0.0 (if major > 0)
                // ^0.2.3 means >=0.2.3, <0.3.0 (if major == 0)
                if v.major == 0 {
                    version.major == 0 && version.minor == v.minor && version >= v
                } else {
                    version.major == v.major && version >= v
                }
            }
            VersionConstraint::Tilde(v) => {
                // ~1.2.3 means >=1.2.3, <1.3.0
                version.major == v.major && version.minor == v.minor && version >= v
            }
            VersionConstraint::GreaterEq(v) => version >= v,
            VersionConstraint::Less(v) => version < v,
            VersionConstraint::Range { min, max } => version >= min && version < max,
            VersionConstraint::Any => true,
        }
    }
}

impl std::fmt::Display for VersionConstraint {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            VersionConstraint::Exact(v) => write!(f, "{}", v),
            VersionConstraint::Caret(v) => write!(f, "^{}", v),
            VersionConstraint::Tilde(v) => write!(f, "~{}", v),
            VersionConstraint::GreaterEq(v) => write!(f, ">={}", v),
            VersionConstraint::Less(v) => write!(f, "<{}", v),
            VersionConstraint::Range { min, max } => write!(f, ">={}, <{}", min, max),
            VersionConstraint::Any => write!(f, "*"),
        }
    }
}

// =============================================================================
// Lock File
// =============================================================================

/// A lock file entry for a resolved package
#[derive(Debug, Clone)]
pub struct LockedPackage {
    pub name: String,
    pub version: Version,
    pub source: LockedSource,
    pub checksum: Option<String>,
    pub dependencies: Vec<String>,
}

/// Source of a locked package
#[derive(Debug, Clone)]
pub enum LockedSource {
    Registry,
    Git { url: String, rev: String },
    Path { path: PathBuf },
}

/// The lock file (lux.lock)
#[derive(Debug, Clone, Default)]
pub struct LockFile {
    pub packages: Vec<LockedPackage>,
}

impl LockFile {
    pub fn new() -> Self {
        Self { packages: Vec::new() }
    }

    /// Parse a lock file
    pub fn parse(content: &str) -> Result<Self, String> {
        let mut packages = Vec::new();
        let mut current_pkg: Option<LockedPackage> = None;
        let mut in_package = false;

        for line in content.lines() {
            let line = line.trim();

            if line.is_empty() || line.starts_with('#') {
                continue;
            }

            if line == "[[package]]" {
                if let Some(pkg) = current_pkg.take() {
                    packages.push(pkg);
                }
                current_pkg = Some(LockedPackage {
                    name: String::new(),
                    version: Version::new(0, 0, 0),
                    source: LockedSource::Registry,
                    checksum: None,
                    dependencies: Vec::new(),
                });
                in_package = true;
                continue;
            }

            if in_package {
                if let Some(ref mut pkg) = current_pkg {
                    if let Some(eq_pos) = line.find('=') {
                        let key = line[..eq_pos].trim();
                        let value = line[eq_pos + 1..].trim().trim_matches('"');

                        match key {
                            "name" => pkg.name = value.to_string(),
                            "version" => pkg.version = Version::parse(value)?,
                            "source" => {
                                if value == "registry" {
                                    pkg.source = LockedSource::Registry;
                                } else if value.starts_with("git:") {
                                    let parts: Vec<&str> = value[4..].splitn(2, '@').collect();
                                    pkg.source = LockedSource::Git {
                                        url: parts[0].to_string(),
                                        rev: parts.get(1).unwrap_or(&"HEAD").to_string(),
                                    };
                                } else if value.starts_with("path:") {
                                    pkg.source = LockedSource::Path {
                                        path: PathBuf::from(&value[5..]),
                                    };
                                }
                            }
                            "checksum" => pkg.checksum = Some(value.to_string()),
                            "dependencies" => {
                                // Parse array
                                let deps_str = value.trim_matches(|c| c == '[' || c == ']');
                                pkg.dependencies = deps_str
                                    .split(',')
                                    .map(|s| s.trim().trim_matches('"').to_string())
                                    .filter(|s| !s.is_empty())
                                    .collect();
                            }
                            _ => {}
                        }
                    }
                }
            }
        }

        if let Some(pkg) = current_pkg {
            packages.push(pkg);
        }

        Ok(Self { packages })
    }

    /// Format lock file as TOML
    pub fn format(&self) -> String {
        let mut output = String::new();
        output.push_str("# This file is auto-generated by lux pkg. Do not edit manually.\n\n");

        for pkg in &self.packages {
            output.push_str("[[package]]\n");
            output.push_str(&format!("name = \"{}\"\n", pkg.name));
            output.push_str(&format!("version = \"{}\"\n", pkg.version));

            let source_str = match &pkg.source {
                LockedSource::Registry => "registry".to_string(),
                LockedSource::Git { url, rev } => format!("git:{}@{}", url, rev),
                LockedSource::Path { path } => format!("path:{}", path.display()),
            };
            output.push_str(&format!("source = \"{}\"\n", source_str));

            if let Some(ref checksum) = pkg.checksum {
                output.push_str(&format!("checksum = \"{}\"\n", checksum));
            }

            if !pkg.dependencies.is_empty() {
                let deps: Vec<String> = pkg.dependencies.iter()
                    .map(|d| format!("\"{}\"", d))
                    .collect();
                output.push_str(&format!("dependencies = [{}]\n", deps.join(", ")));
            }

            output.push('\n');
        }

        output
    }

    /// Find a locked package by name
    pub fn find(&self, name: &str) -> Option<&LockedPackage> {
        self.packages.iter().find(|p| p.name == name)
    }
}

// =============================================================================
// Dependency Resolution
// =============================================================================

/// Resolution error
#[derive(Debug)]
pub struct ResolutionError {
    pub message: String,
    pub package: Option<String>,
}

impl std::fmt::Display for ResolutionError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if let Some(ref pkg) = self.package {
            write!(f, "Resolution error for '{}': {}", pkg, self.message)
        } else {
            write!(f, "Resolution error: {}", self.message)
        }
    }
}

/// Dependency resolver with transitive dependency support
pub struct Resolver {
    /// Available versions for each package (simulated for now)
    available_versions: HashMap<String, Vec<Version>>,
    /// Package dependencies cache (package@version -> dependencies)
    package_deps: HashMap<String, HashMap<String, Dependency>>,
    /// Packages directory for reading transitive deps
    packages_dir: Option<PathBuf>,
}

impl Resolver {
    pub fn new() -> Self {
        Self {
            available_versions: HashMap::new(),
            package_deps: HashMap::new(),
            packages_dir: None,
        }
    }

    /// Create resolver with packages directory for reading transitive deps
    pub fn with_packages_dir(packages_dir: &Path) -> Self {
        Self {
            available_versions: HashMap::new(),
            package_deps: HashMap::new(),
            packages_dir: Some(packages_dir.to_path_buf()),
        }
    }

    /// Add available versions for a package (for testing/registry integration)
    pub fn add_available_versions(&mut self, name: &str, versions: Vec<Version>) {
        self.available_versions.insert(name.to_string(), versions);
    }

    /// Add package dependencies (for testing or when loaded from registry)
    pub fn add_package_deps(&mut self, name: &str, version: &Version, deps: HashMap<String, Dependency>) {
        let key = format!("{}@{}", name, version);
        self.package_deps.insert(key, deps);
    }

    /// Resolve dependencies to a lock file (with transitive dependencies)
    pub fn resolve(
        &self,
        manifest: &Manifest,
        existing_lock: Option<&LockFile>,
    ) -> Result<LockFile, ResolutionError> {
        let mut lock = LockFile::new();
        let mut resolved: HashMap<String, (Version, LockedSource)> = HashMap::new();
        let mut to_resolve: Vec<(String, Dependency, Option<String>)> = Vec::new();

        // If we have an existing lock file, prefer those versions
        if let Some(existing) = existing_lock {
            for pkg in &existing.packages {
                resolved.insert(pkg.name.clone(), (pkg.version.clone(), pkg.source.clone()));
            }
        }

        // Queue direct dependencies for resolution
        for (name, dep) in &manifest.dependencies {
            to_resolve.push((name.clone(), dep.clone(), None));
        }

        // Process queue (breadth-first for better dependency order)
        while let Some((name, dep, required_by)) = to_resolve.pop() {
            // Skip if already resolved with compatible version
            if let Some((existing_version, _)) = resolved.get(&name) {
                let constraint = VersionConstraint::parse(&dep.version)
                    .map_err(|e| ResolutionError {
                        message: e,
                        package: Some(name.clone()),
                    })?;

                if constraint.satisfies(existing_version) {
                    continue; // Already have a compatible version
                } else {
                    // Version conflict
                    return Err(ResolutionError {
                        message: format!(
                            "Version conflict: {} requires {} {}, but {} is already resolved{}",
                            required_by.as_deref().unwrap_or("project"),
                            name,
                            dep.version,
                            existing_version,
                            if let Some(rb) = &required_by {
                                format!(" (required by {})", rb)
                            } else {
                                String::new()
                            }
                        ),
                        package: Some(name.clone()),
                    });
                }
            }

            let constraint = VersionConstraint::parse(&dep.version)
                .map_err(|e| ResolutionError {
                    message: e,
                    package: Some(name.clone()),
                })?;

            // Resolve the version
            let version = self.select_version(&name, &constraint, &dep.source)?;

            let source = match &dep.source {
                DependencySource::Registry => LockedSource::Registry,
                DependencySource::Git { url, branch } => LockedSource::Git {
                    url: url.clone(),
                    rev: branch.clone().unwrap_or_else(|| "HEAD".to_string()),
                },
                DependencySource::Path { path } => LockedSource::Path { path: path.clone() },
            };

            resolved.insert(name.clone(), (version.clone(), source.clone()));

            // Get transitive dependencies
            let transitive_deps = self.get_package_dependencies(&name, &version, &dep.source);
            for (trans_name, trans_dep) in transitive_deps {
                if !resolved.contains_key(&trans_name) {
                    to_resolve.push((trans_name, trans_dep, Some(name.clone())));
                }
            }
        }

        // Build lock file from resolved packages
        for (name, (version, source)) in &resolved {
            // Get the dependency list for this package
            let deps = self.get_package_dependencies(name, version, &match source {
                LockedSource::Registry => DependencySource::Registry,
                LockedSource::Git { url, rev } => DependencySource::Git {
                    url: url.clone(),
                    branch: Some(rev.clone()),
                },
                LockedSource::Path { path } => DependencySource::Path { path: path.clone() },
            });
            let dep_names: Vec<String> = deps.keys().cloned().collect();

            lock.packages.push(LockedPackage {
                name: name.clone(),
                version: version.clone(),
                source: source.clone(),
                checksum: None,
                dependencies: dep_names,
            });
        }

        // Sort packages by name for deterministic output
        lock.packages.sort_by(|a, b| a.name.cmp(&b.name));

        Ok(lock)
    }

    /// Get dependencies of a package
    fn get_package_dependencies(
        &self,
        name: &str,
        version: &Version,
        source: &DependencySource,
    ) -> HashMap<String, Dependency> {
        // First check our cache
        let key = format!("{}@{}", name, version);
        if let Some(deps) = self.package_deps.get(&key) {
            return deps.clone();
        }

        // Try to read from installed package
        if let Some(ref packages_dir) = self.packages_dir {
            let pkg_dir = packages_dir.join(name);
            let manifest_path = pkg_dir.join("lux.toml");

            if manifest_path.exists() {
                if let Ok(content) = fs::read_to_string(&manifest_path) {
                    if let Ok(manifest) = parse_manifest(&content) {
                        return manifest.dependencies;
                    }
                }
            }
        }

        // For path dependencies, read from the path
        if let DependencySource::Path { path } = source {
            let manifest_path = if path.is_absolute() {
                path.join("lux.toml")
            } else if let Some(ref packages_dir) = self.packages_dir {
                packages_dir.parent().unwrap_or(packages_dir).join(path).join("lux.toml")
            } else {
                path.join("lux.toml")
            };

            if manifest_path.exists() {
                if let Ok(content) = fs::read_to_string(&manifest_path) {
                    if let Ok(manifest) = parse_manifest(&content) {
                        return manifest.dependencies;
                    }
                }
            }
        }

        // No dependencies found
        HashMap::new()
    }

    /// Select the best version that satisfies the constraint
    fn select_version(
        &self,
        name: &str,
        constraint: &VersionConstraint,
        source: &DependencySource,
    ) -> Result<Version, ResolutionError> {
        match source {
            DependencySource::Git { .. } | DependencySource::Path { .. } => {
                // For git/path sources, use the version from the constraint or 0.0.0
                match constraint {
                    VersionConstraint::Exact(v) => Ok(v.clone()),
                    _ => Ok(Version::new(0, 0, 0)),
                }
            }
            DependencySource::Registry => {
                // Check available versions
                if let Some(versions) = self.available_versions.get(name) {
                    // Find the highest version that satisfies the constraint
                    let mut matching: Vec<&Version> = versions
                        .iter()
                        .filter(|v| constraint.satisfies(v))
                        .collect();
                    matching.sort();
                    matching.reverse();

                    if let Some(v) = matching.first() {
                        return Ok((*v).clone());
                    }
                }

                // No available versions - use the constraint's base version
                match constraint {
                    VersionConstraint::Exact(v) => Ok(v.clone()),
                    VersionConstraint::Caret(v) => Ok(v.clone()),
                    VersionConstraint::Tilde(v) => Ok(v.clone()),
                    VersionConstraint::GreaterEq(v) => Ok(v.clone()),
                    VersionConstraint::Range { min, .. } => Ok(min.clone()),
                    VersionConstraint::Less(_) | VersionConstraint::Any => {
                        // Can't determine version without registry
                        Ok(Version::new(0, 0, 0))
                    }
                }
            }
        }
    }
}

impl Default for Resolver {
    fn default() -> Self {
        Self::new()
    }
}

// =============================================================================
// Manifest and Package Manager
// =============================================================================

/// Package manifest (lux.toml)
#[derive(Debug, Clone)]
pub struct Manifest {
    pub project: ProjectInfo,
    pub dependencies: HashMap<String, Dependency>,
}

/// Project information from lux.toml
#[derive(Debug, Clone)]
pub struct ProjectInfo {
    pub name: String,
    pub version: String,
    pub description: Option<String>,
    pub authors: Vec<String>,
    pub license: Option<String>,
}

/// A package dependency
#[derive(Debug, Clone)]
pub struct Dependency {
    pub name: String,
    pub version: String,
    pub source: DependencySource,
}

/// Source of a dependency
#[derive(Debug, Clone)]
pub enum DependencySource {
    /// From the Lux package registry
    Registry,
    /// From a git repository
    Git { url: String, branch: Option<String> },
    /// From a local path
    Path { path: PathBuf },
}

/// Package manager
pub struct PackageManager {
    /// Root directory of the project
    project_root: PathBuf,
    /// Directory where packages are cached
    cache_dir: PathBuf,
    /// Directory where packages are installed (project-local)
    packages_dir: PathBuf,
}

impl PackageManager {
    pub fn new(project_root: &Path) -> Self {
        let cache_dir = dirs::cache_dir()
            .unwrap_or_else(|| PathBuf::from("."))
            .join("lux")
            .join("packages");

        let packages_dir = project_root.join(".lux_packages");

        Self {
            project_root: project_root.to_path_buf(),
            cache_dir,
            packages_dir,
        }
    }

    /// Load the lock file (lux.lock)
    pub fn load_lock(&self) -> Result<Option<LockFile>, String> {
        let lock_path = self.project_root.join("lux.lock");

        if !lock_path.exists() {
            return Ok(None);
        }

        let content = fs::read_to_string(&lock_path)
            .map_err(|e| format!("Failed to read lux.lock: {}", e))?;

        LockFile::parse(&content).map(Some)
    }

    /// Save the lock file (lux.lock)
    pub fn save_lock(&self, lock: &LockFile) -> Result<(), String> {
        let lock_path = self.project_root.join("lux.lock");
        let content = lock.format();

        fs::write(&lock_path, content)
            .map_err(|e| format!("Failed to write lux.lock: {}", e))
    }

    /// Resolve dependencies and generate/update lock file
    pub fn resolve(&self) -> Result<LockFile, String> {
        let manifest = self.load_manifest()?;
        let existing_lock = self.load_lock()?;

        // Use resolver with packages directory for transitive dep lookup
        let resolver = Resolver::with_packages_dir(&self.packages_dir);
        let lock = resolver.resolve(&manifest, existing_lock.as_ref())
            .map_err(|e| e.to_string())?;

        self.save_lock(&lock)?;
        Ok(lock)
    }

    /// Find the project root by looking for lux.toml
    pub fn find_project_root() -> Option<PathBuf> {
        let mut current = std::env::current_dir().ok()?;

        loop {
            if current.join("lux.toml").exists() {
                return Some(current);
            }

            if !current.pop() {
                return None;
            }
        }
    }

    /// Load the manifest from lux.toml
    pub fn load_manifest(&self) -> Result<Manifest, String> {
        let manifest_path = self.project_root.join("lux.toml");

        if !manifest_path.exists() {
            return Err("No lux.toml found in project root".to_string());
        }

        let content = fs::read_to_string(&manifest_path)
            .map_err(|e| format!("Failed to read lux.toml: {}", e))?;

        parse_manifest(&content)
    }

    /// Save the manifest to lux.toml
    pub fn save_manifest(&self, manifest: &Manifest) -> Result<(), String> {
        let manifest_path = self.project_root.join("lux.toml");
        let content = format_manifest(manifest);

        fs::write(&manifest_path, content)
            .map_err(|e| format!("Failed to write lux.toml: {}", e))
    }

    /// Add a dependency to the project
    pub fn add(&self, name: &str, version: &str, source: DependencySource) -> Result<(), String> {
        let mut manifest = self.load_manifest()?;

        let dep = Dependency {
            name: name.to_string(),
            version: version.to_string(),
            source,
        };

        manifest.dependencies.insert(name.to_string(), dep);
        self.save_manifest(&manifest)?;

        println!("Added {} v{} to dependencies", name, version);
        Ok(())
    }

    /// Remove a dependency from the project
    pub fn remove(&self, name: &str) -> Result<(), String> {
        let mut manifest = self.load_manifest()?;

        if manifest.dependencies.remove(name).is_some() {
            self.save_manifest(&manifest)?;

            // Remove from packages dir if it exists
            let pkg_dir = self.packages_dir.join(name);
            if pkg_dir.exists() {
                fs::remove_dir_all(&pkg_dir)
                    .map_err(|e| format!("Failed to remove package directory: {}", e))?;
            }

            println!("Removed {} from dependencies", name);
            Ok(())
        } else {
            Err(format!("Dependency '{}' not found", name))
        }
    }

    /// Install all dependencies
    pub fn install(&self) -> Result<(), String> {
        let manifest = self.load_manifest()?;

        if manifest.dependencies.is_empty() {
            println!("No dependencies to install.");
            return Ok(());
        }

        // Resolve dependencies and generate/update lock file
        let lock = self.resolve()?;

        // Create packages directory
        fs::create_dir_all(&self.packages_dir)
            .map_err(|e| format!("Failed to create packages directory: {}", e))?;

        println!("Installing {} dependencies...", lock.packages.len());
        println!();

        // Install from lock file for reproducibility
        for locked_pkg in &lock.packages {
            self.install_locked_package(locked_pkg, &manifest)?;
        }

        println!();
        println!("Done! Installed {} packages.", lock.packages.len());
        println!("Lock file written to lux.lock");
        Ok(())
    }

    /// Install a package from the lock file
    fn install_locked_package(&self, locked: &LockedPackage, manifest: &Manifest) -> Result<(), String> {
        print!("  Installing {} v{}... ", locked.name, locked.version);
        io::stdout().flush().unwrap();

        let dest_dir = self.packages_dir.join(&locked.name);

        // Get the dependency info from manifest for source details
        let dep = manifest.dependencies.get(&locked.name);

        match &locked.source {
            LockedSource::Registry => {
                self.install_from_registry_locked(locked, &dest_dir)?;
            }
            LockedSource::Git { url, rev } => {
                self.install_from_git_locked(url, rev, &dest_dir)?;
            }
            LockedSource::Path { path } => {
                let source_path = if let Some(d) = dep {
                    match &d.source {
                        DependencySource::Path { path } => path.clone(),
                        _ => path.clone(),
                    }
                } else {
                    path.clone()
                };
                self.install_from_path(&source_path, &dest_dir)?;
            }
        }

        println!("done");
        Ok(())
    }

    fn install_from_registry_locked(&self, locked: &LockedPackage, dest: &Path) -> Result<(), String> {
        // Check if already installed with correct version
        let version_file = dest.join(".version");
        if version_file.exists() {
            let installed_version = fs::read_to_string(&version_file).unwrap_or_default();
            if installed_version.trim() == locked.version.to_string() {
                return Ok(());
            }
        }

        // Check cache first
        let cache_path = self.cache_dir.join(&locked.name).join(locked.version.to_string());

        if cache_path.exists() {
            // Copy from cache
            copy_dir_recursive(&cache_path, dest)?;
        } else {
            // Create placeholder package (in real impl, would download)
            fs::create_dir_all(dest)
                .map_err(|e| format!("Failed to create package directory: {}", e))?;

            // Create a lib.lux placeholder
            let lib_content = format!(
                "// Package: {} v{}\n// This is a placeholder - real package would be downloaded from registry\n\n",
                locked.name, locked.version
            );
            fs::write(dest.join("lib.lux"), lib_content)
                .map_err(|e| format!("Failed to create lib.lux: {}", e))?;
        }

        // Write version file
        fs::write(&version_file, locked.version.to_string())
            .map_err(|e| format!("Failed to write version file: {}", e))?;

        // Verify checksum if present
        if let Some(ref expected) = locked.checksum {
            // In a real implementation, verify the checksum here
            let _ = expected; // Placeholder
        }

        Ok(())
    }

    fn install_from_git_locked(&self, url: &str, rev: &str, dest: &Path) -> Result<(), String> {
        // Remove existing if present
        if dest.exists() {
            fs::remove_dir_all(dest)
                .map_err(|e| format!("Failed to remove existing directory: {}", e))?;
        }

        // Clone at specific revision
        let mut cmd = std::process::Command::new("git");
        cmd.arg("clone")
            .arg("--depth").arg("1");

        // If rev is not HEAD, we need to fetch the specific revision
        if rev != "HEAD" && !rev.is_empty() {
            cmd.arg("--branch").arg(rev);
        }

        cmd.arg(url).arg(dest);

        let output = cmd.output()
            .map_err(|e| format!("Failed to run git: {}", e))?;

        if !output.status.success() {
            return Err(format!(
                "Git clone failed: {}",
                String::from_utf8_lossy(&output.stderr)
            ));
        }

        // Remove .git directory to save space
        let git_dir = dest.join(".git");
        if git_dir.exists() {
            fs::remove_dir_all(&git_dir).ok();
        }

        Ok(())
    }

    /// Install a single dependency
    fn install_dependency(&self, dep: &Dependency) -> Result<(), String> {
        print!("  Installing {} v{}... ", dep.name, dep.version);
        io::stdout().flush().unwrap();

        let dest_dir = self.packages_dir.join(&dep.name);

        match &dep.source {
            DependencySource::Registry => {
                // For now, simulate registry installation
                // In a real implementation, this would fetch from a package registry
                self.install_from_registry(dep, &dest_dir)?;
            }
            DependencySource::Git { url, branch } => {
                self.install_from_git(url, branch.as_deref(), &dest_dir)?;
            }
            DependencySource::Path { path } => {
                self.install_from_path(path, &dest_dir)?;
            }
        }

        println!("done");
        Ok(())
    }

    fn install_from_registry(&self, dep: &Dependency, dest: &Path) -> Result<(), String> {
        // Check if already installed with correct version
        let version_file = dest.join(".version");
        if version_file.exists() {
            let installed_version = fs::read_to_string(&version_file).unwrap_or_default();
            if installed_version.trim() == dep.version {
                return Ok(());
            }
        }

        // Check cache first
        let cache_path = self.cache_dir.join(&dep.name).join(&dep.version);

        if cache_path.exists() {
            // Copy from cache
            copy_dir_recursive(&cache_path, dest)?;
        } else {
            // Create placeholder package (in real impl, would download)
            fs::create_dir_all(dest)
                .map_err(|e| format!("Failed to create package directory: {}", e))?;

            // Create a lib.lux placeholder
            let lib_content = format!(
                "// Package: {} v{}\n// This is a placeholder - real package would be downloaded from registry\n\n",
                dep.name, dep.version
            );
            fs::write(dest.join("lib.lux"), lib_content)
                .map_err(|e| format!("Failed to create lib.lux: {}", e))?;
        }

        // Write version file
        fs::write(&version_file, &dep.version)
            .map_err(|e| format!("Failed to write version file: {}", e))?;

        Ok(())
    }

    fn install_from_git(&self, url: &str, branch: Option<&str>, dest: &Path) -> Result<(), String> {
        // Remove existing if present
        if dest.exists() {
            fs::remove_dir_all(dest)
                .map_err(|e| format!("Failed to remove existing directory: {}", e))?;
        }

        // Clone the repository
        let mut cmd = std::process::Command::new("git");
        cmd.arg("clone")
            .arg("--depth").arg("1");

        if let Some(b) = branch {
            cmd.arg("--branch").arg(b);
        }

        cmd.arg(url).arg(dest);

        let output = cmd.output()
            .map_err(|e| format!("Failed to run git: {}", e))?;

        if !output.status.success() {
            return Err(format!(
                "Git clone failed: {}",
                String::from_utf8_lossy(&output.stderr)
            ));
        }

        // Remove .git directory to save space
        let git_dir = dest.join(".git");
        if git_dir.exists() {
            fs::remove_dir_all(&git_dir).ok();
        }

        Ok(())
    }

    fn install_from_path(&self, source: &Path, dest: &Path) -> Result<(), String> {
        let source = if source.is_absolute() {
            source.to_path_buf()
        } else {
            self.project_root.join(source)
        };

        if !source.exists() {
            return Err(format!("Source path does not exist: {}", source.display()));
        }

        // Remove existing if present
        if dest.exists() {
            fs::remove_dir_all(dest)
                .map_err(|e| format!("Failed to remove existing directory: {}", e))?;
        }

        // Copy the directory
        copy_dir_recursive(&source, dest)?;

        Ok(())
    }

    /// List installed packages
    pub fn list(&self) -> Result<(), String> {
        let manifest = self.load_manifest()?;

        if manifest.dependencies.is_empty() {
            println!("No dependencies in lux.toml");
            return Ok(());
        }

        println!("Dependencies:");
        for (name, dep) in &manifest.dependencies {
            let source_info = match &dep.source {
                DependencySource::Registry => "registry".to_string(),
                DependencySource::Git { url, branch } => {
                    if let Some(b) = branch {
                        format!("git: {} ({})", url, b)
                    } else {
                        format!("git: {}", url)
                    }
                }
                DependencySource::Path { path } => format!("path: {}", path.display()),
            };

            let installed = self.packages_dir.join(name).exists();
            let status = if installed { "✓" } else { "✗" };

            println!("  {} {} v{} [{}]", status, name, dep.version, source_info);
        }

        Ok(())
    }

    /// Update all dependencies
    pub fn update(&self) -> Result<(), String> {
        let manifest = self.load_manifest()?;

        if manifest.dependencies.is_empty() {
            println!("No dependencies to update.");
            return Ok(());
        }

        println!("Updating {} dependencies...", manifest.dependencies.len());
        println!();

        for (name, dep) in &manifest.dependencies {
            // Force reinstall
            let dest_dir = self.packages_dir.join(name);
            if dest_dir.exists() {
                fs::remove_dir_all(&dest_dir).ok();
            }
            self.install_dependency(dep)?;
        }

        println!();
        println!("Done!");
        Ok(())
    }

    /// Get the search paths for installed packages
    #[allow(dead_code)]
    pub fn get_package_paths(&self) -> Vec<PathBuf> {
        let mut paths = vec![];

        if self.packages_dir.exists() {
            paths.push(self.packages_dir.clone());
        }

        // Also include global cache
        if self.cache_dir.exists() {
            paths.push(self.cache_dir.clone());
        }

        paths
    }

    /// Clean the packages directory
    pub fn clean(&self) -> Result<(), String> {
        if self.packages_dir.exists() {
            fs::remove_dir_all(&self.packages_dir)
                .map_err(|e| format!("Failed to remove packages directory: {}", e))?;
            println!("Cleaned .lux_packages directory");
        } else {
            println!("Nothing to clean");
        }
        Ok(())
    }
}

/// Parse a lux.toml manifest
fn parse_manifest(content: &str) -> Result<Manifest, String> {
    let mut project = ProjectInfo {
        name: String::new(),
        version: String::new(),
        description: None,
        authors: vec![],
        license: None,
    };
    let mut dependencies = HashMap::new();

    let mut current_section = "";

    for line in content.lines() {
        let line = line.trim();

        // Skip comments and empty lines
        if line.is_empty() || line.starts_with('#') {
            continue;
        }

        // Section header
        if line.starts_with('[') && line.ends_with(']') {
            current_section = &line[1..line.len()-1];
            continue;
        }

        // Key = value
        if let Some(eq_pos) = line.find('=') {
            let key = line[..eq_pos].trim();
            let value = line[eq_pos+1..].trim();
            let value = value.trim_matches('"');

            match current_section {
                "project" => {
                    match key {
                        "name" => project.name = value.to_string(),
                        "version" => project.version = value.to_string(),
                        "description" => project.description = Some(value.to_string()),
                        "license" => project.license = Some(value.to_string()),
                        "authors" => {
                            // Simple array parsing
                            let authors_str = value.trim_matches(|c| c == '[' || c == ']');
                            project.authors = authors_str
                                .split(',')
                                .map(|s| s.trim().trim_matches('"').to_string())
                                .filter(|s| !s.is_empty())
                                .collect();
                        }
                        _ => {}
                    }
                }
                "dependencies" => {
                    // Parse dependency
                    let dep = parse_dependency(key, value)?;
                    dependencies.insert(key.to_string(), dep);
                }
                _ => {}
            }
        }
    }

    if project.name.is_empty() {
        return Err("Missing project name in lux.toml".to_string());
    }
    if project.version.is_empty() {
        return Err("Missing project version in lux.toml".to_string());
    }

    Ok(Manifest { project, dependencies })
}

fn parse_dependency(name: &str, value: &str) -> Result<Dependency, String> {
    let value = value.trim();

    // Simple version string: "1.0.0"
    if value.starts_with('"') || value.chars().next().map(|c| c.is_ascii_digit()).unwrap_or(false) {
        let version = value.trim_matches('"');
        return Ok(Dependency {
            name: name.to_string(),
            version: version.to_string(),
            source: DependencySource::Registry,
        });
    }

    // Inline table: { version = "1.0", git = "..." }
    if value.starts_with('{') && value.ends_with('}') {
        let inner = &value[1..value.len()-1];
        let mut version = "0.0.0".to_string();
        let mut git_url = None;
        let mut git_branch = None;
        let mut path = None;

        for part in inner.split(',') {
            let part = part.trim();
            if let Some(eq_pos) = part.find('=') {
                let k = part[..eq_pos].trim();
                let v = part[eq_pos+1..].trim().trim_matches('"');

                match k {
                    "version" => version = v.to_string(),
                    "git" => git_url = Some(v.to_string()),
                    "branch" => git_branch = Some(v.to_string()),
                    "path" => path = Some(PathBuf::from(v)),
                    _ => {}
                }
            }
        }

        let source = if let Some(url) = git_url {
            DependencySource::Git { url, branch: git_branch }
        } else if let Some(p) = path {
            DependencySource::Path { path: p }
        } else {
            DependencySource::Registry
        };

        return Ok(Dependency {
            name: name.to_string(),
            version,
            source,
        });
    }

    Err(format!("Invalid dependency format for {}: {}", name, value))
}

/// Format a manifest as TOML
fn format_manifest(manifest: &Manifest) -> String {
    let mut output = String::new();

    output.push_str("[project]\n");
    output.push_str(&format!("name = \"{}\"\n", manifest.project.name));
    output.push_str(&format!("version = \"{}\"\n", manifest.project.version));

    if let Some(desc) = &manifest.project.description {
        output.push_str(&format!("description = \"{}\"\n", desc));
    }

    if !manifest.project.authors.is_empty() {
        let authors: Vec<String> = manifest.project.authors.iter()
            .map(|a| format!("\"{}\"", a))
            .collect();
        output.push_str(&format!("authors = [{}]\n", authors.join(", ")));
    }

    if let Some(license) = &manifest.project.license {
        output.push_str(&format!("license = \"{}\"\n", license));
    }

    output.push_str("\n[dependencies]\n");

    for (name, dep) in &manifest.dependencies {
        match &dep.source {
            DependencySource::Registry => {
                output.push_str(&format!("{} = \"{}\"\n", name, dep.version));
            }
            DependencySource::Git { url, branch } => {
                if let Some(b) = branch {
                    output.push_str(&format!(
                        "{} = {{ version = \"{}\", git = \"{}\", branch = \"{}\" }}\n",
                        name, dep.version, url, b
                    ));
                } else {
                    output.push_str(&format!(
                        "{} = {{ version = \"{}\", git = \"{}\" }}\n",
                        name, dep.version, url
                    ));
                }
            }
            DependencySource::Path { path } => {
                output.push_str(&format!(
                    "{} = {{ version = \"{}\", path = \"{}\" }}\n",
                    name, dep.version, path.display()
                ));
            }
        }
    }

    output
}

/// Recursively copy a directory
fn copy_dir_recursive(src: &Path, dst: &Path) -> Result<(), String> {
    fs::create_dir_all(dst)
        .map_err(|e| format!("Failed to create directory: {}", e))?;

    for entry in fs::read_dir(src).map_err(|e| format!("Failed to read directory: {}", e))? {
        let entry = entry.map_err(|e| format!("Failed to read entry: {}", e))?;
        let path = entry.path();
        let dest_path = dst.join(entry.file_name());

        if path.is_dir() {
            copy_dir_recursive(&path, &dest_path)?;
        } else {
            fs::copy(&path, &dest_path)
                .map_err(|e| format!("Failed to copy file: {}", e))?;
        }
    }

    Ok(())
}

// Platform-specific cache directory handling
mod dirs {
    use std::path::PathBuf;

    pub fn cache_dir() -> Option<PathBuf> {
        #[cfg(target_os = "linux")]
        {
            std::env::var("XDG_CACHE_HOME")
                .map(PathBuf::from)
                .ok()
                .or_else(|| {
                    std::env::var("HOME")
                        .map(|h| PathBuf::from(h).join(".cache"))
                        .ok()
                })
        }

        #[cfg(target_os = "macos")]
        {
            std::env::var("HOME")
                .map(|h| PathBuf::from(h).join("Library").join("Caches"))
                .ok()
        }

        #[cfg(target_os = "windows")]
        {
            std::env::var("LOCALAPPDATA")
                .map(PathBuf::from)
                .ok()
        }

        #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
        {
            None
        }
    }
}
