//! Dependency requirement processing and conversion
//!
//! This module handles the processing of package dependency requirements and
//! their conversion into resolvo ConditionalRequirement structures. Key features:
//! - Support for multiple dependency fields (Requires, Recommends, Suggests, etc.)
//! - IfInstall conditional dependency handling
//! - Capability normalization and no-install filtering
//! - Missing package/capability checking with ignore_missing support

use resolvo::{Condition, ConditionId, ConditionalRequirement, KnownDependencies, LogicalOperator};
use crate::models::Package;
use crate::parse_requires::{AndDepends, Operator, PkgDepend};
use crate::resolve::types::{NameType, SolverMatchSpec, DependFieldFlags};
use crate::resolve::provider::GenericDependencyProvider;
use color_eyre::Result;
use std::collections::HashSet;

impl GenericDependencyProvider {

    /// Process dependency requirements from a package
    pub fn process_requirements(&self, package: &Package, known_deps: &mut KnownDependencies) {
        let dep_strings = self.get_dependency_strings(package);

        for dep_str in &dep_strings {
            // Parse the dependency string
            let and_depends = match crate::parse_requires::parse_requires(self.format, dep_str) {
                Ok(deps) => deps,
                Err(e) => {
                    log::warn!("[RESOLVO] Failed to parse dependency '{}': {}", dep_str, e);
                    continue;
                }
            };

            match self.convert_and_depends_to_requirements(&and_depends) {
                Ok(reqs) => known_deps.requirements.extend(reqs),
                Err(e) => {
                    log::warn!(
                        "[RESOLVO] Failed to convert dependency specification '{}': {}",
                        dep_str,
                        e
                    );
                }
            }
        }
    }

    /// Get dependency strings from a package based on depend_fields
    fn get_dependency_strings(&self, package: &Package) -> HashSet<String> {
        let mut deps = HashSet::new();

        if self.depend_fields.borrow().contains(DependFieldFlags::REQUIRES) {
            deps.extend(package.requires.iter().cloned());
            deps.extend(package.requires_pre.iter().cloned());
        }

        if self.depend_fields.borrow().contains(DependFieldFlags::RECOMMENDS) {
            deps.extend(package.recommends.iter().cloned());
        }

        if self.depend_fields.borrow().contains(DependFieldFlags::SUGGESTS) {
            deps.extend(package.suggests.iter().cloned());
        }

        if self.depend_fields.borrow().contains(DependFieldFlags::BUILD_REQUIRES)
            && package.repodata_name == "aur"
        {
            deps.extend(package.build_requires.iter().cloned());
        }

        if self.depend_fields.borrow().contains(DependFieldFlags::CHECK_REQUIRES)
        {
            deps.extend(package.check_requires.iter().cloned());
        }

        deps
    }

    /// Convert AndDepends to resolvo ConditionalRequirement list
    /// Handles IfInstall conditions by creating proper ConditionalRequirement with conditions
    pub fn convert_and_depends_to_requirements(
        &self,
        and_depends: &AndDepends,
    ) -> Result<Vec<ConditionalRequirement>> {
        let ignore_missing = crate::models::config().common.ignore_missing;
        let mut requirements = Vec::new();

        for or_depends in and_depends {
            // For each OR group, we need to create a union of version sets
            let mut version_set_ids = Vec::new();
            let mut condition_ids: Vec<Option<ConditionId>> = Vec::new();

            for pkg_depend in or_depends {
                let capability = &pkg_depend.capability;

                // Normalize capability name by stripping architecture suffixes (e.g., python3:any -> python3)
                // This matches the behavior of the simple solver's resolve_single_capability_item
                let normalized_capability = self.normalize_capability_name(capability);

                // Filter out dependencies where the package name is in no-install
                // Check the normalized capability name (which is what we actually use for lookups)
                if !self.no_install.is_empty() && self.no_install.contains(&normalized_capability) {
                    log::debug!(
                        "[RESOLVO] Skipping dependency '{}' (normalized: '{}') - in no-install list",
                        capability,
                        normalized_capability
                    );
                    continue;
                }

                // Check if package/capability exists when ignore_missing is enabled
                if ignore_missing
                    && !self.check_package_or_capability_exists(&normalized_capability)
                {
                    log::info!(
                        "[RESOLVO] Package/capability '{}' not found, skipping (ignore_missing=true)",
                        normalized_capability
                    );
                    continue;
                }

                // Check for IfInstall constraints
                let mut if_install_conditions = Vec::new();
                let mut non_conditional_constraints = Vec::new();

                for constraint in &pkg_depend.constraints {
                    if constraint.operator == Operator::IfInstall {
                        // Create a condition: the operand package must be installed
                        // The operand is the package name that must be installed for this dependency to apply
                        let condition_pkg_name = &constraint.operand;

                        // Check if condition package exists when ignore_missing is enabled
                        if ignore_missing
                            && !self.check_package_or_capability_exists(condition_pkg_name)
                        {
                            log::warn!(
                                "[RESOLVO] IfInstall condition package '{}' not found, skipping condition (ignore_missing=true)",
                                condition_pkg_name
                            );
                            continue;
                        }

                        let condition_name_id = self
                            .pool
                            .intern_package_name(NameType(condition_pkg_name.clone()));

                        // Create a version set that matches any version of the condition package
                        // This condition is true if the condition package is installed (any version)
                        // We create a PkgDepend with no constraints (empty constraints = any version)
                        let pkg_depend = PkgDepend {
                            capability: condition_pkg_name.clone(),
                            constraints: Vec::new(), // No constraints = matches any version
                        };
                        let or_deps = vec![pkg_depend];
                        let and_deps = vec![or_deps];
                        let condition_version_set_id = self.pool.intern_version_set(
                            condition_name_id,
                            SolverMatchSpec::MatchSpec(and_deps),
                        );

                        // Create Condition::Requirement - this is true if the package is installed
                        let condition = Condition::Requirement(condition_version_set_id);
                        let condition_id = self.pool.intern_condition(condition);
                        if_install_conditions.push(condition_id);
                    } else {
                        non_conditional_constraints.push(constraint.clone());
                    }
                }

                // Use the non-conditional constraints
                let final_constraints = non_conditional_constraints;

                // Intern the normalized package name (without arch suffix)
                log::trace!(
                    "[RESOLVO] Processing requirement: original='{}', normalized='{}'",
                    capability,
                    normalized_capability
                );
                let name_id = self
                    .pool
                    .intern_package_name(NameType(normalized_capability.clone()));

                // Create version set from non-conditional constraints using normalized capability
                let pkg_dep = PkgDepend {
                    capability: normalized_capability.clone(),
                    constraints: final_constraints,
                };
                let or_deps = vec![pkg_dep];
                let and_deps = vec![or_deps];

                let version_set_id = self
                    .pool
                    .intern_version_set(name_id, SolverMatchSpec::MatchSpec(and_deps));

                version_set_ids.push(version_set_id);

                // Combine all IfInstall conditions with AND (all must be true)
                if !if_install_conditions.is_empty() {
                    let mut combined_condition = if_install_conditions[0];
                    for &cond_id in &if_install_conditions[1..] {
                        let and_condition =
                            Condition::Binary(LogicalOperator::And, combined_condition, cond_id);
                        combined_condition = self.pool.intern_condition(and_condition);
                    }
                    condition_ids.push(Some(combined_condition));
                } else {
                    condition_ids.push(None);
                }
            }

            if version_set_ids.is_empty() {
                continue;
            }

            // Create requirement (single or union)
            let requirement: resolvo::Requirement = if version_set_ids.len() == 1 {
                version_set_ids[0].into()
            } else {
                let first = version_set_ids[0];
                let union_id = self
                    .pool
                    .intern_version_set_union(first, version_set_ids[1..].iter().copied());
                union_id.into()
            };

            // Combine conditions: if multiple OR alternatives have conditions, we need to OR them
            // For now, if any condition exists, use the first one (simplified)
            // In a full implementation, we'd OR all conditions together
            let final_condition = condition_ids
                .iter()
                .find(|c| c.is_some())
                .copied()
                .flatten();

            requirements.push(ConditionalRequirement {
                requirement,
                condition: final_condition,
            });
        }

        Ok(requirements)
    }

}