"""
Version management utilities.

This module provides utilities for working with versions and setuptools-scm.
"""

import subprocess
from pathlib import Path
from typing import Optional, Dict, Any
from packaging import version


class VersionManager:
    """Utilities for version management with setuptools-scm."""

    def __init__(self, project_root: Optional[Path] = None):
        """Initialize version manager.

        Args:
            project_root: Root directory of the project
        """
        self.project_root = project_root or Path.cwd()

    def get_current_version(self) -> str:
        """Get current version using setuptools-scm.

        Returns:
            Current version string or "unknown" if unavailable
        """
        try:
            result = subprocess.run(
                ['python', '-m', 'setuptools_scm'],
                capture_output=True,
                text=True,
                check=True,
                cwd=self.project_root
            )
            return result.stdout.strip()
        except subprocess.CalledProcessError:
            return "unknown"

    def parse_version(self, version_string: str) -> Dict[str, Any]:
        """Parse a version string and return components.

        Args:
            version_string: Version string to parse

        Returns:
            Dictionary with version components
        """
        try:
            v = version.Version(version_string)
            return {
                'major': v.major,
                'minor': v.minor,
                'micro': v.micro,
                'is_prerelease': v.is_prerelease,
                'is_devrelease': v.is_devrelease,
                'local': v.local,
                'public': v.public,
                'base_version': v.base_version,
            }
        except version.InvalidVersion:
            return {'error': f'Invalid version: {version_string}'}

    def is_development_version(self, version_string: Optional[str] = None) -> bool:
        """Check if version is a development version.

        Args:
            version_string: Version to check. If None, uses current version.

        Returns:
            True if development version, False otherwise
        """
        if version_string is None:
            version_string = self.get_current_version()

        try:
            v = version.Version(version_string)
            return v.is_devrelease or 'dev' in version_string.lower()
        except version.InvalidVersion:
            return True  # Assume unknown versions are dev

    def compare_versions(self, version1: str, version2: str) -> int:
        """Compare two version strings.

        Args:
            version1: First version to compare
            version2: Second version to compare

        Returns:
            -1 if version1 < version2, 0 if equal, 1 if version1 > version2
        """
        try:
            v1 = version.Version(version1)
            v2 = version.Version(version2)

            if v1 < v2:
                return -1
            elif v1 > v2:
                return 1
            else:
                return 0
        except version.InvalidVersion:
            # Fallback to string comparison
            if version1 < version2:
                return -1
            elif version1 > version2:
                return 1
            else:
                return 0

    def get_next_version(self, current_version: str, bump_type: str = 'patch') -> str:
        """Get the next version based on bump type.

        Args:
            current_version: Current version string
            bump_type: Type of bump ('major', 'minor', 'patch')

        Returns:
            Next version string

        Raises:
            ValueError: If bump_type is invalid
        """
        try:
            v = version.Version(current_version)
            major, minor, micro = v.major, v.minor, v.micro

            if bump_type == 'major':
                return f"{major + 1}.0.0"
            elif bump_type == 'minor':
                return f"{major}.{minor + 1}.0"
            elif bump_type == 'patch':
                return f"{major}.{minor}.{micro + 1}"
            else:
                raise ValueError(f"Invalid bump type: {bump_type}")

        except version.InvalidVersion:
            raise ValueError(f"Cannot parse version: {current_version}")

    def suggest_version(self, current_version: Optional[str] = None) -> Dict[str, str]:
        """Suggest next version options.

        Args:
            current_version: Current version. If None, gets from setuptools-scm.

        Returns:
            Dictionary with version suggestions
        """
        if current_version is None:
            current_version = self.get_current_version()

        if current_version == "unknown":
            return {
                'error': 'Cannot determine current version',
                'suggestion': 'Consider creating an initial tag like v0.1.0'
            }

        try:
            # Strip development version info to get base
            v = version.Version(current_version)
            base_version = v.base_version

            return {
                'current': current_version,
                'base': base_version,
                'patch': self.get_next_version(base_version, 'patch'),
                'minor': self.get_next_version(base_version, 'minor'),
                'major': self.get_next_version(base_version, 'major'),
            }
        except Exception as e:
            return {
                'error': str(e),
                'current': current_version
            }

    def validate_version_format(self, version_string: str) -> bool:
        """Validate if a version string follows semantic versioning.

        Args:
            version_string: Version string to validate

        Returns:
            True if valid semantic version, False otherwise
        """
        try:
            version.Version(version_string)
            return True
        except version.InvalidVersion:
            return False

    def get_version_info(self, project_root: Optional[Path] = None) -> Dict[str, Any]:
        """Get comprehensive version information for a project.

        Args:
            project_root: Root directory of project. If None, uses current directory.

        Returns:
            Dictionary with version information
        """
        if project_root:
            original_root = self.project_root
            self.project_root = project_root

        try:
            current_version = self.get_current_version()

            # Try to get git information
            git_info = self._get_git_info()

            # Parse version components
            version_parts = self.parse_version(current_version) if current_version != "unknown" else {}

            return {
                'full_version': current_version,
                'short_version': current_version.split('.dev')[0] if '.dev' in current_version else current_version,
                'version_components': version_parts,
                'is_dev': self.is_development_version(current_version),
                'git_commit': git_info.get('commit'),
                'git_branch': git_info.get('branch'),
                'is_git_repo': git_info.get('is_repo', False)
            }
        finally:
            if project_root:
                self.project_root = original_root

    def get_release_info(self, project_root: Optional[Path] = None) -> Dict[str, Any]:
        """Get release information for a project.

        Args:
            project_root: Root directory of project. If None, uses current directory.

        Returns:
            Dictionary with release information
        """
        from datetime import datetime

        version_info = self.get_version_info(project_root)

        return {
            'name': 'MarkiTect',
            'version': version_info['full_version'],
            'short_version': version_info['short_version'],
            'is_development': version_info['is_dev'],
            'git_branch': version_info.get('git_branch', 'unknown'),
            'git_commit': version_info.get('git_commit', 'unknown'),
            'build_date': datetime.now().isoformat(),
            'python_version': f"{__import__('sys').version_info.major}.{__import__('sys').version_info.minor}.{__import__('sys').version_info.micro}"
        }

    def _get_git_info(self) -> Dict[str, Any]:
        """Get git repository information.

        Returns:
            Dictionary with git information
        """
        git_info = {'is_repo': False}

        try:
            # Check if in git repo
            subprocess.run(['git', 'rev-parse', '--git-dir'],
                         cwd=self.project_root, check=True, capture_output=True)
            git_info['is_repo'] = True

            # Get branch
            try:
                result = subprocess.run(['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
                                      cwd=self.project_root, capture_output=True, text=True, check=True)
                git_info['branch'] = result.stdout.strip()
            except subprocess.CalledProcessError:
                git_info['branch'] = 'unknown'

            # Get commit
            try:
                result = subprocess.run(['git', 'rev-parse', 'HEAD'],
                                      cwd=self.project_root, capture_output=True, text=True, check=True)
                git_info['commit'] = result.stdout.strip()
            except subprocess.CalledProcessError:
                git_info['commit'] = 'unknown'

        except subprocess.CalledProcessError:
            pass  # Not a git repo

        return git_info


# Convenience functions for backward compatibility and easy import
def get_version_info(project_root: Optional[Path] = None) -> Dict[str, Any]:
    """Get version information using default VersionManager."""
    manager = VersionManager(project_root)
    return manager.get_version_info()


def get_release_info(project_root: Optional[Path] = None) -> Dict[str, Any]:
    """Get release information using default VersionManager."""
    manager = VersionManager(project_root)
    return manager.get_release_info()