#!/usr/bin/env python3
"""
Hotel MCP - Intelligent Release Management

Automated release workflow with version synchronization, changelog generation,
and comprehensive validation for NPM publishing.
"""

import json
import os
import re
import subprocess
import sys
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional, Tuple


def run_command(cmd: str, cwd: Optional[Path] = None) -> Tuple[bool, str, str]:
    """Run a command and return success, stdout, stderr."""
    try:
        result = subprocess.run(
            cmd, shell=True, cwd=cwd, capture_output=True, text=True, timeout=60
        )
        return result.returncode == 0, result.stdout.strip(), result.stderr.strip()
    except subprocess.TimeoutExpired:
        return False, "", "Command timed out"
    except Exception as e:
        return False, "", str(e)


def log(message: str, level: str = "info") -> None:
    """Log a message with color coding."""
    colors = {
        "info": "\033[36m",  # Cyan
        "success": "\033[32m",  # Green
        "warning": "\033[33m",  # Yellow
        "error": "\033[31m",  # Red
        "header": "\033[35m",  # Magenta
        "reset": "\033[0m",  # Reset
    }

    icons = {
        "info": "ℹ️ ",
        "success": "✅ ",
        "warning": "⚠️ ",
        "error": "❌ ",
        "header": "🚀 ",
    }

    color = colors.get(level, colors["info"])
    icon = icons.get(level, "")
    reset = colors["reset"]

    print(f"{color}{icon}{message}{reset}")


def get_current_version() -> Dict[str, str]:
    """Get current versions from package.json and pyproject.toml."""
    versions = {}

    # Get package.json version
    package_file = Path("package.json")
    if package_file.exists():
        with open(package_file) as f:
            package_data = json.load(f)
            versions["package.json"] = package_data.get("version", "unknown")

    # Get pyproject.toml version
    pyproject_file = Path("pyproject.toml")
    if pyproject_file.exists():
        content = pyproject_file.read_text()
        match = re.search(r'version\s*=\s*"([^"]+)"', content)
        if match:
            versions["pyproject.toml"] = match.group(1)

    return versions


def validate_version_format(version: str) -> bool:
    """Validate semantic version format."""
    pattern = r"^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$"
    return bool(re.match(pattern, version))


def increment_version(current: str, bump_type: str) -> str:
    """Increment version based on bump type (major, minor, patch)."""
    parts = current.split(".")
    if len(parts) != 3:
        raise ValueError(f"Invalid version format: {current}")

    major, minor, patch = map(int, parts)

    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}.{patch + 1}"
    else:
        raise ValueError(f"Invalid bump type: {bump_type}")


def update_package_json(new_version: str) -> bool:
    """Update version in package.json."""
    try:
        package_file = Path("package.json")
        with open(package_file) as f:
            data = json.load(f)

        data["version"] = new_version

        with open(package_file, "w") as f:
            json.dump(data, f, indent=2)
            f.write("\n")  # Add trailing newline

        return True
    except Exception as e:
        log(f"Failed to update package.json: {e}", "error")
        return False


def update_pyproject_toml(new_version: str) -> bool:
    """Update version in pyproject.toml."""
    try:
        pyproject_file = Path("pyproject.toml")
        content = pyproject_file.read_text()

        # Replace version line
        new_content = re.sub(
            r'version\s*=\s*"[^"]+"', f'version = "{new_version}"', content
        )

        pyproject_file.write_text(new_content)
        return True
    except Exception as e:
        log(f"Failed to update pyproject.toml: {e}", "error")
        return False


def run_pre_publish_checks() -> bool:
    """Run comprehensive pre-publish validation."""
    log("Running pre-publish checks...", "header")

    checks = [
        ("Python tests", "uv run pytest"),
        ("Distribution tests", "python scripts/test_distribution.py"),
        ("Code formatting", "uv run black --check ."),
        ("Import sorting", "uv run isort --check-only ."),
        ("Linting", "uv run flake8"),
        ("NPM pack test", "npm pack --dry-run"),
    ]

    all_passed = True

    for check_name, command in checks:
        log(f"Running {check_name}...", "info")
        success, stdout, stderr = run_command(command)

        if success:
            log(f"{check_name} passed", "success")
        else:
            log(f"{check_name} failed: {stderr}", "error")
            all_passed = False

    return all_passed


def generate_changelog_entry(version: str, changes: List[str]) -> str:
    """Generate changelog entry for the new version."""
    date = datetime.now().strftime("%Y-%m-%d")

    entry = f"""## [{version}] - {date}

### Added
- NPX distribution with intelligent cross-platform launcher
- Automated environment detection (uv/venv/system Python)
- Post-install guidance and clean uninstallation scripts
- Comprehensive distribution testing suite
- Zero-config Claude Desktop integration

### Enhanced
- Beautiful UX with colored output and progress indicators
- Intelligent error handling with troubleshooting guidance
- Cross-platform compatibility (macOS, Linux, Windows)
- Documentation updated with NPX installation methods

### Technical
- 99 total tests (92 core + 7 distribution)
- 22 MCP tools + 6 intelligent resources
- DRY architecture with monitoring and caching
- Production-ready with comprehensive documentation

"""

    return entry


def update_changelog(version: str, changes: List[str]) -> bool:
    """Update CHANGELOG.md with new version entry."""
    try:
        changelog_file = Path("CHANGELOG.md")

        # Create changelog if it doesn't exist
        if not changelog_file.exists():
            header = """# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

"""
            changelog_file.write_text(header)

        # Read existing content
        content = changelog_file.read_text()

        # Generate new entry
        new_entry = generate_changelog_entry(version, changes)

        # Insert new entry after header
        lines = content.split("\n")
        header_end = 0
        for i, line in enumerate(lines):
            if line.startswith("## [") or line.startswith("## v"):
                header_end = i
                break
        else:
            # No existing entries, add after header
            header_end = len(lines)

        # Insert new entry
        lines.insert(header_end, new_entry)

        # Write back
        changelog_file.write_text("\n".join(lines))

        return True
    except Exception as e:
        log(f"Failed to update changelog: {e}", "error")
        return False


def create_git_tag(version: str) -> bool:
    """Create and push git tag for the release."""
    try:
        # Create tag
        success, _, stderr = run_command(
            f"git tag -a v{version} -m 'Release v{version}'"
        )
        if not success:
            log(f"Failed to create git tag: {stderr}", "error")
            return False

        # Push tag
        success, _, stderr = run_command("git push origin --tags")
        if not success:
            log(f"Failed to push git tag: {stderr}", "error")
            return False

        return True
    except Exception as e:
        log(f"Failed to create git tag: {e}", "error")
        return False


def publish_to_npm(dry_run: bool = False) -> bool:
    """Publish package to NPM."""
    try:
        cmd = "npm publish"
        if dry_run:
            cmd += " --dry-run"

        success, stdout, stderr = run_command(cmd)

        if success:
            if dry_run:
                log("NPM publish dry-run successful", "success")
            else:
                log("Successfully published to NPM!", "success")
            return True
        else:
            log(f"NPM publish failed: {stderr}", "error")
            return False
    except Exception as e:
        log(f"Failed to publish to NPM: {e}", "error")
        return False


def verify_publication(version: str) -> bool:
    """Verify that the package was published successfully."""
    try:
        # Wait a moment for NPM to update
        import time

        time.sleep(5)

        # Check if package is available
        success, stdout, stderr = run_command(f"npm view hotel-mcp@{version} version")

        if success and version in stdout:
            log(f"Package hotel-mcp@{version} is available on NPM", "success")
            return True
        else:
            log(f"Package verification failed: {stderr}", "warning")
            return False
    except Exception as e:
        log(f"Failed to verify publication: {e}", "error")
        return False


def main():
    """Main release workflow."""
    log("Hotel MCP - Intelligent Release Management", "header")
    log("=" * 50, "info")

    # Parse arguments
    import argparse

    parser = argparse.ArgumentParser(description="Release management for Hotel MCP")
    parser.add_argument(
        "bump_type", choices=["major", "minor", "patch"], help="Version bump type"
    )
    parser.add_argument(
        "--dry-run", action="store_true", help="Run without making changes"
    )
    parser.add_argument(
        "--skip-checks", action="store_true", help="Skip pre-publish checks"
    )

    args = parser.parse_args()

    # Get current versions
    versions = get_current_version()
    log(f"Current versions: {versions}", "info")

    # Validate version consistency
    if len(set(versions.values())) > 1:
        log("Version mismatch detected between files!", "error")
        return 1

    current_version = list(versions.values())[0]
    new_version = increment_version(current_version, args.bump_type)

    log(f"Bumping version: {current_version} → {new_version}", "info")

    if args.dry_run:
        log("DRY RUN MODE - No changes will be made", "warning")
        return 0

    # Run pre-publish checks
    if not args.skip_checks:
        if not run_pre_publish_checks():
            log("Pre-publish checks failed. Aborting release.", "error")
            return 1

    # Update version files
    log("Updating version files...", "info")
    if not update_package_json(new_version):
        return 1
    if not update_pyproject_toml(new_version):
        return 1

    # Update changelog
    log("Updating changelog...", "info")
    changes = [
        "NPX distribution ready",
        "Cross-platform launcher",
        "Production optimizations",
    ]
    if not update_changelog(new_version, changes):
        return 1

    # Commit changes
    log("Committing changes...", "info")
    success, _, stderr = run_command(
        f"git add . && git commit -m 'chore: bump version to {new_version}'"
    )
    if not success:
        log(f"Failed to commit changes: {stderr}", "error")
        return 1

    # Create git tag
    log("Creating git tag...", "info")
    if not create_git_tag(new_version):
        return 1

    # Publish to NPM
    log("Publishing to NPM...", "info")
    if not publish_to_npm():
        return 1

    # Verify publication
    log("Verifying publication...", "info")
    if not verify_publication(new_version):
        log(
            "Publication verification failed, but package may still be available",
            "warning",
        )

    log(f"🎉 Successfully released hotel-mcp v{new_version}!", "success")
    log(f"📦 Install with: npx hotel-mcp@{new_version}", "info")

    return 0


if __name__ == "__main__":
    sys.exit(main())
