"""
Package Commands - Package lifecycle and publishing for the LightWave CLI.

SST Reference: packages/lightwave-core/lightwave/schema/definitions/cli.yaml
Domain: package (runtime: python)
"""

from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from typing import Any

from lightwave.cli.runner import CommandError

# Package states for SST-driven CI/CD
PACKAGE_STATES = [
    "dev",  # Local development only
    "git_clone",  # Install via git clone in CI
    "test_pypi",  # Published to test.pypi.org
    "pypi",  # Published to pypi.org (production)
]


def find_packages_yaml() -> Path:
    """Find the packages.yaml SST definition."""
    search_paths = [
        Path.cwd() / "packages" / "lightwave-core" / "lightwave" / "schema" / "definitions" / "packages.yaml",
        Path.cwd() / "lightwave" / "schema" / "definitions" / "packages.yaml",
        Path(__file__).parent.parent / "schema" / "definitions" / "packages.yaml",
    ]

    for path in search_paths:
        if path.exists():
            return path

    raise CommandError("Could not find packages.yaml definition")


def package_status(
    args: list[str],
    *,
    json_output: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Show publication state, version, and readiness.

    SST: domains.package.commands[name=status]
    args: [package-name]
    flags: [--json]
    """
    if not args:
        raise CommandError("Package name required. Example: lw package status lightwave-core")

    package_name = args[0]

    if verbose:
        print(f"Checking status for: {package_name}")

    # Try to find packages.yaml and load state
    try:
        import yaml

        packages_yaml = find_packages_yaml()
        with open(packages_yaml) as f:
            packages_data = yaml.safe_load(f)

        package_info = packages_data.get("packages", {}).get(package_name)
        if not package_info:
            raise CommandError(f"Unknown package: {package_name}")

        status = {
            "name": package_name,
            "state": package_info.get("state", "unknown"),
            "version": package_info.get("version", "0.0.0"),
            "pypi_name": package_info.get("pypi_name", package_name),
            "ready_for_publish": package_info.get("ready_for_publish", False),
            "dependencies": package_info.get("dependencies", []),
        }

    except FileNotFoundError:
        # Fallback - check package directly
        status = {
            "name": package_name,
            "state": "unknown",
            "version": _get_package_version(package_name),
            "note": "packages.yaml not found - using fallback detection",
        }

    if json_output:
        return status

    print(f"\nPackage: {status['name']}")
    print("=" * 50)
    print(f"State: {status['state']}")
    print(f"Version: {status['version']}")
    if "pypi_name" in status:
        print(f"PyPI Name: {status['pypi_name']}")
    if "ready_for_publish" in status:
        print(f"Ready for publish: {status['ready_for_publish']}")
    if status.get("note"):
        print(f"Note: {status['note']}")

    return None


def package_validate(
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Check if package meets publication criteria.

    SST: domains.package.commands[name=validate]
    args: [package-name]
    flags: [--fix, --skip-tests]
    """
    if not args:
        raise CommandError("Package name required. Example: lw package validate lightwave-core")

    package_name = args[0]

    # Parse flags
    fix = "--fix" in args
    skip_tests = "--skip-tests" in args

    if verbose:
        print(f"Validating package: {package_name}")
        print(f"Fix mode: {fix}")
        print(f"Skip tests: {skip_tests}")

    if dry_run:
        if json_output:
            return {"success": True, "dry_run": True, "package": package_name}
        print(f"[dry-run] Would validate package: {package_name}")
        return None

    # Run validation checks
    checks = {
        "pyproject_toml": _check_pyproject_toml(package_name),
        "version_tag": _check_version_tag(package_name),
        "changelog": _check_changelog(package_name),
        "dependencies": _check_dependencies(package_name),
    }

    if not skip_tests:
        checks["tests_pass"] = _check_tests(package_name)

    all_passed = all(c["passed"] for c in checks.values())

    result = {
        "package": package_name,
        "success": all_passed,
        "checks": checks,
    }

    if json_output:
        return result

    print(f"\nValidation: {package_name}")
    print("=" * 50)
    for check_name, check_result in checks.items():
        status = "✓" if check_result["passed"] else "✗"
        print(f"  {status} {check_name}: {check_result.get('message', 'OK')}")

    if not all_passed:
        raise CommandError("Package validation failed")

    print("\nAll checks passed!")
    return None


def package_ci_mode(
    args: list[str],
    *,
    json_output: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Output CI install method (git_clone|pypi).

    SST: domains.package.commands[name=ci-mode]
    args: [package-name]
    """
    if not args:
        raise CommandError("Package name required. Example: lw package ci-mode lightwave-core")

    package_name = args[0]

    if verbose:
        print(f"Checking CI mode for: {package_name}")

    # Determine install method from packages.yaml
    try:
        import yaml

        packages_yaml = find_packages_yaml()
        with open(packages_yaml) as f:
            packages_data = yaml.safe_load(f)

        package_info = packages_data.get("packages", {}).get(package_name)
        if not package_info:
            raise CommandError(f"Unknown package: {package_name}")

        state = package_info.get("state", "dev")

        # Map state to CI install method
        if state in ("pypi", "test_pypi"):
            ci_mode = "pypi"
        else:
            ci_mode = "git_clone"

    except FileNotFoundError:
        # Default to git_clone if no packages.yaml
        ci_mode = "git_clone"

    result = {
        "package": package_name,
        "ci_mode": ci_mode,
    }

    if json_output:
        return result

    # Just output the mode for CI scripts to parse
    print(ci_mode)
    return None


def package_publish(
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Build and publish package to PyPI.

    SST: domains.package.commands[name=publish]
    args: [package-name]
    flags: [--dry-run, --test-pypi]
    """
    if not args:
        raise CommandError("Package name required. Example: lw package publish lightwave-core")

    package_name = args[0]
    test_pypi = "--test-pypi" in args

    if verbose:
        print(f"Publishing package: {package_name}")
        print(f"Target: {'test.pypi.org' if test_pypi else 'pypi.org'}")

    if dry_run:
        if json_output:
            return {
                "success": True,
                "dry_run": True,
                "package": package_name,
                "target": "test_pypi" if test_pypi else "pypi",
            }
        target = "test.pypi.org" if test_pypi else "pypi.org"
        print(f"[dry-run] Would publish {package_name} to {target}")
        return None

    # Build the package
    if verbose:
        print("Building package...")

    import subprocess

    # Find package directory
    package_dir = _find_package_dir(package_name)

    # Build with uv/pip
    build_cmd = ["uv", "build", "--no-isolation"]
    build_result = subprocess.run(build_cmd, cwd=package_dir, capture_output=True)

    if build_result.returncode != 0:
        raise CommandError(f"Build failed: {build_result.stderr.decode()}")

    # Publish
    if verbose:
        print(f"Publishing to {'test.pypi.org' if test_pypi else 'pypi.org'}...")

    publish_cmd = ["uv", "publish"]
    if test_pypi:
        publish_cmd.extend(["--index-url", "https://test.pypi.org/simple/"])

    publish_result = subprocess.run(publish_cmd, cwd=package_dir, capture_output=True)

    if publish_result.returncode != 0:
        raise CommandError(f"Publish failed: {publish_result.stderr.decode()}")

    result = {
        "success": True,
        "package": package_name,
        "target": "test_pypi" if test_pypi else "pypi",
        "message": f"Published {package_name} successfully",
    }

    if json_output:
        return result

    print(f"Successfully published {package_name}!")
    return None


def package_update_state(
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Update publication state in packages.yaml.

    SST: domains.package.commands[name=update-state]
    args: [package-name]
    flags: [--state]
    """
    if not args:
        raise CommandError("Package name required. Example: lw package update-state lightwave-core --state pypi")

    package_name = args[0]

    # Parse state flag
    new_state = None
    for i, arg in enumerate(args[1:]):
        if arg == "--state" and i + 1 < len(args[1:]):
            new_state = args[1:][i + 1]

    if not new_state:
        raise CommandError("State required. Use --state <state>. Valid states: " + ", ".join(PACKAGE_STATES))

    if new_state not in PACKAGE_STATES:
        raise CommandError(f"Invalid state: {new_state}. Valid states: {', '.join(PACKAGE_STATES)}")

    if verbose:
        print(f"Updating {package_name} state to: {new_state}")

    if dry_run:
        if json_output:
            return {
                "success": True,
                "dry_run": True,
                "package": package_name,
                "new_state": new_state,
            }
        print(f"[dry-run] Would update {package_name} state to: {new_state}")
        return None

    # Update packages.yaml
    try:
        import yaml

        packages_yaml = find_packages_yaml()
        with open(packages_yaml) as f:
            packages_data = yaml.safe_load(f)

        if package_name not in packages_data.get("packages", {}):
            raise CommandError(f"Unknown package: {package_name}")

        old_state = packages_data["packages"][package_name].get("state", "unknown")
        packages_data["packages"][package_name]["state"] = new_state

        with open(packages_yaml, "w") as f:
            yaml.dump(packages_data, f, default_flow_style=False, sort_keys=False)

        result = {
            "success": True,
            "package": package_name,
            "old_state": old_state,
            "new_state": new_state,
        }

        if json_output:
            return result

        print(f"Updated {package_name}: {old_state} → {new_state}")

    except FileNotFoundError:
        raise CommandError("packages.yaml not found")

    return None


def package_align(
    args: list[str],
    *,
    json_output: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Validate consumer/provider version alignment.

    SST: domains.package.commands[name=align]
    args: []
    """
    if verbose:
        print("Checking package version alignment...")

    # Load packages.yaml to check dependencies
    try:
        import yaml

        packages_yaml = find_packages_yaml()
        with open(packages_yaml) as f:
            packages_data = yaml.safe_load(f)

        packages = packages_data.get("packages", {})
        misalignments = []

        for pkg_name, pkg_info in packages.items():
            deps = pkg_info.get("dependencies", [])
            for dep in deps:
                if dep in packages:
                    # Check if consumer version is compatible with provider
                    provider_version = packages[dep].get("version", "0.0.0")
                    consumer_constraint = pkg_info.get("dependency_versions", {}).get(dep, "*")

                    if not _version_compatible(provider_version, consumer_constraint):
                        misalignments.append(
                            {
                                "consumer": pkg_name,
                                "provider": dep,
                                "provider_version": provider_version,
                                "consumer_constraint": consumer_constraint,
                            }
                        )

        result = {
            "success": len(misalignments) == 0,
            "total_packages": len(packages),
            "misalignments": misalignments,
        }

        if json_output:
            return result

        print("\nPackage Alignment Report")
        print("=" * 50)
        print(f"Total packages: {result['total_packages']}")

        if misalignments:
            print(f"\nMisalignments found: {len(misalignments)}")
            for m in misalignments:
                print(f"  {m['consumer']} requires {m['provider']} {m['consumer_constraint']}")
                print(f"    but {m['provider']} is at {m['provider_version']}")
            raise CommandError("Package alignment check failed")

        print("\nAll packages aligned!")

    except FileNotFoundError:
        if json_output:
            return {"success": False, "error": "packages.yaml not found"}
        print("packages.yaml not found - cannot check alignment")

    return None


# =============================================================================
# Helper Functions
# =============================================================================


def _get_package_version(package_name: str) -> str:
    """Get package version from pyproject.toml or __version__."""
    try:
        # Try to import and get version
        import importlib.metadata

        return importlib.metadata.version(package_name.replace("-", "_"))
    except Exception:
        return "unknown"


def _find_package_dir(package_name: str) -> Path:
    """Find the directory for a package."""
    search_paths = [
        Path.cwd() / "packages" / package_name,
        Path.cwd() / package_name,
    ]

    for path in search_paths:
        if path.exists() and (path / "pyproject.toml").exists():
            return path

    raise CommandError(f"Could not find package directory for: {package_name}")


def _check_pyproject_toml(package_name: str) -> dict[str, Any]:
    """Check if pyproject.toml exists and is valid."""
    try:
        package_dir = _find_package_dir(package_name)
        pyproject = package_dir / "pyproject.toml"

        if not pyproject.exists():
            return {"passed": False, "message": "pyproject.toml not found"}

        # Basic validation
        import tomllib

        with open(pyproject, "rb") as f:
            data = tomllib.load(f)

        if "project" not in data:
            return {"passed": False, "message": "Missing [project] section"}

        return {"passed": True, "message": "Valid pyproject.toml"}

    except Exception as e:
        return {"passed": False, "message": str(e)}


def _check_version_tag(package_name: str) -> dict[str, Any]:
    """Check if version has a git tag."""
    # TODO: Implement git tag check
    return {"passed": True, "message": "Version tag check not yet implemented"}


def _check_changelog(package_name: str) -> dict[str, Any]:
    """Check if CHANGELOG exists."""
    try:
        package_dir = _find_package_dir(package_name)
        changelog_files = ["CHANGELOG.md", "CHANGELOG.rst", "HISTORY.md"]

        for cf in changelog_files:
            if (package_dir / cf).exists():
                return {"passed": True, "message": f"Found {cf}"}

        return {"passed": False, "message": "No changelog found"}

    except Exception as e:
        return {"passed": False, "message": str(e)}


def _check_dependencies(package_name: str) -> dict[str, Any]:
    """Check if dependencies are properly specified."""
    try:
        package_dir = _find_package_dir(package_name)
        pyproject = package_dir / "pyproject.toml"

        import tomllib

        with open(pyproject, "rb") as f:
            data = tomllib.load(f)

        deps = data.get("project", {}).get("dependencies", [])
        return {"passed": True, "message": f"{len(deps)} dependencies specified"}

    except Exception as e:
        return {"passed": False, "message": str(e)}


def _check_tests(package_name: str) -> dict[str, Any]:
    """Run tests for the package."""
    import subprocess

    try:
        package_dir = _find_package_dir(package_name)
        result = subprocess.run(
            ["pytest", "--collect-only", "-q"],
            cwd=package_dir,
            capture_output=True,
            timeout=30,
        )

        if result.returncode == 0:
            return {"passed": True, "message": "Tests collected successfully"}
        else:
            return {"passed": False, "message": "Test collection failed"}

    except subprocess.TimeoutExpired:
        return {"passed": False, "message": "Test collection timed out"}
    except Exception as e:
        return {"passed": False, "message": str(e)}


def _version_compatible(version: str, constraint: str) -> bool:
    """Check if version satisfies constraint (simplified)."""
    if constraint == "*":
        return True

    # Basic version comparison - would use packaging library in production
    try:
        from packaging import version as pkg_version
        from packaging.specifiers import SpecifierSet

        spec = SpecifierSet(constraint)
        return pkg_version.parse(version) in spec
    except ImportError:
        # Fallback - assume compatible if we can't check
        return True
    except Exception:
        return True
