/// Manifest management utilities for .project/manifest.toml
///
/// Provides functions to read, write, and manipulate manifest.toml files
/// that track installed resources in external projects.
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};

/// Resource type classification
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ResourceType {
    Symlink,
    Local,
    Copy,
    Generated,
}

impl ResourceType {
    /// Parse from string
    pub fn parse(s: &str) -> Self {
        match s.to_lowercase().as_str() {
            "symlink" => ResourceType::Symlink,
            "local" => ResourceType::Local,
            "copy" => ResourceType::Copy,
            "generated" => ResourceType::Generated,
            _ => ResourceType::Copy,
        }
    }

    /// Convert to string
    pub fn as_str(&self) -> &'static str {
        match self {
            ResourceType::Symlink => "symlink",
            ResourceType::Local => "local",
            ResourceType::Copy => "copy",
            ResourceType::Generated => "generated",
        }
    }
}

/// Configuration entry in manifest
#[derive(Debug, Clone)]
pub struct ConfigEntry {
    pub name: String,
    pub enabled: bool,
    pub resource_type: ResourceType,
    pub path: String,
    pub modified: bool,
}

/// Manifest structure
#[derive(Debug)]
pub struct Manifest {
    pub tools_path: PathBuf,
    pub resource_type: ResourceType,
    pub configs: HashMap<String, ConfigEntry>,
}

impl Manifest {
    /// Load manifest from path
    pub fn load(path: &Path) -> Result<Self, String> {
        if !path.exists() {
            return Err(format!("Manifest not found: {}", path.display()));
        }

        let content =
            fs::read_to_string(path).map_err(|e| format!("Failed to read manifest: {}", e))?;

        Self::from_toml(&content)
    }

    /// Parse manifest from TOML string
    pub fn from_toml(content: &str) -> Result<Self, String> {
        // Simple TOML parsing for manifest
        let mut tools_path = PathBuf::new();
        let mut resource_type = ResourceType::Symlink;
        let mut configs = HashMap::new();

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

            // Parse tools_path
            if line.starts_with("tools_path") && line.contains('=') {
                if let Some(value) = extract_toml_value(line) {
                    tools_path = PathBuf::from(value);
                }
            }

            // Parse resource_type
            if line.starts_with("resource_type") && line.contains('=') {
                if let Some(value) = extract_toml_value(line) {
                    resource_type = ResourceType::parse(&value);
                }
            }

            // Parse config entries [configs] section
            if line.starts_with("doc-lifecycle")
                || line.starts_with("lifecycle")
                || line.starts_with("tracking")
                || line.starts_with("presentation")
            {
                if let Some((name, entry)) = parse_config_entry(line) {
                    configs.insert(name, entry);
                }
            }
        }

        Ok(Manifest {
            tools_path,
            resource_type,
            configs,
        })
    }

    /// Save manifest to file
    pub fn save(&self, path: &Path) -> Result<(), String> {
        let toml = self.to_toml();
        fs::write(path, toml).map_err(|e| format!("Failed to write manifest: {}", e))
    }

    /// Convert to TOML string
    pub fn to_toml(&self) -> String {
        let mut output = String::new();
        output.push_str("# Project Tools Manifest\n");
        output.push_str("[project-tools]\n");
        output.push_str(&format!("tools_path = \"{}\"\n", self.tools_path.display()));
        output.push_str(&format!(
            "resource_type = \"{}\"\n",
            self.resource_type.as_str()
        ));
        output.push_str("\n[configs]\n");

        for (name, entry) in &self.configs {
            output.push_str(&format!("{} = {{ ", name));
            output.push_str(&format!("enabled = {}, ", entry.enabled));
            output.push_str(&format!("type = \"{}\", ", entry.resource_type.as_str()));
            output.push_str(&format!("path = \"{}\", ", entry.path));
            output.push_str(&format!("modified = {}", entry.modified));
            output.push_str(" }\n");
        }

        output
    }

    /// Enable a configuration
    pub fn enable(&mut self, name: &str) {
        if let Some(entry) = self.configs.get_mut(name) {
            entry.enabled = true;
        }
    }

    /// Disable a configuration
    pub fn disable(&mut self, name: &str) {
        if let Some(entry) = self.configs.get_mut(name) {
            entry.enabled = false;
        }
    }

    /// Mark a configuration as modified
    pub fn mark_modified(&mut self, name: &str) {
        if let Some(entry) = self.configs.get_mut(name) {
            entry.modified = true;
            entry.resource_type = ResourceType::Local;
        }
    }

    /// Get enabled configurations
    pub fn enabled_configs(&self) -> Vec<&ConfigEntry> {
        self.configs.values().filter(|e| e.enabled).collect()
    }

    /// Check if config is enabled
    pub fn is_enabled(&self, name: &str) -> bool {
        self.configs.get(name).map(|e| e.enabled).unwrap_or(false)
    }
}

/// Helper: Extract value from TOML line (key = "value")
fn extract_toml_value(line: &str) -> Option<String> {
    if let Some(pos) = line.find('=') {
        let value_part = line[pos + 1..].trim();
        if value_part.starts_with('"') && value_part.ends_with('"') {
            return Some(value_part[1..value_part.len() - 1].to_string());
        }
    }
    None
}

/// Helper: Parse a config entry line
fn parse_config_entry(line: &str) -> Option<(String, ConfigEntry)> {
    if !line.contains('=') {
        return None;
    }

    let parts: Vec<&str> = line.split('=').collect();
    if parts.is_empty() {
        return None;
    }

    let name = parts[0].trim().to_string();
    let config_str = parts[1..].join("=");

    // Simple parsing of { enabled = true, type = "symlink", path = "...", modified = false }
    let enabled = config_str.contains("enabled = true");
    let resource_type = if config_str.contains("type = \"symlink\"") {
        ResourceType::Symlink
    } else if config_str.contains("type = \"local\"") {
        ResourceType::Local
    } else if config_str.contains("type = \"generated\"") {
        ResourceType::Generated
    } else {
        ResourceType::Copy
    };

    let path = if let Some(start) = config_str.find("path = \"") {
        let start = start + 8;
        if let Some(end) = config_str[start..].find('"') {
            config_str[start..start + end].to_string()
        } else {
            format!(".project/{}", name)
        }
    } else {
        format!(".project/{}", name)
    };

    let modified = config_str.contains("modified = true");

    Some((
        name.clone(),
        ConfigEntry {
            name,
            enabled,
            resource_type,
            path,
            modified,
        },
    ))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_resource_type_conversion() {
        assert_eq!(ResourceType::parse("symlink"), ResourceType::Symlink);
        assert_eq!(ResourceType::parse("SYMLINK"), ResourceType::Symlink);
        assert_eq!(ResourceType::parse("local"), ResourceType::Local);
        assert_eq!(ResourceType::Symlink.as_str(), "symlink");
    }

    #[test]
    fn test_manifest_from_toml() {
        let toml = r#"
[project-tools]
tools_path = "/Users/example/Tools"
resource_type = "symlink"

[configs]
tracking = { enabled = true, type = "symlink", path = ".project/tracking.toml", modified = false }
lifecycle = { enabled = false, type = "copy", path = ".project/lifecycle.toml", modified = true }
"#;

        let manifest = Manifest::from_toml(toml).expect("Failed to parse");
        assert_eq!(
            manifest.tools_path.to_str().unwrap(),
            "/Users/example/Tools"
        );
        assert_eq!(manifest.resource_type, ResourceType::Symlink);
        assert_eq!(manifest.configs.len(), 2);
        assert!(manifest.is_enabled("tracking"));
        assert!(!manifest.is_enabled("lifecycle"));
    }

    #[test]
    fn test_manifest_enable_disable() {
        let toml = r#"
[project-tools]
tools_path = "/Users/example/Tools"
resource_type = "symlink"

[configs]
tracking = { enabled = false, type = "symlink", path = ".project/tracking.toml", modified = false }
"#;

        let mut manifest = Manifest::from_toml(toml).expect("Failed to parse");
        assert!(!manifest.is_enabled("tracking"));

        manifest.enable("tracking");
        assert!(manifest.is_enabled("tracking"));

        manifest.disable("tracking");
        assert!(!manifest.is_enabled("tracking"));
    }

    #[test]
    fn test_manifest_mark_modified() {
        let toml = r#"
[project-tools]
tools_path = "/Users/example/Tools"
resource_type = "symlink"

[configs]
tracking = { enabled = true, type = "symlink", path = ".project/tracking.toml", modified = false }
"#;

        let mut manifest = Manifest::from_toml(toml).expect("Failed to parse");
        let entry = manifest.configs.get("tracking").unwrap();
        assert_eq!(entry.resource_type, ResourceType::Symlink);
        assert!(!entry.modified);

        manifest.mark_modified("tracking");
        let entry = manifest.configs.get("tracking").unwrap();
        assert_eq!(entry.resource_type, ResourceType::Local);
        assert!(entry.modified);
    }

    #[test]
    fn test_manifest_to_toml() {
        let toml = r#"
[project-tools]
tools_path = "/Users/example/Tools"
resource_type = "symlink"

[configs]
tracking = { enabled = true, type = "symlink", path = ".project/tracking.toml", modified = false }
"#;

        let manifest = Manifest::from_toml(toml).expect("Failed to parse");
        let output = manifest.to_toml();

        assert!(output.contains("tools_path = \"/Users/example/Tools\""));
        assert!(output.contains("resource_type = \"symlink\""));
        assert!(output.contains("enabled = true"));
    }

    #[test]
    fn test_enabled_configs() {
        let toml = r#"
[project-tools]
tools_path = "/Users/example/Tools"
resource_type = "symlink"

[configs]
tracking = { enabled = true, type = "symlink", path = ".project/tracking.toml", modified = false }
lifecycle = { enabled = false, type = "copy", path = ".project/lifecycle.toml", modified = false }
presentation = { enabled = true, type = "symlink", path = ".project/presentation.toml", modified = false }
"#;

        let manifest = Manifest::from_toml(toml).expect("Failed to parse");
        let enabled = manifest.enabled_configs();

        assert_eq!(enabled.len(), 2);
        assert!(enabled.iter().any(|e| e.name == "tracking"));
        assert!(enabled.iter().any(|e| e.name == "presentation"));
    }
}
