#!/usr/bin/env python3
"""
MarkiTect Installer

This script provides an easy way to install MarkiTect and make it available
system-wide. It handles virtual environment creation, dependency installation,
and creates symbolic links to make the commands available from anywhere.

Usage:
    python install.py [options]

Options:
    --prefix PATH       Installation prefix (default: ~/.local)
    --system           Install system-wide (requires sudo, uses /usr/local)
    --venv-dir PATH    Custom virtual environment directory
    --no-symlinks      Don't create symbolic links (manual PATH setup required)
    --force            Force reinstallation over existing installation
    --dev              Install in development mode with test dependencies
    --check            Check if MarkiTect is already installed
    --uninstall        Uninstall MarkiTect
    --help             Show this help message
"""

import os
import sys
import subprocess
import shutil
import argparse
from pathlib import Path
import tempfile


class MarkiTectInstaller:
    """MarkiTect installation manager."""

    def __init__(self, prefix=None, system=False, venv_dir=None, force=False, dev=False):
        self.system = system
        self.force = force
        self.dev = dev

        # Determine installation paths
        if system:
            self.prefix = Path("/usr/local")
            self.bin_dir = self.prefix / "bin"
            self.venv_dir = Path(venv_dir) if venv_dir else self.prefix / "lib" / "markitect"
        else:
            self.prefix = Path(prefix) if prefix else Path.home() / ".local"
            self.bin_dir = self.prefix / "bin"
            self.venv_dir = Path(venv_dir) if venv_dir else self.prefix / "lib" / "markitect"

        self.project_dir = Path(__file__).parent.absolute()

    def check_requirements(self):
        """Check system requirements."""
        print("🔍 Checking system requirements...")

        # Check Python version
        if sys.version_info < (3, 8):
            print("❌ Python 3.8 or higher is required")
            sys.exit(1)
        print(f"✅ Python {sys.version.split()[0]} found")

        # Check if pip is available
        try:
            subprocess.run([sys.executable, "-m", "pip", "--version"],
                         check=True, capture_output=True)
            print("✅ pip is available")
        except subprocess.CalledProcessError:
            print("❌ pip is not available. Please install pip first.")
            sys.exit(1)

        # Check if git is available (optional)
        try:
            subprocess.run(["git", "--version"], check=True, capture_output=True)
            print("✅ git is available")
        except (subprocess.CalledProcessError, FileNotFoundError):
            print("⚠️  git is not available (optional for version info)")

    def check_existing_installation(self):
        """Check if MarkiTect is already installed."""
        # Check for existing venv
        if self.venv_dir.exists():
            print(f"📁 Existing installation found at {self.venv_dir}")
            return True

        # Check for existing binaries
        markitect_bin = self.bin_dir / "markitect"
        if markitect_bin.exists():
            print(f"📁 Existing binary found at {markitect_bin}")
            return True

        return False

    def create_directories(self):
        """Create necessary directories."""
        print(f"📁 Creating directories...")

        if self.system and not os.access(self.prefix, os.W_OK):
            print("❌ System installation requires sudo privileges")
            print("    Please run with sudo or choose a different installation prefix")
            sys.exit(1)

        self.prefix.mkdir(parents=True, exist_ok=True)
        self.bin_dir.mkdir(parents=True, exist_ok=True)
        print(f"✅ Created directories in {self.prefix}")

    def create_virtual_environment(self):
        """Create and set up virtual environment."""
        print(f"🐍 Creating virtual environment at {self.venv_dir}")

        if self.venv_dir.exists():
            if self.force:
                print(f"🗑️  Removing existing installation...")
                shutil.rmtree(self.venv_dir)
            else:
                print("❌ Virtual environment already exists. Use --force to overwrite.")
                sys.exit(1)

        # Create virtual environment
        subprocess.run([
            sys.executable, "-m", "venv", str(self.venv_dir)
        ], check=True)

        # Get paths to venv executables
        if sys.platform == "win32":
            venv_python = self.venv_dir / "Scripts" / "python.exe"
            venv_pip = self.venv_dir / "Scripts" / "pip.exe"
        else:
            venv_python = self.venv_dir / "bin" / "python"
            venv_pip = self.venv_dir / "bin" / "pip"

        # Upgrade pip
        print("📦 Upgrading pip...")
        subprocess.run([
            str(venv_pip), "install", "--upgrade", "pip", "setuptools", "wheel"
        ], check=True)

        return venv_python, venv_pip

    def install_markitect(self, venv_python, venv_pip):
        """Install MarkiTect in the virtual environment."""
        print("📦 Installing MarkiTect...")

        install_cmd = [str(venv_pip), "install"]

        if self.dev:
            print("🛠️  Installing in development mode with test dependencies...")
            # Install in editable mode from current directory
            install_cmd.extend(["-e", str(self.project_dir)])

            # Install test dependencies
            subprocess.run(install_cmd, check=True)
            subprocess.run([
                str(venv_pip), "install", "pytest", "pytest-cov", "black", "flake8", "mypy"
            ], check=True)
        else:
            # Install from current directory
            install_cmd.append(str(self.project_dir))
            subprocess.run(install_cmd, check=True)

        print("✅ MarkiTect installed successfully")

    def create_symlinks(self, no_symlinks=False):
        """Create symbolic links for global access."""
        if no_symlinks:
            print("⚠️  Skipping symbolic link creation")
            self.show_manual_setup()
            return

        print("🔗 Creating symbolic links...")

        # Get venv bin directory
        if sys.platform == "win32":
            venv_bin = self.venv_dir / "Scripts"
            exe_suffix = ".exe"
        else:
            venv_bin = self.venv_dir / "bin"
            exe_suffix = ""

        # Commands to link
        commands = ["markitect", "tddai", "issue"]

        for cmd in commands:
            src = venv_bin / f"{cmd}{exe_suffix}"
            dst = self.bin_dir / cmd

            if src.exists():
                # Remove existing symlink/file
                if dst.exists() or dst.is_symlink():
                    dst.unlink()

                # Create symlink
                try:
                    dst.symlink_to(src)
                    print(f"✅ Created symlink: {dst} -> {src}")
                except OSError:
                    # Fallback: create wrapper script
                    self.create_wrapper_script(dst, src)
            else:
                print(f"⚠️  Command {cmd} not found in virtual environment")

    def create_wrapper_script(self, dst, src):
        """Create a wrapper script when symlinks aren't available."""
        print(f"🔧 Creating wrapper script: {dst}")

        if sys.platform == "win32":
            # Windows batch file
            dst = dst.with_suffix(".bat")
            content = f'@echo off\n"{src}" %*\n'
        else:
            # Unix shell script
            content = f'#!/bin/bash\nexec "{src}" "$@"\n'

        dst.write_text(content)
        if sys.platform != "win32":
            os.chmod(dst, 0o755)

    def show_manual_setup(self):
        """Show manual PATH setup instructions."""
        print("\n📋 Manual Setup Instructions:")
        print("=" * 50)
        print(f"Add the following to your PATH environment variable:")
        print(f"  {self.venv_dir / 'bin'}")
        print()
        print("For bash/zsh, add this line to ~/.bashrc or ~/.zshrc:")
        print(f'  export PATH="{self.venv_dir / "bin"}:$PATH"')
        print()

    def test_installation(self):
        """Test the installation."""
        print("🧪 Testing installation...")

        # Test markitect command
        try:
            markitect_bin = self.bin_dir / "markitect"
            if not markitect_bin.exists():
                # Try direct venv path
                if sys.platform == "win32":
                    markitect_bin = self.venv_dir / "Scripts" / "markitect.exe"
                else:
                    markitect_bin = self.venv_dir / "bin" / "markitect"

            result = subprocess.run([
                str(markitect_bin), "version", "--short"
            ], capture_output=True, text=True, check=True)

            version = result.stdout.strip()
            print(f"✅ MarkiTect installed successfully - version {version}")
            return True
        except (subprocess.CalledProcessError, FileNotFoundError) as e:
            print(f"❌ Installation test failed: {e}")
            return False

    def uninstall(self):
        """Uninstall MarkiTect."""
        print("🗑️  Uninstalling MarkiTect...")

        removed_something = False

        # Remove virtual environment
        if self.venv_dir.exists():
            print(f"🗑️  Removing virtual environment: {self.venv_dir}")
            shutil.rmtree(self.venv_dir)
            removed_something = True

        # Remove symlinks
        commands = ["markitect", "tddai", "issue"]
        for cmd in commands:
            for bin_path in [self.bin_dir / cmd, self.bin_dir / f"{cmd}.bat"]:
                if bin_path.exists() or bin_path.is_symlink():
                    print(f"🗑️  Removing: {bin_path}")
                    bin_path.unlink()
                    removed_something = True

        if removed_something:
            print("✅ MarkiTect uninstalled successfully")
        else:
            print("⚠️  No MarkiTect installation found")

    def install(self, no_symlinks=False):
        """Perform the complete installation."""
        print("🚀 Installing MarkiTect")
        print("=" * 50)

        self.check_requirements()

        if not self.force and self.check_existing_installation():
            print("❌ MarkiTect is already installed. Use --force to reinstall.")
            sys.exit(1)

        self.create_directories()
        venv_python, venv_pip = self.create_virtual_environment()
        self.install_markitect(venv_python, venv_pip)
        self.create_symlinks(no_symlinks)

        print()
        if self.test_installation():
            print("🎉 Installation completed successfully!")
            print()
            print("You can now use MarkiTect from anywhere:")
            print("  markitect --help")
            print("  markitect version")
            print("  tddai --help")
            print("  issue --help")
        else:
            print("⚠️  Installation completed but tests failed")
            self.show_manual_setup()

    def check_installation_status(self):
        """Check current installation status."""
        print("🔍 MarkiTect Installation Status")
        print("=" * 50)

        # Check virtual environment
        if self.venv_dir.exists():
            print(f"✅ Virtual environment: {self.venv_dir}")
        else:
            print(f"❌ Virtual environment: Not found at {self.venv_dir}")

        # Check binaries
        commands = ["markitect", "tddai", "issue"]
        for cmd in commands:
            bin_path = self.bin_dir / cmd
            if bin_path.exists():
                print(f"✅ {cmd}: {bin_path}")
            else:
                print(f"❌ {cmd}: Not found at {bin_path}")

        # Try to get version
        try:
            result = subprocess.run([
                "markitect", "version", "--short"
            ], capture_output=True, text=True)
            if result.returncode == 0:
                version = result.stdout.strip()
                print(f"✅ Working installation: version {version}")
            else:
                print("❌ Installation found but not working")
        except FileNotFoundError:
            print("❌ markitect command not available in PATH")


def main():
    parser = argparse.ArgumentParser(
        description="MarkiTect Installer",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=__doc__.split('\n\n')[1]  # Show usage from docstring
    )

    parser.add_argument("--prefix", type=Path,
                       help="Installation prefix (default: ~/.local)")
    parser.add_argument("--system", action="store_true",
                       help="Install system-wide (requires sudo)")
    parser.add_argument("--venv-dir", type=Path,
                       help="Custom virtual environment directory")
    parser.add_argument("--no-symlinks", action="store_true",
                       help="Don't create symbolic links")
    parser.add_argument("--force", action="store_true",
                       help="Force reinstallation")
    parser.add_argument("--dev", action="store_true",
                       help="Install in development mode")
    parser.add_argument("--check", action="store_true",
                       help="Check installation status")
    parser.add_argument("--uninstall", action="store_true",
                       help="Uninstall MarkiTect")

    args = parser.parse_args()

    # Create installer instance
    installer = MarkiTectInstaller(
        prefix=args.prefix,
        system=args.system,
        venv_dir=args.venv_dir,
        force=args.force,
        dev=args.dev
    )

    # Handle different actions
    if args.check:
        installer.check_installation_status()
    elif args.uninstall:
        installer.uninstall()
    else:
        installer.install(no_symlinks=args.no_symlinks)


if __name__ == "__main__":
    main()