"""Version comparison utilities with support for caret (^) notation."""

from typing import Tuple, List
import re


def parse_version(version_str: str) -> Tuple[int, ...]:
    """
    Parse a version string into a tuple of integers.
    
    Extracts version numbers in the format X.Y.Z (e.g., "1.2.3", "1.0")
    Ignores any additional suffixes like "-20260319.1" or "-alpha"
    
    Args:
        version_str: Version string (e.g., "1.2.3", "2.1.79-20260319.1", "v1.0.0")
    
    Returns:
        Tuple of version components
    """
    # Remove any leading 'v' or 'V'
    version_str = version_str.lstrip('vV')
    
    # Use regex to extract version numbers (X.Y.Z pattern)
    # Matches: 1.2.3, 2.1.79 from "2.1.79-20260319.1"
    version_match = re.match(r'^(\d+(?:\.\d+)*)', version_str)
    
    if version_match:
        version_str = version_match.group(1)
    else:
        # If no match, return empty tuple
        return ()
    
    # Extract numeric parts
    parts = []
    for part in version_str.split('.'):
        match = re.match(r'^(\d+)', part)
        if match:
            parts.append(int(match.group(1)))
    
    return tuple(parts)


def compare_versions(v1: Tuple[int, ...], v2: Tuple[int, ...]) -> int:
    """
    Compare two version tuples.
    
    Args:
        v1: First version tuple
        v2: Second version tuple
    
    Returns:
        -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
    """
    max_len = max(len(v1), len(v2))
    
    for i in range(max_len):
        v1_part = v1[i] if i < len(v1) else 0
        v2_part = v2[i] if i < len(v2) else 0
        
        if v1_part < v2_part:
            return -1
        elif v1_part > v2_part:
            return 1
    
    return 0


def check_version_requirement(current_version: str, required_version: str) -> bool:
    """
    Check if current version satisfies the required version requirement.
    
    Supports:
    - Exact version: "1.2.3"
    - Caret notation: "^1.2.3" or "1.2.3^" (compatible with >=1.2.3 and <2.0.0)
    
    Args:
        current_version: Current version string
        required_version: Required version string (may include ^)
    
    Returns:
        True if current version satisfies requirement, False otherwise
    """
    current = parse_version(current_version)
    
    # Check for caret notation (support both ^prefix and suffix^)
    is_caret = required_version.startswith('^') or required_version.endswith('^')
    
    if is_caret:
        # Remove ^ from either prefix or suffix
        if required_version.startswith('^'):
            required = parse_version(required_version[1:])
        else:
            required = parse_version(required_version[:-1])
        
        # For ^1.2.3, we need >=1.2.3 and <2.0.0
        # For ^0.2.3, we need >=0.2.3 and <0.3.0 (special case for 0.x versions)
        if len(required) > 0:
            # Create upper bound based on semver caret rules
            if required[0] == 0:
                # For 0.x.y, increment the second component
                if len(required) >= 2:
                    upper_bound = [required[0], required[1] + 1] + [0] * (len(required) - 2)
                else:
                    upper_bound = [required[0] + 1]
            else:
                # For >=1.x.y, increment the first component
                upper_bound = [required[0] + 1] + [0] * (len(required) - 1)
            upper_bound = tuple(upper_bound)
            
            # Check if current >= required and current < upper_bound
            return (compare_versions(current, required) >= 0 and
                    compare_versions(current, upper_bound) < 0)
    else:
        # Exact version match
        required = parse_version(required_version)
        return compare_versions(current, required) >= 0
    
    return False


def validate_support_agents(support_agents: dict, target_agents: list, current_versions: dict, package_name: str = "") -> List[str]:
    """
    Validate support_agents against target_agents and current versions.

    Args:
        support_agents: Dictionary mapping agent names to version requirements
        target_agents: List of target agents
        current_versions: Dictionary mapping agent names to current versions
        package_name: Name of the package for warning messages (optional)

    Returns:
        List of warning messages (empty if no warnings)
    """
    warnings = []
    pkg_prefix = f"[{package_name}] " if package_name else ""

    for agent in target_agents:
        if agent not in support_agents:
            warnings.append(
                f"{pkg_prefix}Warning: Agent '{agent}' is not in support_agents. "
                f"Supported agents: {list(support_agents.keys())}"
            )
            continue

        required_version = support_agents[agent]
        if agent not in current_versions:
            warnings.append(
                f"{pkg_prefix}Warning: Cannot verify version for agent '{agent}'. "
                f"Current version not provided."
            )
            continue

        current_version = current_versions[agent]

        if not check_version_requirement(current_version, required_version):
            warnings.append(
                f"{pkg_prefix}Warning: Agent '{agent}' version {current_version} does not meet "
                f"required version {required_version}"
            )

    return warnings


def validate_metadata(metadata: dict, package_name: str) -> List[str]:
    """
    Validate metadata for required fields.
    
    Args:
        metadata: Dictionary containing metadata
        package_name: Name of the package (for warning messages)
    
    Returns:
        List of warning messages (empty if no warnings)
    """
    warnings = []
    
    if 'version' not in metadata:
        warnings.append(
            f"Warning: Missing 'version' field in {package_name} metadata. "
            f"Version tracking will not be available."
        )
    
    if 'support_agents' not in metadata:
        warnings.append(
            f"Warning: Missing 'support_agents' field in {package_name} metadata. "
            f"Version compatibility checks will be skipped."
        )
    
    return warnings


def normalize_support_field(metadata: dict) -> dict:
    """
    Normalize support field name to support_agents.
    
    Args:
        metadata: Dictionary containing metadata
    
    Returns:
        Dictionary with support_agents field
    """
    result = metadata.copy()
    
    # Ensure support_agents field exists (for backward compatibility with old metadata)
    if 'support_agents' not in result:
        result['support_agents'] = {}
    
    return result