"""
RAGSentinel CLI - Command Line Interface.

This module provides the command-line interface for RAGSentinel.

Commands:
    init     - Initialize a new project with configuration templates
    run      - Run RAG evaluation (auto-starts MLflow server)
    validate - Validate configuration files

Example:
    $ rag-sentinel init
    $ rag-sentinel run
    $ rag-sentinel validate
"""

import sys
import shutil
import socket
import subprocess
import time
import argparse
import configparser
from pathlib import Path
from urllib.parse import urlparse


# =============================================================================
# Constants
# =============================================================================

TEMPLATES_DIR = Path(__file__).parent / "templates"


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

def get_mlflow_host_port():
    """
    Read MLflow tracking_uri from config.ini and parse host and port.

    Returns:
        tuple: (host, port) - defaults to ("127.0.0.1", 5001) if not configured
    """
    default_host = "127.0.0.1"
    default_port = 5000

    config_path = Path("config.ini")
    if not config_path.exists():
        return default_host, default_port

    try:
        ini = configparser.ConfigParser()
        ini.read(config_path)

        tracking_uri = ini.get("mlflow", "tracking_uri", fallback=None)
        if not tracking_uri:
            return default_host, default_port

        # Parse the URI (e.g., "http://192.168.1.100:5000")
        parsed = urlparse(tracking_uri)

        host = parsed.hostname or default_host
        port = parsed.port or default_port

        return host, port
    except Exception:
        return default_host, default_port


def is_port_in_use(host, port):
    """
    Check if a port is already in use.

    Args:
        host: The hostname to check (e.g., "127.0.0.1")
        port: The port number to check

    Returns:
        bool: True if port is in use, False otherwise
    """
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        return s.connect_ex((host, port)) == 0


def start_mlflow_server(host, port):
    """
    Start MLflow tracking server as a background process.

    If the server is already running on the specified port, this function
    will skip starting a new instance.

    Args:
        host: The hostname to bind the server to
        port: The port number for the server

    Returns:
        subprocess.Popen or None: The server process, or None if already running
    """
    if is_port_in_use(host, port):
        print(f"✓ MLflow server already running at http://{host}:{port}")
        return None

    print(f"🚀 Starting MLflow server at http://{host}:{port}...")

    # Start MLflow server as a detached background process
    process = subprocess.Popen(
        [sys.executable, "-m", "mlflow", "server", "--host", host, "--port", str(port)],
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if sys.platform == "win32" else 0
    )

    # Wait for server to become available (max 10 seconds)
    for _ in range(10):
        time.sleep(1)
        if is_port_in_use(host, port):
            print(f"✓ MLflow server started at http://{host}:{port}")
            return process

    print("⚠ MLflow server may not have started properly")
    return process


# =============================================================================
# CLI Commands
# =============================================================================

def cmd_init(args):
    """
    Initialize a new project with configuration templates.

    Copies the following template files to the current directory:
        - .env           : LLM and Embeddings API configuration
        - config.ini     : Application settings (URLs, auth, dataset path)
        - rag_eval_config.yaml : Master configuration with placeholders

    Args:
        args: Parsed command-line arguments (includes --force flag)
    """
    print("=" * 50)
    print("RAGSentinel - Project Initialization")
    print("=" * 50)

    # Template files to copy: (source_name, destination_name)
    files_to_copy = [
        (".env.template", ".env"),
        ("config.ini.template", "config.ini"),
        ("rag_eval_config.yaml", "rag_eval_config.yaml"),
    ]

    for src_name, dest_name in files_to_copy:
        src_path = TEMPLATES_DIR / src_name
        dest_path = Path.cwd() / dest_name

        if dest_path.exists() and not args.force:
            print(f"⚠ {dest_name} already exists (use --force to overwrite)")
        else:
            if src_path.exists():
                shutil.copy(src_path, dest_path)
                print(f"✓ Created {dest_name}")
            else:
                print(f"✗ Template not found: {src_name}")

    # Display next steps for the user
    print("\n" + "=" * 50)
    print("Next steps:")
    print("  1. Edit .env with your LLM/Embeddings API keys")
    print("  2. Edit config.ini with your app settings")
    print("  3. Create test_dataset.csv with your test data")
    print("  4. Run: rag-sentinel run")
    print("=" * 50)


def cmd_run(args):
    """
    Run the RAG evaluation pipeline.

    This command:
        1. Validates that all required config files exist
        2. Reads MLflow host/port from config.ini
        3. Starts MLflow server (unless --no-server is specified)
        4. Runs the evaluation using the evaluator module

    Args:
        args: Parsed command-line arguments (includes --no-server flag)
    """
    # Check if all required configuration files exist
    required_files = [".env", "config.ini", "rag_eval_config.yaml"]
    missing_files = [f for f in required_files if not Path(f).exists()]

    if missing_files:
        print("❌ Missing configuration files:")
        for f in missing_files:
            print(f"   - {f}")
        print("\nRun 'rag-sentinel init' first to create config files.")
        sys.exit(1)

    # Start MLflow server if not disabled (uses host/port from config.ini)
    if not args.no_server:
        host, port = get_mlflow_host_port()
        start_mlflow_server(host, port)

    # Import and run the evaluation
    from rag_sentinel.evaluator import run_evaluation
    run_evaluation()


def cmd_validate(args):
    """
    Validate configuration files without running evaluation.

    Checks:
        - All required files exist (.env, config.ini, rag_eval_config.yaml)
        - Configuration can be loaded and parsed correctly
        - Displays key configuration values for verification

    Args:
        args: Parsed command-line arguments (unused, kept for consistency)
    """
    _ = args  # Unused, but required for command handler signature

    print("=" * 50)
    print("RAGSentinel - Configuration Validation")
    print("=" * 50)

    # Check if all required files exist
    files_to_check = [".env", "config.ini", "rag_eval_config.yaml"]
    all_exist = True

    for f in files_to_check:
        if Path(f).exists():
            print(f"✓ {f} exists")
        else:
            print(f"✗ {f} missing")
            all_exist = False

    if not all_exist:
        print("\n❌ Some files are missing. Run 'rag-sentinel init' first.")
        return

    # Attempt to load and validate configuration
    try:
        from rag_sentinel.evaluator import load_config
        config = load_config()
        print("\n✓ Configuration loaded successfully")
        print(f"  - LLM Provider: {config['ragas']['llm']['provider']}")
        print(f"  - Embeddings Provider: {config['ragas']['embeddings']['provider']}")
        print(f"  - Backend URL: {config['backend']['base_url']}")
        print(f"  - Dataset: {config['dataset']['path']}")
    except Exception as e:
        print(f"\n❌ Configuration error: {e}")


# =============================================================================
# Main Entry Point
# =============================================================================

def main():
    """
    Main entry point for the RAGSentinel CLI.

    Parses command-line arguments and dispatches to the appropriate
    command handler (init, run, or validate).
    """
    parser = argparse.ArgumentParser(
        prog="rag-sentinel",
        description="RAGSentinel - RAG Evaluation Framework using Ragas and MLflow"
    )

    subparsers = parser.add_subparsers(dest="command", help="Available commands")

    # 'init' command - Initialize project with config templates
    init_parser = subparsers.add_parser("init", help="Initialize project with config templates")
    init_parser.add_argument("--force", "-f", action="store_true", help="Overwrite existing files")

    # 'run' command - Run RAG evaluation
    run_parser = subparsers.add_parser("run", help="Run RAG evaluation")
    run_parser.add_argument("--no-server", action="store_true", help="Don't start MLflow server")

    # 'validate' command - Validate configuration files
    subparsers.add_parser("validate", help="Validate configuration files")

    args = parser.parse_args()

    # Dispatch to appropriate command handler
    if args.command == "init":
        cmd_init(args)
    elif args.command == "run":
        cmd_run(args)
    elif args.command == "validate":
        cmd_validate(args)
    else:
        parser.print_help()


if __name__ == "__main__":
    main()

