//! Mode A: Cargo.toml introspection for automatic feature detection.

use crate::error::{CargoIntrospectionError, Result};
use crate::models::*;
use std::collections::HashMap;
use std::path::Path;

/// Analyzes Cargo.toml to infer domain features and project configuration.
pub struct CargoIntrospector;

/// Dependency-to-feature mapping heuristics.
impl CargoIntrospector {
    /// Analyze a Cargo.toml file to extract project information.
    pub fn analyze(cargo_path: impl AsRef<Path>) -> Result<ProjectSpec> {
        let cargo_path = cargo_path.as_ref();

        if !cargo_path.exists() {
            return Err(CargoIntrospectionError {
                cargo_path: cargo_path.to_path_buf(),
                reason: "File does not exist".to_string(),
            }
            .into());
        }

        let manifest =
            cargo_toml::Manifest::from_path(cargo_path).map_err(|e| CargoIntrospectionError {
                cargo_path: cargo_path.to_path_buf(),
                reason: format!("Failed to parse: {}", e),
            })?;

        let package = manifest
            .package
            .as_ref()
            .ok_or_else(|| CargoIntrospectionError {
                cargo_path: cargo_path.to_path_buf(),
                reason: "Package section missing in Cargo.toml".to_string(),
            })?;

        let name = package.name.clone();

        // Determine project type from analysis
        let project_type = Self::infer_project_type(&manifest);

        // Infer infrastructure requirements
        let infrastructure = Self::infer_infrastructure(&manifest);

        // Infer domain features
        let domain_features = Self::infer_domain_features(&manifest);

        // Build constraints from inferred features
        let constraints = Self::infer_constraints(&domain_features);

        let mut spec = ProjectSpec::new(name, project_type);
        spec.infrastructure = infrastructure;
        spec.domain_features = domain_features;
        spec.constraints = constraints;

        // Validate the spec
        spec.validate().map_err(|errors| CargoIntrospectionError {
            cargo_path: cargo_path.to_path_buf(),
            reason: format!("Invalid spec generated: {}", errors.join(", ")),
        })?;

        Ok(spec)
    }

    /// Infer project type from manifest metadata.
    fn infer_project_type(manifest: &cargo_toml::Manifest) -> ProjectType {
        // Check description field first
        if let Some(package) = &manifest.package {
            if let Some(description) = &package.description {
                // Inheritable<String> - use Debug or convert to string
                let desc_str = format!("{:?}", description);
                let desc_lower = desc_str.to_lowercase();
                if desc_lower.contains("service") || desc_lower.contains("api") {
                    return ProjectType::WebService;
                } else if desc_lower.contains("microservice") {
                    return ProjectType::Microservice;
                } else if desc_lower.contains("tool") || desc_lower.contains("cli") {
                    return ProjectType::CliTool;
                }
            }
        }

        // Check dependencies for web frameworks
        let deps = Self::collect_all_dependencies(manifest);
        if deps.contains_key("axum")
            || deps.contains_key("actix-web")
            || deps.contains_key("rocket")
            || deps.contains_key("warp")
            || deps.contains_key("tide")
        {
            return ProjectType::WebService;
        }

        // Check for CLI frameworks
        if deps.contains_key("clap") || deps.contains_key("structopt") {
            return ProjectType::CliTool;
        }

        // Default based on presence of binaries
        // manifest.bin is a BTreeMap, not Option. Check if not empty.
        if !manifest.bin.is_empty() {
            ProjectType::CliTool
        } else {
            ProjectType::Library
        }
    }

    /// Infer infrastructure requirements from dependencies.
    fn infer_infrastructure(manifest: &cargo_toml::Manifest) -> InfrastructureSpec {
        let deps = Self::collect_all_dependencies(manifest);

        let ssh = deps.contains_key("openssh-keys") || deps.contains_key("ssh2");

        let database = if deps.contains_key("sqlx") || deps.contains_key("rusqlite") {
            Some(DatabaseSpec {
                db_type: DatabaseType::Sqlite,
                required: true,
            })
        } else if deps.contains_key("mysql") || deps.contains_key("sqlx") {
            Some(DatabaseSpec {
                db_type: DatabaseType::Mysql,
                required: true,
            })
        } else if deps.contains_key("postgres") || deps.contains_key("sqlx") {
            Some(DatabaseSpec {
                db_type: DatabaseType::Postgres,
                required: true,
            })
        } else {
            None
        };

        let mut providers = Vec::new();
        if deps.contains_key("aws-config") {
            providers.push(CloudProvider::Aws);
        }
        if deps.contains_key("google-cloudkms1") {
            providers.push(CloudProvider::Gcp);
        }
        if providers.is_empty() {
            providers.push(CloudProvider::Lxd);
        }

        let mut monitoring = Vec::new();
        if deps.contains_key("prometheus") {
            monitoring.push(MonitoringType::Prometheus);
        }
        if deps.contains_key("grafana") {
            monitoring.push(MonitoringType::Grafana);
        }

        InfrastructureSpec {
            ssh,
            database,
            providers,
            monitoring,
        }
    }

    /// Infer domain features from dependencies and features.
    fn infer_domain_features(manifest: &cargo_toml::Manifest) -> Vec<DomainFeature> {
        let deps = Self::collect_all_dependencies(manifest);
        let mut features = Vec::new();

        // HTTP server detection
        if deps.contains_key("axum")
            || deps.contains_key("actix-web")
            || deps.contains_key("rocket")
        {
            let mut http_feature = DomainFeature::new("http_server".to_string());
            http_feature.description = Some("HTTP/REST API server".to_string());

            http_feature = http_feature
                .with_field(
                    ConfigField::new(
                        "bind_address".to_string(),
                        FieldType::Text,
                        "Server bind address".to_string(),
                    )
                    .with_default(serde_json::json!("0.0.0.0:8080"))
                    .with_help("Format: IP:PORT (e.g., 0.0.0.0:8080)"),
                )
                .with_field(
                    ConfigField::new(
                        "timeout_seconds".to_string(),
                        FieldType::Number,
                        "Request timeout".to_string(),
                    )
                    .with_default(serde_json::json!(30))
                    .with_help("Timeout in seconds for HTTP requests"),
                );

            features.push(http_feature);
        }

        // Authentication detection
        if deps.contains_key("jsonwebtoken") || deps.contains_key("oauth2") {
            let mut auth_feature = DomainFeature::new("authentication".to_string());
            auth_feature.description = Some("User authentication".to_string());

            auth_feature = auth_feature.with_field(
                ConfigField::new(
                    "jwt_secret".to_string(),
                    FieldType::Password,
                    "JWT signing secret".to_string(),
                )
                .with_help("Secret key for JWT token signing")
                .sensitive("age"),
            );

            features.push(auth_feature);
        }

        // Caching detection
        if deps.contains_key("redis") || deps.contains_key("memcache") {
            let mut cache_feature = DomainFeature::new("caching".to_string());
            cache_feature.description = Some("Caching layer".to_string());

            cache_feature = cache_feature
                .with_field(
                    ConfigField::new(
                        "cache_enabled".to_string(),
                        FieldType::Confirm,
                        "Enable caching".to_string(),
                    )
                    .with_default(serde_json::json!(true)),
                )
                .with_field(
                    ConfigField::new(
                        "cache_ttl_seconds".to_string(),
                        FieldType::Number,
                        "Cache TTL".to_string(),
                    )
                    .with_default(serde_json::json!(3600))
                    .with_help("Time to live in seconds"),
                );

            features.push(cache_feature);
        }

        // Default: at least one feature (basic configuration)
        if features.is_empty() {
            let mut basic = DomainFeature::new("basic_config".to_string());
            basic.description = Some("Basic project configuration".to_string());
            basic = basic.with_field(
                ConfigField::new(
                    "config_version".to_string(),
                    FieldType::Text,
                    "Configuration version".to_string(),
                )
                .with_default(serde_json::json!("1.0"))
                .optional(),
            );
            features.push(basic);
        }

        features
    }

    /// Infer constraints from domain features.
    fn infer_constraints(features: &[DomainFeature]) -> Vec<Constraint> {
        let mut constraints = Vec::new();

        for feature in features {
            for field in &feature.fields {
                // Only create constraints for repeating group fields with min/max bounds
                if field.field_type == FieldType::RepeatingGroup {
                    if let (Some(min), Some(max)) = (field.min, field.max) {
                        let constraint =
                            Constraint::new(format!("{}.{}", feature.name, field.name))
                                .with_min_items(min)
                                .with_max_items(max);

                        constraints.push(constraint);
                    }
                }
            }
        }

        constraints
    }

    /// Collect all dependencies (both regular and dev).
    fn collect_all_dependencies(manifest: &cargo_toml::Manifest) -> HashMap<String, String> {
        let mut deps = HashMap::new();

        // Dependencies is a BTreeMap, not Option
        for name in manifest.dependencies.keys() {
            deps.insert(name.clone(), "dependency".to_string());
        }

        // Dev dependencies is a BTreeMap, not Option
        for name in manifest.dev_dependencies.keys() {
            deps.insert(name.clone(), "dev-dependency".to_string());
        }

        deps
    }
}

#[cfg(test)]
mod tests {

    #[test]
    fn test_project_type_inference() {
        // Tests for project type detection would go here
        // Requires fixture Cargo.toml files
    }

    #[test]
    fn test_infrastructure_inference() {
        // Tests for infrastructure detection
    }

    #[test]
    fn test_domain_features_inference() {
        // Tests for feature detection
    }
}
