"""
Release validation utilities.

This module provides validation functions for release readiness.
"""

import subprocess
from pathlib import Path
from typing import List, Tuple, Optional

from ..git.manager import GitManager


class ReleaseValidator:
    """Validates release readiness and requirements."""

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

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

    def validate_release_state(self, force: bool = False) -> Tuple[bool, List[str]]:
        """Validate that repository is ready for release.

        Args:
            force: Skip validation checks if True

        Returns:
            Tuple of (is_valid, list_of_issues)
        """
        if force:
            return True, []

        issues = []

        # Git repository validation
        git_issues = self._validate_git_state()
        issues.extend(git_issues)

        # Project structure validation
        structure_issues = self._validate_project_structure()
        issues.extend(structure_issues)

        # Configuration validation
        config_issues = self._validate_configuration()
        issues.extend(config_issues)

        # CHANGELOG validation
        changelog_issues = self._validate_changelog()
        issues.extend(changelog_issues)

        return len(issues) == 0, issues

    def _validate_git_state(self) -> List[str]:
        """Validate git repository state.

        Returns:
            List of git-related issues
        """
        issues = []
        status = self.git_manager.get_repository_status()

        if not status['is_repo']:
            issues.append("Not in a git repository")
            return issues

        if status['has_changes']:
            issues.append("Repository has uncommitted changes")

        if status['branch'] != 'main':
            issues.append(f"Not on main branch (currently on {status['branch']})")

        # Check if remote exists
        remote_url = self.git_manager.get_remote_url()
        if not remote_url:
            issues.append("No git remote 'origin' configured")

        return issues

    def _validate_project_structure(self) -> List[str]:
        """Validate project structure for releases.

        Returns:
            List of project structure issues
        """
        issues = []

        # Check for required files
        required_files = ['pyproject.toml']
        for file_name in required_files:
            file_path = self.project_root / file_name
            if not file_path.exists():
                issues.append(f"Missing required file: {file_name}")

        # Check for setuptools-scm configuration
        pyproject_path = self.project_root / 'pyproject.toml'
        if pyproject_path.exists():
            try:
                import tomllib
            except ImportError:
                try:
                    import tomli as tomllib
                except ImportError:
                    issues.append("Cannot read pyproject.toml (tomllib/tomli not available)")
                    return issues

            try:
                with open(pyproject_path, 'rb') as f:
                    config = tomllib.load(f)

                # Check for setuptools-scm configuration
                build_system = config.get('build-system', {})
                if 'setuptools-scm' not in str(build_system.get('requires', [])):
                    issues.append("setuptools-scm not found in build-system.requires")

                # Check for dynamic version
                project_config = config.get('project', {})
                if 'version' in project_config:
                    issues.append("Static version found in project config. Use dynamic versioning with setuptools-scm.")

                dynamic = project_config.get('dynamic', [])
                if 'version' not in dynamic:
                    issues.append("'version' not in project.dynamic. Add it for setuptools-scm.")

            except Exception as e:
                issues.append(f"Error reading pyproject.toml: {e}")

        return issues

    def _validate_configuration(self) -> List[str]:
        """Validate release configuration.

        Returns:
            List of configuration issues
        """
        issues = []

        # Check for environment variables that might be needed
        import os

        # Check for common auth tokens (warn, don't fail)
        auth_vars = ['GITEA_API_TOKEN', 'PYPI_TOKEN', 'GITHUB_TOKEN']
        available_auth = [var for var in auth_vars if os.getenv(var)]

        if not available_auth:
            issues.append("No authentication tokens found in environment. "
                         "Consider setting GITEA_API_TOKEN, PYPI_TOKEN, or GITHUB_TOKEN "
                         "for package publishing.")

        return issues

    def validate_version_string(self, version_string: str) -> Tuple[bool, List[str]]:
        """Validate a version string for release.

        Args:
            version_string: Version string to validate

        Returns:
            Tuple of (is_valid, list_of_issues)
        """
        issues = []

        if not version_string:
            issues.append("Version string cannot be empty")
            return False, issues

        # Check basic format
        if not version_string.replace('.', '').replace('-', '').replace('+', '').replace('a', '').replace('b', '').replace('rc', '').isalnum():
            issues.append("Version string contains invalid characters")

        # Check for development markers in release
        dev_markers = ['dev', '.dev', '+dev']
        if any(marker in version_string.lower() for marker in dev_markers):
            issues.append("Development versions should not be released")

        # Check for reasonable version format (semantic versioning)
        try:
            from packaging import version
            version.Version(version_string)
        except Exception:
            issues.append("Version string is not valid according to PEP 440")

        # Check if version already exists as git tag
        tag_name = version_string if version_string.startswith('v') else f'v{version_string}'
        if self.git_manager.tag_exists(tag_name):
            issues.append(f"Git tag {tag_name} already exists")

        return len(issues) == 0, issues

    def _validate_changelog(self) -> List[str]:
        """Validate CHANGELOG.md using changelog schema.

        Returns:
            List of CHANGELOG-related issues
        """
        issues = []
        changelog_path = self.project_root / 'CHANGELOG.md'

        # Check if CHANGELOG exists
        if not changelog_path.exists():
            issues.append("Missing CHANGELOG.md file")
            return issues

        # Check if changelog schema exists
        schema_path = self.project_root / 'markitect' / 'schemas' / 'changelog-schema-v1.0.md'
        if not schema_path.exists():
            # Schema doesn't exist, skip validation
            return issues

        # Validate CHANGELOG with schema using markitect validate command
        try:
            result = subprocess.run(
                [
                    'markitect', 'validate', str(changelog_path),
                    '--schema', str(schema_path),
                    '--semantic'
                ],
                capture_output=True,
                text=True,
                cwd=self.project_root
            )

            if result.returncode != 0:
                issues.append("CHANGELOG.md validation failed against schema")
                # Parse output for specific errors
                if 'Unreleased section' in result.stdout:
                    issues.append("  - Missing [Unreleased] section in CHANGELOG")
                if 'version format' in result.stdout.lower():
                    issues.append("  - Invalid version format in CHANGELOG")

        except FileNotFoundError:
            # markitect command not available
            issues.append("Cannot validate CHANGELOG (markitect command not found)")
        except Exception as e:
            issues.append(f"Error validating CHANGELOG: {e}")

        return issues

    def validate_changelog_version(self, version: str) -> Tuple[bool, List[str]]:
        """Validate that CHANGELOG has section for specified version.

        Args:
            version: Version to check (e.g., "0.10.0")

        Returns:
            Tuple of (is_valid, list_of_issues)
        """
        issues = []
        changelog_path = self.project_root / 'CHANGELOG.md'

        if not changelog_path.exists():
            issues.append("CHANGELOG.md not found")
            return False, issues

        try:
            content = changelog_path.read_text()

            # Check for version section
            version_header = f"## [{version}]"
            if version_header not in content:
                issues.append(f"CHANGELOG missing section for version {version}")

            # Check for Unreleased section
            if "## [Unreleased]" not in content:
                issues.append("CHANGELOG missing [Unreleased] section")

            # Check if version section has a date
            import re
            date_pattern = rf"## \[{re.escape(version)}\] - \d{{4}}-\d{{2}}-\d{{2}}"
            if not re.search(date_pattern, content):
                issues.append(f"Version {version} section missing date or has invalid date format")

        except Exception as e:
            issues.append(f"Error reading CHANGELOG: {e}")

        return len(issues) == 0, issues

    def check_version_tag_consistency(self, version: str) -> Tuple[bool, List[str]]:
        """Check consistency between CHANGELOG version and git tags.

        Args:
            version: Version to check (e.g., "0.10.0")

        Returns:
            Tuple of (is_consistent, list_of_issues)
        """
        issues = []

        # Check CHANGELOG has the version
        changelog_valid, changelog_issues = self.validate_changelog_version(version)
        if not changelog_valid:
            issues.extend(changelog_issues)

        # Check git tag exists
        tag_name = version if version.startswith('v') else f'v{version}'
        if not self.git_manager.tag_exists(tag_name):
            issues.append(f"Git tag {tag_name} doesn't exist for version in CHANGELOG")

        return len(issues) == 0, issues

    def get_validation_summary(self) -> dict:
        """Get a comprehensive validation summary.

        Returns:
            Dictionary with validation results
        """
        is_valid, issues = self.validate_release_state()

        return {
            'is_valid': is_valid,
            'issues': issues,
            'git_status': self.git_manager.get_repository_status(),
            'recommendations': self._get_recommendations(issues)
        }

    def _get_recommendations(self, issues: List[str]) -> List[str]:
        """Get recommendations based on validation issues.

        Args:
            issues: List of validation issues

        Returns:
            List of recommendations
        """
        recommendations = []

        if any('uncommitted changes' in issue for issue in issues):
            recommendations.append("Commit or stash your changes before releasing")

        if any('not on main branch' in issue for issue in issues):
            recommendations.append("Switch to main branch: git checkout main")

        if any('setuptools-scm' in issue for issue in issues):
            recommendations.append("Configure setuptools-scm in pyproject.toml")

        if any('authentication' in issue.lower() for issue in issues):
            recommendations.append("Set up authentication tokens for package publishing")

        if any('CHANGELOG' in issue for issue in issues):
            recommendations.append("Fix CHANGELOG.md format and ensure [Unreleased] section exists")
            recommendations.append("Validate with: markitect validate CHANGELOG.md --schema changelog-schema-v1.0.md --semantic")

        if not issues:
            recommendations.append("Repository is ready for release!")

        return recommendations