use lazy_static::lazy_static;
use regex::Regex;
use std::fmt;
use std::path::Path;
use std::error::Error;
use crate::models::PackageFormat;
use crate::rpm_requires::{parse_rpm_requires, parse_package};
use crate::conda_requires::parse_conda_requires;

/*
 * Design and Rules for Parsing Package Requirements
 *
 * This implementation normalizes package requirements from various package managers
 * (RPM, DEB, Arch Linux, Python, Conda) into a consistent hierarchical structure.
 * The goal is to represent dependencies in a unified way, making it easier to
 * compare, analyze, or process them programmatically.
 *
 * ### Normalized Data Structure
 *
 * The output is structured as follows:
 *
 * 1. `and_depends`: A list of `or_depends` (logical AND groups).
 *    - Each `or_depends` represents a group of dependencies that must be satisfied together.
 *    - and_depends := [or_depends1, or_depends2, or_depends3, ...]
 *
 * 2. `or_depends`: A list of `pkg_depend` (logical OR groups).
 *    - Each `pkg_depend` represents an alternative dependency that can satisfy the requirement.
 *    - or_depends := [pkg_depend1, pkg_depend2, ...]
 *
 * 3. `pkg_depend`: A tuple of `capability` and `constraints`.
 *    - `capability`: A string representing the package name, filename, or module name.
 *      - capability := String of pkgname | filename | modulename | ...
 *    - `constraints`: A list of `version_constraint` (version or conditional requirements).
 *      - pkg_depend := (capability, [version_constraint1, version_constraint2, ...])
 *
 * 4. `version_constraint`: A tuple of `operator` and `operand`.
 *    - `operator`: An enum representing the relationship (e.g., `VersionGreaterThan`, `VersionGreaterThanEqual`, `IfInstall`).
 *      - operator := Enum {
 *          IfInstall,                     // Dependency is required if the operand capability is installed.
 *          VersionGreaterThan,            // Version must be greater than the operand.
 *          VersionGreaterThanEqual,       // Version must be greater than or equal to the operand.
 *          VersionEqual,                  // Version must be equal to the operand.
 *          VersionLessThan,               // Version must be less than the operand.
 *          VersionLessThanEqual,          // Version must be less than or equal to the operand.
 *          VersionNotEqual,               // Version must not be equal to the operand.
 *          ...
 *      }
 *    - `operand`: A string representing the version or condition.
 *      - operand := String of version | capability (when operator=IfInstall)
 *
 * ### Rules for Parsing
 *
 * 1. **Logical Operators**:
 *    - `and`: Represents a conjunction of dependencies (all must be satisfied).
 *    - `or`: Represents a disjunction of dependencies (any one can satisfy the requirement).
 *    - `if`: Represents a conditional dependency (e.g., "feh if Xserver").
 *
 * 2. **Version Constraints**:
 *    - Operators: `>=`, `<=`, `>`, `<`, `=`, `==`, `!=`.
 *    - Operands: Version strings or capabilities (for `if` conditions).
 *
 * 3. **Parentheses Handling**:
 *    - Nested parentheses are supported (e.g., `(feh and xrandr) if Xserver`).
 *    - Parentheses are used to group logical expressions.
 *
 * 4. **Package-Specific Rules**:
 *    - **RPM**:
 *      - Supports conditional dependencies (`if`).
 *      - Handles nested logical expressions.
 *    - **DEB**:
 *      - Uses `|` for alternatives (logical OR).
 *      - Version constraints are enclosed in parentheses (e.g., `libc6 (>= 2.34)`).
 *    - **Arch Linux**:
 *      - Simple space-separated dependencies.
 *      - Version constraints are directly appended to package names.
 *    - **Python**:
 *      - Supports environment markers (e.g., `; sys.platform == "win32"`).
 *      - Multiple constraints per package (e.g., `pbr!=2.1.0,>=2.0.0`).
 *    - **Conda**:
 *      - Simple space-separated dependencies.
 *      - Version constraints use `>=`, `<=`, `=`, etc.
 *
 * ### Error Handling
 *
 * 1. **Unbalanced Parentheses**:
 *    - Detects and reports mismatched parentheses.
 * 2. **Invalid Format**:
 *    - Reports malformed package requirements (e.g., missing version after operator).
 * 3. **Unsupported Operator**:
 *    - Reports unrecognized operators.
 * 4. **Unsupported Package Type**:
 *    - Reports if the package type is not supported.
 *
 * ### Example Inputs and Outputs
 *
 * 1. **RPM Input**: `"((feh and xrandr) if Xserver)"`
 *    - Output:
 *      [
 *        [
 *          PkgDepend { capability: "feh", constraints: [VersionConstraint { operator: IfInstall, operand: "Xserver" }] },
 *        ],
 *        [
 *          PkgDepend { capability: "xrandr", constraints: [VersionConstraint { operator: IfInstall, operand: "Xserver" }] },
 *        ],
 *      ]
 *
 * 2. **DEB Input**: `"libc6 (>= 2.34), libgcc-s1 (>= 3.0) | gcc"`
 *    - Output:
 *      [
 *        [
 *          PkgDepend { capability: "libc6", constraints: [VersionConstraint { operator: VersionGreaterThanEqual, operand: "2.34" }] },
 *        ],
 *        [
 *          PkgDepend { capability: "libgcc-s1", constraints: [VersionConstraint { operator: VersionGreaterThanEqual, operand: "3.0" }] },
 *          PkgDepend { capability: "gcc", constraints: [] },
 *        ],
 *      ]
 *
 * 3. **Python Input**: `"networkx>=2.3.0\npbr!=2.1.0,>=2.0.0"`
 *    - Output:
 *      [
 *        [
 *          PkgDepend { capability: "networkx", constraints: [VersionConstraint { operator: VersionGreaterThanEqual, operand: "2.3.0" }] },
 *        ],
 *        [
 *          PkgDepend { capability: "pbr", constraints: [
 *            VersionConstraint { operator: VersionNotEqual, operand: "2.1.0" },
 *            VersionConstraint { operator: VersionGreaterThanEqual, operand: "2.0.0" },
 *          ]},
 *        ],
 *      ]
 *
 * 4. **Conda Input**: `"python 3.6*, cudatoolkit 9.0.*"`
 *    - Output:
 *      [
 *        [
 *          PkgDepend { capability: "python", constraints: [VersionConstraint { operator: VersionEqual, operand: "3.6*" }] },
 *        ],
 *        [
 *          PkgDepend { capability: "cudatoolkit", constraints: [VersionConstraint { operator: VersionEqual, operand: "9.0.*" }] },
 *        ],
 *      ]
 *
 * ### Extensibility
 *
 * - New package types can be added by implementing their respective parsers.
 * - The normalized structure (`AndDepends`) ensures consistency across package types.
 */

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
#[allow(dead_code)]
pub enum Operator {
    IfInstall,
    VersionGreaterThan,
    VersionGreaterThanEqual,
    VersionLessThan,
    VersionLessThanEqual,
    VersionEqual,
    VersionNotEqual,
    VersionCompatible,
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct VersionConstraint {
    pub operator: Operator,
    pub operand: String,
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct PkgDepend {
    pub capability: String,
    pub constraints: Vec<VersionConstraint>,
}

pub type OrDepends = Vec<PkgDepend>;
pub type AndDepends = Vec<OrDepends>;

#[derive(Debug, PartialEq)]
pub enum ParseError {
    UnbalancedParentheses,
    InvalidFormat(String),
    UnsupportedOperator,
    UnsupportedPackageType,
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ParseError::UnbalancedParentheses => write!(f, "Unbalanced parentheses"),
            ParseError::InvalidFormat(msg) => write!(f, "Invalid format: {}", msg),
            ParseError::UnsupportedOperator => write!(f, "Unsupported operator"),
            ParseError::UnsupportedPackageType => write!(f, "Unsupported package type"),
        }
    }
}

impl Error for ParseError {}



/// Parses the `requires` field into a normalized `AndDepends` structure.
pub fn parse_requires(package_format: PackageFormat, requires: &str) -> Result<AndDepends, ParseError> {
    match package_format {
        PackageFormat::Rpm => parse_rpm_requires(requires),
        PackageFormat::Deb => parse_deb_requires(requires),
        PackageFormat::Pacman => parse_archlinux_requires(requires),
        PackageFormat::Python => parse_python_requires(requires),
        PackageFormat::Conda => parse_conda_requires(requires),
        PackageFormat::Apk => parse_archlinux_requires(requires),
        PackageFormat::Epkg => Err(ParseError::UnsupportedPackageType), // Default case
        PackageFormat::Brew => parse_brew_requires(requires),
    }
}

lazy_static! {
    // Include '=' for cases where it's not normalized (e.g., "kernel=6.6.0")
    // We match '=' only when it's not part of '==', '!=', '>=', '<=', '~='
    // This is done by matching '=' separately and checking context in code
    static ref RPM_OPERATOR_REGEX:       Regex = Regex::new(r"(>=|<=|==|!=|>|<|~=|=)").unwrap();

    static ref ARCHLINUX_OPERATOR_REGEX: Regex = Regex::new(r"(>=|<=|==|!=|>|<|=~|=|~=|~)").unwrap();
    static ref ARCHLINUX_COMMENT_REGEX: Regex = Regex::new(r"\s*: .*").unwrap();
    static ref PYTHON_COMMENT_REGEX: Regex = Regex::new(r"\s*# .*").unwrap();
}

/// Parses DEB-style requirements.
// Example inputs:
// Depends: aria2 | wget | curl, binutils, wine
// Depends: libc6 (>= 2.34)
// Depends: ruby-activesupport (>= 2:5.2.0), ruby-activesupport (<< 2:8.0), ruby-concurrent (>= 1.0), ruby-method-source (>= 1.0)
// Depends: librust-bincode-1+default-dev (>= 1.3.3-~~), librust-jargon-args-0.2+default-dev (>= 0.2.5-~~)
// Depends: libc6 (>= 2.34), libgcc-s1 (>= 4.2)
// Depends: debconf (>= 0.5) | debconf-2.0, wget
// Depends: xwayland, libxcursor1 (>> 1.1.2)
// Depends: python3:any (>= 3.11), python3-magcode-core (>= 1.5.4~), python3-setproctitle
fn parse_deb_requires(requires: &str) -> Result<AndDepends, ParseError> {
    let mut and_depends = Vec::new();

    // Split the input into individual dependencies using commas
    for dependency in requires.split(',') {
        let dependency = dependency.trim();
        if dependency.is_empty() {
            continue;
        }

        // Split the dependency into alternatives using the `|` operator
        let mut or_depends = Vec::new();
        for alternative in dependency.split('|') {
            let alternative = alternative.trim();
            if alternative.is_empty() {
                continue;
            }

            // Parse each alternative as a separate `PkgDepend`
            let (name, constraints) = parse_debian_package(alternative)?;
            or_depends.push(PkgDepend {
                capability: name,
                constraints,
            });
        }

        and_depends.push(or_depends);
    }

    Ok(and_depends)
}

/// Parses a DEB-style package requirement into a name and version constraints.
fn parse_debian_package(clause: &str) -> Result<(String, Vec<VersionConstraint>), ParseError> {
    let clause = clause.trim();

    // Check if the clause contains version constraints in parentheses
    if let Some((name, version_part)) = clause.split_once('(') {
        let name = name.trim().to_string();

        // Extract the version constraints from inside the parentheses
        let version_part = version_part.trim();
        if !version_part.ends_with(')') {
            return Err(ParseError::InvalidFormat("Missing closing parenthesis".to_string()));
        }

        let version_part = &version_part[..version_part.len() - 1]; // Remove the closing ')'
        let constraints = parse_version_constraints(version_part)?;

        Ok((name, constraints))
    } else {
        // No version constraints, just the package name
        Ok((clause.to_string(), Vec::new()))
    }
}

/// Format a version constraint to a string for world.json
/// Returns the constraint string (e.g., "=version1", ">=version2", "=version4" for pkgkey format)
pub fn format_version_constraint_for_world(constraints: &[VersionConstraint]) -> String {
    if constraints.is_empty() {
        return String::new();
    }

    // For normal constraints, format the first constraint
    // APK world format supports: =, >=, >, <=, <, ~, >~, <~
    if let Some(constraint) = constraints.first() {
        let op_str = match constraint.operator {
            Operator::VersionEqual => "=",
            Operator::VersionGreaterThanEqual => ">=",
            Operator::VersionGreaterThan => ">",
            Operator::VersionLessThanEqual => "<=",
            Operator::VersionLessThan => "<",
            Operator::VersionCompatible => "~",
            _ => "=", // Default to = for other operators
        };
        format!("{}{}", op_str, constraint.operand)
    } else {
        String::new()
    }
}

/// Parse a world.json constraint string back into VersionConstraint
/// Examples: "=version1" -> VersionEqual, ">=version2" -> VersionGreaterThanEqual, "" -> None
pub fn parse_world_constraint(constraint_str: &str) -> Option<Vec<VersionConstraint>> {
    if constraint_str.is_empty() {
        return None;
    }

    // Parse the constraint string (e.g., "=version1", ">=version2")
    match parse_version_constraints(constraint_str) {
        Ok(constraints) if !constraints.is_empty() => Some(constraints),
        _ => None,
    }
}

/// Parse a package spec with version constraints (e.g., "pkgname=version", "pkgname>=version")
/// Also supports pkgkey format: "pkgname__version__arch" (e.g., "htop__3.4.1-4__arm64")
/// Returns (package_name_without_version, Option<Vec<VersionConstraint>>, is_pkgkey_format)
///
/// `format` is used to determine whether '~' should be treated as a version operator.
/// In APK format, '~' can be a version operator (e.g., "a~2.2"), but in other formats
/// like RPM, '~' in library names like "libSPIRV-Tools-2025.1~rc1.so" is part of the name.
pub fn parse_package_spec_with_version(spec: &str, format: PackageFormat) -> (String, Option<Vec<VersionConstraint>>) {

    // Check if this is a pkgkey format: pkgname__version__arch
    // Only treat as pkgkey if it matches the exact pattern and doesn't contain parentheses
    // (capabilities with parentheses like "ksym(default:__SCT__cond_resched)" are not pkgkeys)
    let parts: Vec<&str> = spec.split("__").collect();
    if parts.len() == 3 && !spec.contains('(') && !spec.contains(')') {
        // This is a pkgkey format
        let pkgname = parts[0].to_string();
        let version = parts[1].to_string();
        // Create an exact version constraint
        let constraint = VersionConstraint {
            operator: Operator::VersionEqual,
            operand: version,
        };
        return (pkgname, Some(vec![constraint]));
    }

    // Capabilities such as "libfoo.so()(64bit)" (RPM provides) are atomic tokens that
    // include parentheses but never embed inline version operators.  They also don't
    // contain whitespace, so treat them as pure capability names and avoid splitting
    // on characters like '~' that belong to the name rather than a constraint.
    // Fixes: tests/debug_solve.sh opensuse Mesa-dri
    // Also handle ksym capabilities like "ksym(default:__SCT__cond_resched)" which
    // contain colons but are still atomic capabilities.
    // Also handle capabilities with parameters like "font(:lang=en)" where the '='
    // is inside parentheses and should not be treated as a version operator.
    let has_parens = spec.contains('(') && spec.contains(')');
    let has_whitespace = spec.chars().any(|c| c.is_ascii_whitespace());
    // Check for version operators OUTSIDE parentheses (operators inside parentheses
    // are part of the capability name, e.g., "font(:lang=en)")
    let mut paren_depth = 0;
    let has_version_op = spec.chars().any(|c| {
        match c {
            '(' => {
                paren_depth += 1;
                false
            }
            ')' => {
                paren_depth -= 1;
                false
            }
            '<' | '>' | '=' | '!' if paren_depth == 0 => true,
            _ => false,
        }
    });
    let looks_like_atomic_capability = has_parens && !has_whitespace && !has_version_op;
    if looks_like_atomic_capability {
        return (spec.to_string(), None);
    }

    // If we get here, the atomic check didn't match, so look for version operators
    // But first check if this might be a capability with a version that was already
    // split incorrectly (e.g., "ksym(default:" from "ksym(default:__SCT__cond_resched) = version")
    // In that case, we should not try to parse it further

    // Find the first occurrence of a version operator
    // But skip operators that are inside parentheses (e.g., font(:lang=en) should not split on =)
    // Note: '~' can be an operator in APK format (e.g., "a~=2.2" or "a~2.2").
    // In RPM versions like "0.9~rc2-2.fc42", the '~' is part of the version string AFTER an operator,
    // so checking for '~' BEFORE other operators is safe.
    // However, for library names like "libSPIRV-Tools-2025.1~rc1.so", the '~' is part of the name,
    // not an operator. So we only treat '~' as an operator in APK format, and only when
    // followed by a digit (e.g., "package~2.2"). This prevents "~rc1" from being treated as an operator.
    let mut paren_depth = 0;
    let mut found_idx = None;
    // First, check for two-character operators like "~=" before single-character operators
    let chars: Vec<char> = spec.chars().collect();
    for (idx, ch) in spec.char_indices() {
        match ch {
            '(' => paren_depth += 1,
            ')' => paren_depth -= 1,
            '~' if paren_depth == 0 => {
                // Check if it's "~=" (two-character operator) or standalone "~"
                if idx + 1 < chars.len() && chars[idx + 1] == '=' {
                    // This is "~=", split before the "~" (valid in all formats)
                    found_idx = Some(idx);
                    break;
                } else if format == PackageFormat::Apk && idx + 1 < chars.len() {
                    // Standalone "~" operator is only valid in APK format
                    // and only if followed by a digit (e.g., "package~2.2")
                    // This prevents treating "~rc1" in library names as an operator
                    let next_char = chars[idx + 1];
                    if next_char.is_ascii_digit() {
                        found_idx = Some(idx);
                        break;
                    }
                }
                // For non-APK formats or when '~' is not followed by a digit,
                // standalone "~" is part of the name, not an operator
            }
            '>' | '<' | '=' | '!' if paren_depth == 0 => {
                found_idx = Some(idx);
                break;
            }
            _ => {}
        }
    }

    if let Some(idx) = found_idx {
        let name = spec[..idx].trim().to_string();
        let version_part = spec[idx..].trim();

        // Try to parse version constraints
        match parse_version_constraints(version_part) {
            Ok(constraints) if !constraints.is_empty() => {
                return (name, Some(constraints));
            }
            _ => {
                // If parsing fails, treat the whole string as the package name
                return (spec.to_string(), None);
            }
        }
    }

    // No version constraints found
    (spec.to_string(), None)
}

/// Parses Debian/Python/Conda version constraints from a string
/// Example inputs:
/// - ">= 2.34" or ">= 2.34, << 3.0"
/// - ">=1.14.12,<2.0a0"
pub fn parse_version_constraints(version_part: &str) -> Result<Vec<VersionConstraint>, ParseError> {
    parse_version_constraints_and(version_part)
}

/// Parses comma-separated AND constraints (no OR operator).
/// Used internally for parsing version constraints.
pub fn parse_version_constraints_and(version_part: &str) -> Result<Vec<VersionConstraint>, ParseError> {
    let mut constraints = Vec::new();

    // Split the version part by commas to handle multiple constraints
    for constraint in version_part.split(',') {
        let constraint = constraint.trim();
        if constraint.is_empty() {
            continue;
        }

        let (operator, op_len) = match parse_operator_from_start(constraint) {
            Some((op, len)) => (op, len),
            None => (Operator::VersionEqual, 0), // Conda case: if no operator is found, assume it's a version constraint with "=="
        };

        let operand = if op_len > 0 {
            constraint[op_len..].trim().to_string()
        } else {
            constraint.to_string()
        };

        if operand.is_empty() {
            return Err(ParseError::InvalidFormat(format!(
                "Invalid version constraint: {}",
                constraint
            )));
        }

        // Check if the operand ends with `.*` or just `*` and update the operator accordingly
        // Note: VersionEqual and VersionNotEqual now handle both literal and pattern matching
        // Patterns like "=9*" or "=6.9.*" are handled by checking the operand for '*' in the version checking logic

        constraints.push(VersionConstraint { operator, operand });
    }

    Ok(constraints)
}

/// Parses an operator from the start of a string (e.g., ">=1.14.12" -> (Operator::VersionGreaterThanEqual, 2)).
fn parse_operator_from_start(s: &str) -> Option<(Operator, usize)> {
    if s.starts_with(">=") {
        Some((Operator::VersionGreaterThanEqual, 2))
    } else if s.starts_with(">>") {
        Some((Operator::VersionGreaterThan, 2))
    } else if s.starts_with(">") {
        Some((Operator::VersionGreaterThan, 1))
    } else if s.starts_with("<=") {
        Some((Operator::VersionLessThanEqual, 2))
    } else if s.starts_with("<<") {
        Some((Operator::VersionLessThan, 2))
    } else if s.starts_with("<") {
        Some((Operator::VersionLessThan, 1))
    } else if s.starts_with("==") {
        Some((Operator::VersionEqual, 2))
    } else if s.starts_with("=") {
        Some((Operator::VersionEqual, 1))
    } else if s.starts_with("!=") {
        Some((Operator::VersionNotEqual, 2))
    } else if s.starts_with("~=") {
        Some((Operator::VersionCompatible, 2))
    } else if s.starts_with("~") {
        Some((Operator::VersionCompatible, 1))
    } else {
        None
    }
}

/// Parses an operator string into an `Operator` enum.
pub fn parse_operator(op: &str) -> Option<Operator> {
    match op {
        ">=" => Some(Operator::VersionGreaterThanEqual),
        "<=" => Some(Operator::VersionLessThanEqual),
        ">" | ">>" => Some(Operator::VersionGreaterThan),
        "<" | "<<" => Some(Operator::VersionLessThan),
        "=" | "==" => Some(Operator::VersionEqual),
        "!=" => Some(Operator::VersionNotEqual),
        "~=" => Some(Operator::VersionCompatible),
        "=~" => Some(Operator::VersionCompatible),   // https://wiki.alpinelinux.org/wiki/Alpine_Package_Keeper#Package_pinning apk add 'asterisk=~1.6'
        "~"  => Some(Operator::VersionCompatible),   // https://wiki.alpinelinux.org/wiki/APKBUILD_Reference ignores revision part
        // Note: "=*" and "!*" are no longer needed - use "=" and "!=" with operand containing '*'
        "if" => Some(Operator::IfInstall),
        _ => None,
    }
}

/// Parses Arch Linux PKGBUILD requirements.
// Example inputs:
// optdepends=('python-pygments: for syntax highlighting')
// depends=('zsh')
// makedepends=('asciidoc')
// depends=('zsh>=4.3.9')
// depends=('libstk-5.0.0.so=libstk-5.0.0.so-64')  // library alias, not version constraint
pub fn parse_archlinux_requires(requires: &str) -> Result<AndDepends, ParseError> {
    let requires = ARCHLINUX_COMMENT_REGEX.replace(requires, "").to_string();
    let mut and_depends = Vec::new();

    for clause in requires.split_whitespace() {
        // Check if this is a library alias (e.g., "lib.so=lib.so-64")
        // In Arch Linux, = can mean either a version constraint or a library alias.
        // Library aliases typically have .so followed by -64, -32, etc., and don't start with digits.
        if let Some(equals_pos) = clause.find('=') {
            let after_equals = &clause[equals_pos + 1..];
            // Check if it looks like a library alias (contains .so and doesn't start with digit)
            // This distinguishes from version constraints like "libfoo=1.0.0"
            if after_equals.contains(".so") && !after_equals.chars().next().map_or(false, |c| c.is_ascii_digit()) {
                // This is a library alias, not a version constraint
                // Use the left side as the capability name
                let capability = clause[..equals_pos].to_string();
                and_depends.push(vec![PkgDepend {
                    capability,
                    constraints: Vec::new(),
                }]);
                continue;
            }
        }

        // Normalize the clause by adding whitespace around operators
        let normalized_clause = ARCHLINUX_OPERATOR_REGEX.replace_all(clause, " $1 ").to_string();
        let (name, constraints) = parse_package(&normalized_clause)?;
        and_depends.push(vec![PkgDepend {
            capability: name,
            constraints,
        }]);
    }

    Ok(and_depends)
}

/// Parses Brew-style requirements.
// Example inputs:
// oniguruma
// gettext, libunistring
// Brew dependencies are simple package names (no version constraints in formula.json)
pub fn parse_brew_requires(requires: &str) -> Result<AndDepends, ParseError> {
    let mut and_depends = Vec::new();

    for name in requires.split(',') {
        let name = name.trim();
        if name.is_empty() {
            continue;
        }
        and_depends.push(vec![PkgDepend {
            capability: name.to_string(),
            constraints: Vec::new(),
        }]);
    }

    Ok(and_depends)
}

/// Parses Python-style requirements.
// Example inputs:
// pbr!=2.1.0,>=2.0.0 # Apache-2.0
// PyYAML>=3.12 # MIT
// flake8<6.0.0,>=3.6.0 # MIT
// jsonschema>=3.0.2 # MIT
// netifaces==0.11.0; sys.platform == "win32"
// ./granulate-utils/
// humanfriendly==10.0
// beautifulsoup4==4.11.1
pub fn parse_python_requires(requires: &str) -> Result<AndDepends, ParseError> {
    let mut and_depends = Vec::new();
    let requires = PYTHON_COMMENT_REGEX.replace(requires, "").to_string();

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

        if line.is_empty() {
            continue;
        }

        // Split the line into the spec part and the environment marker (if any)
        let (spec_part, marker) = match line.split_once(';') {
            Some((s, m)) => (s.trim(), Some(m.trim())),
            None => (line, None),
        };

        // Parse the spec part into a package name and version constraints
        let (name, constraints) = parse_python_package(spec_part)?;

        // Add the package to the OR dependencies
        let mut or_depends = Vec::new();
        if let Some(marker) = marker {
            // If there's an environment marker, add it as a conditional constraint
            or_depends.push(PkgDepend {
                capability: name,
                constraints: vec![VersionConstraint {
                    operator: Operator::IfInstall,
                    operand: marker.to_string(),
                }],
            });
        } else {
            // Otherwise, add the package with its version constraints
            or_depends.push(PkgDepend {
                capability: name,
                constraints,
            });
        }

        // Add the OR dependencies to the AND dependencies
        and_depends.push(or_depends);
    }

    Ok(and_depends)
}

/// Parses a Python-style package requirement into a name and version constraints.
fn parse_python_package(clause: &str) -> Result<(String, Vec<VersionConstraint>), ParseError> {
    let clause = clause.trim();

    let (name, version_part) = if let Some(idx) = clause.find(|c: char| c == '>' || c == '<' || c == '=' || c == '!' || c == '~') {
        let name = clause[..idx].trim().to_string();
        let version_part = clause[idx..].trim();
        (name, version_part)
    } else {
        // No version constraints, just the package name
        (clause.to_string(), "")
    };

    // Parse version constraints if present
    let constraints = if !version_part.is_empty() {
        parse_version_constraints(version_part)?
    } else {
        Vec::new()
    };

    Ok((name, constraints))
}

#[allow(dead_code)]
pub fn get_package_format(origin_url: &str) -> Option<PackageFormat> {
    let path = Path::new(origin_url);
    // Get filename to handle multi-part extensions like .pkg.tar.xz
    let file_name = path.file_name()
        .and_then(|n| n.to_str())?;
    
    // Use from_suffix_opt which handles both full filenames and extensions
    // Default to Epkg if format cannot be determined
    PackageFormat::from_suffix(file_name).ok().or(Some(PackageFormat::Epkg))
}

// Helper function to create a PkgDepend (for tests)
#[cfg(test)]
pub fn pkg(name: &str, constraints: &[(&str, &str)]) -> PkgDepend {
    PkgDepend {
        capability: name.to_string(),
        constraints: constraints
            .iter()
            .map(|(op, ver)| VersionConstraint {
                operator: parse_operator(op).unwrap(),
                operand: ver.to_string(),
            })
            .collect(),
    }
}

// Helper function to create a PkgDepend with an "if" constraint (for tests)
#[cfg(test)]
pub fn pkg_if(name: &str, condition: &str) -> PkgDepend {
    PkgDepend {
        capability: name.to_string(),
        constraints: vec![VersionConstraint {
            operator: Operator::IfInstall,
            operand: condition.to_string(),
        }],
    }
}

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

    #[test]
    fn test_parse_package_spec_with_version_apk_tilde_operator() {
        // In APK format, '~' can be a version operator, but only if followed by a digit
        let spec = "package~2.2";
        let (name, constraints) = parse_package_spec_with_version(spec, PackageFormat::Apk);
        assert_eq!(name, "package");
        assert!(constraints.is_some());
        let constraints = constraints.unwrap();
        assert_eq!(constraints.len(), 1);
        assert_eq!(constraints[0].operator, Operator::VersionCompatible);
        assert_eq!(constraints[0].operand, "2.2");
    }

    #[test]
    fn test_parse_package_spec_with_version_apk_tilde_not_operator_when_followed_by_letter() {
        // In APK format, '~' should NOT be treated as operator if followed by a letter
        // This handles cases like "package~rc1" or library names with tildes
        let spec = "package~rc1";
        let (name, constraints) = parse_package_spec_with_version(spec, PackageFormat::Apk);
        assert_eq!(name, spec); // Should not split, '~rc1' is part of the name
        assert!(constraints.is_none());
    }

    // Test DEB parsing
    #[test]
    fn test_deb() {
        // Simple package with version
        assert_eq!(
            parse_requires(PackageFormat::Deb, "libc6 (>= 2.34)").unwrap(),
            vec![vec![pkg("libc6", &[(">=", "2.34")])]]
        );

        // Alternative dependencies
        assert_eq!(
            parse_requires(PackageFormat::Deb, "libgcc-s1 (>= 3.0) | gcc").unwrap(),
            vec![vec![
                pkg("libgcc-s1", &[(">=", "3.0")]),
                pkg("gcc", &[]),
            ]]
        );

        // Multiple alternatives
        assert_eq!(
            parse_requires(PackageFormat::Deb, "emacs | emacs-gtk | emacs-lucid").unwrap(),
            vec![vec![
                pkg("emacs", &[]),
                pkg("emacs-gtk", &[]),
                pkg("emacs-lucid", &[]),
            ]]
        );

        // Complex example from original question
        let input = "libao4 (>= 1.1.0), libc6 (>= 2.34), debconf (>= 0.5) | debconf-2.0";
        let result = parse_requires(PackageFormat::Deb, input).unwrap();
        assert_eq!(result.len(), 3);
        assert!(result.contains(&vec![pkg("libao4", &[(">=", "1.1.0")])]));
        assert!(result.contains(&vec![pkg("libc6", &[(">=", "2.34")])]));
        assert!(result.contains(&vec![
            pkg("debconf", &[(">=", "0.5")]),
            pkg("debconf-2.0", &[]),
        ]));
    }

    // Test Arch Linux parsing
    #[test]
    fn test_archlinux() {
        // Simple package
        assert_eq!(
            parse_requires(PackageFormat::Pacman, "bash: GNU Bourne Again SHell").unwrap(),
            vec![vec![pkg("bash", &[])]]
        );

        // Version constraint
        assert_eq!(
            parse_requires(PackageFormat::Pacman, "zsh>=4.3.9").unwrap(),
            vec![vec![pkg("zsh", &[(">=", "4.3.9")])]]
        );

        // Multiple packages
        assert_eq!(
            parse_requires(PackageFormat::Pacman, "git python").unwrap(),
            vec![
                vec![pkg("git", &[])],
                vec![pkg("python", &[])]
            ]
        );

        // Complex example from original question
        let input = "python python-gobject ttf-font gtk3 python-xdg";
        let result = parse_requires(PackageFormat::Pacman, input).unwrap();
        assert_eq!(result.len(), 5);
        assert!(result.contains(&vec![pkg("python", &[])]));
        assert!(result.contains(&vec![pkg("python-gobject", &[])]));
        assert!(result.contains(&vec![pkg("ttf-font", &[])]));
        assert!(result.contains(&vec![pkg("gtk3", &[])]));
        assert!(result.contains(&vec![pkg("python-xdg", &[])]));
    }

    // Test Python parsing
    #[test]
    fn test_python() {
        // Simple requirement
        assert_eq!(
            parse_requires(PackageFormat::Python, "networkx>=2.3.0").unwrap(),
            vec![vec![pkg("networkx", &[(">=", "2.3.0")])]]
        );

        // Multiple constraints
        assert_eq!(
            parse_requires(PackageFormat::Python, "pbr!=2.1.0,>=2.0.0").unwrap(),
            vec![vec![pkg("pbr", &[("!=", "2.1.0"), (">=", "2.0.0")])]]
        );

        // Comment line
        assert_eq!(
            parse_requires(PackageFormat::Python, "pkg # comment").unwrap(),
            vec![vec![pkg("pkg", &[])]]
        );

        // File path
        assert_eq!(
            parse_requires(PackageFormat::Python, "./granulate-utils/").unwrap(),
            vec![vec![pkg("./granulate-utils/", &[])]]
        );

        // compatibility operator (~=)
        assert_eq!(
            parse_requires(PackageFormat::Python, "package~=1.0").unwrap(),
            vec![vec![pkg("package", &[("~=", "1.0")])]]
        );
    }

    // Test parsing of package types with different naming conventions
    #[test]
    fn test_package_naming_conventions() {
        // RPM with namespace
        assert_eq!(
            parse_requires(PackageFormat::Rpm, "perl(Net::LibIDN)").unwrap(),
            vec![vec![pkg("perl(Net::LibIDN)", &[])]]
        );

        // DEB with colon in name
        assert_eq!(
            parse_requires(PackageFormat::Deb, "lib:package").unwrap(),
            vec![vec![pkg("lib:package", &[])]]
        );

        // Python with hyphen in name
        assert_eq!(
            parse_requires(PackageFormat::Python, "package-name").unwrap(),
            vec![vec![pkg("package-name", &[])]]
        );
    }

    #[test]
    fn test_parentheses() {
        // Case 1: Fully enclosed and balanced
        let input = "(A and B and C)";
        assert_eq!(has_outer_parentheses(input), Ok(true));

        // Case 2: Not fully enclosed
        let input = "(A and B and C) if (X or Y)";
        assert_eq!(has_outer_parentheses(input), Ok(false));

        // Case 3: Unbalanced parentheses (missing closing parenthesis)
        let input = "(A and B and C";
        assert_eq!(
            has_outer_parentheses(input),
            Err(ParseError::UnbalancedParentheses)
        );

        // Case 4: Unbalanced parentheses (missing opening parenthesis)
        let input = "(A and B and C))";
        assert_eq!(
            has_outer_parentheses(input),
            Ok(false)
        );

        // Case 5: Unbalanced parentheses (nested and missing closing parenthesis)
        let input = "(A and (B and C)";
        assert_eq!(
            has_outer_parentheses(input),
            Err(ParseError::UnbalancedParentheses)
        );

        // Case 6: Nested and balanced
        let input = "((A and B) if (X or Y))";
        assert_eq!(has_outer_parentheses(input), Ok(true));

        // Case 7: Empty string
        let input = "";
        assert_eq!(has_outer_parentheses(input), Ok(false));

        // Case 8: String without parentheses
        let input = "A and B and C";
        assert_eq!(has_outer_parentheses(input), Ok(false));

        // Case 9: String with only one parenthesis
        let input = "(";
        assert_eq!(
            has_outer_parentheses(input),
            Err(ParseError::UnbalancedParentheses)
        );

        // Case 10: String with only one parenthesis
        let input = ")";
        assert_eq!(
            has_outer_parentheses(input),
            Ok(false)
        );

        // Case 11: String with multiple nested parentheses
        let input = "((A and B) and (C or D))";
        assert_eq!(has_outer_parentheses(input), Ok(true));

        // Case 12: String with mismatched parentheses
        let input = "((A and B) and (C or D)";
        assert_eq!(
            has_outer_parentheses(input),
            Err(ParseError::UnbalancedParentheses)
        );
    }

    #[test]
    fn test_get_package_format() {
        let test_url = "https://mirrors.huaweicloud.com/ubuntu//pool/main/u/ubuntu-themes/ubuntu-mono_24.04-0ubuntu1_all.deb";
        match get_package_format(test_url) {
            Some(format) => println!("包格式: {:?}", format),
            None => println!("无法确定包格式"),
        }
        assert_eq!(get_package_format(test_url), Some(PackageFormat::Deb));
        let rpm_url = "https://repo.openeuler.org/openEuler-24.09/everything/aarch64/Packages/http_load-09Mar2016-1.oe2409.aarch64.rpm";
        assert_eq!(get_package_format(rpm_url), Some(PackageFormat::Rpm));
    }
}