"""
Soren CLI - Command-line interface for Soren AI evaluation framework
"""
import yaml
import argparse
import sys
import os
import subprocess
from datetime import datetime
from . import __version__
from .client import SorenClient
from .config import SorenConfig


def handle_login(args):
    """Handle the login command"""
    config = SorenConfig()

    api_key = args.SOREN_API_KEY or input("Enter your Soren API key: ")
    try:
        # Authenticate with backend
        print("Authenticating with backend...")
        client = SorenClient(base_url=config.get_api_url())
        print("API key: ", api_key)
        response = client.login(api_key)
        
        # Store API key locally
        api_key = response.get('access_token')
        if api_key:
            config.set_api_key(api_key)
            print("✓ Successfully logged in!")
            print(f"API key stored in {config.config_file}")
        else:
            print("✗ Login failed: No API key received")
            sys.exit(1)
            
    except Exception as e:
        print(f"✗ Login failed: {e}")
        sys.exit(1)


def handle_run(args):
    """Handle the run command"""

    """
    config-path: The path to the config file
    - This config file is a YAML file that contains the necessary configs for run
 
    Pseudocode:
    1. API is parsed to validate each run.
    2. Reads the YAML configuration file for each of the necessary toggles + other for UI.
    3. Spawns their CLI and commands with these toggles.
    4. This creates a new run in my frontend (running)
    5. After it is done, it pulls from the output directory and outputs to backend and my UI.
    6. View on UI
    """
    config = SorenConfig()
    api_key = config.get_api_key()
    
    if not api_key:
        print("✗ Not logged in. Run 'soren login' first.")
        sys.exit(1)

    print("API key: ", api_key)
    print("Config path: ", args.config_path)
    
    try:
        with open(args.config_path, "r") as f:
            yaml_data = yaml.safe_load(f)

        print("YAML data: ", yaml_data)

        # Print for debugging purposes
        print("\n=== YAML Configuration ===")
        print(yaml.dump(yaml_data, default_flow_style=False))
        print("=" * 26 + "\n")

    except FileNotFoundError:
        print(f"✗ Error: Config file not found at: {args.config_path}")
        sys.exit(1)
    except yaml.YAMLError as e:
        print(f"✗ Error: Invalid YAML format: {e}")
        sys.exit(1)
    except Exception as e:
        print(f"✗ Error reading config file: {e}")
        sys.exit(1)
    
    # Build command from config
    print("\n=== Building Command ===")
    try:
        command = build_command_from_config(yaml_data)
        print(f"Command: {command}\n")
    except Exception as e:
        print(f"✗ Error building command: {e}")
        sys.exit(1)
    
    # Create run in backend
    try:
        client = SorenClient(api_key=api_key, base_url=config.get_api_url())
        
        print("Creating a new evaluation run...")
        run = client.create_run(yaml_config=yaml_data)
        run_id = run.get('id')
        
        print(f"✓ Run created: {run_id}")
        
    except Exception as e:
        print(f"✗ Failed to create run: {e}")
        sys.exit(1)
    
    # Determine working directory
    working_dir = yaml_data.get("working-directory")
    if not working_dir:
        # Default to directory containing the YAML config file
        working_dir = os.path.dirname(os.path.abspath(args.config_path))
    
    print(f"Working directory: {working_dir}")
    
    # Validate working directory exists
    if not os.path.isdir(working_dir):
        print(f"✗ Error: Working directory does not exist: {working_dir}")
        sys.exit(1)
    
    # Execute command locally
    print("\n=== Executing Command ===")
    print(f"Starting execution at {datetime.now()}")
    print("-" * 50)
    print()
    
    result = execute_command(command=command, working_dir=working_dir)
    
    print()
    print("-" * 50)
    
    if result["success"]:
        print(f"✓ Command completed successfully!")
        print(f"Exit code: {result['exit_code']}")
    else:
        print(f"✗ Command failed with exit code: {result['exit_code']}")
        if result["stderr"]:
            print(f"Error: {result['stderr']}")
        sys.exit(1)
    
    print(f"\nView run at: {config.get_api_url() or 'http://localhost:8000'}/runs/{run_id}")

def execute_command(command: str, working_dir: str = None) -> dict:
    """
    Execute a shell command on the local machine with real-time output.
    
    Args:
        command: The full command string to execute
        working_dir: Optional working directory (defaults to current dir)
        
    Returns:
        Dict with execution results: {
            "success": bool,
            "exit_code": int,
            "stdout": str,
            "stderr": str
        }
    """
    print(f"Executing: {command}")
    print(f"Working directory: {working_dir or os.getcwd()}")
    print()
    
    try:
        # Start the process
        process = subprocess.Popen(
            command,
            shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
            bufsize=1,  # Line buffered
            cwd=working_dir
        )
        
        # Collect output while streaming to console
        stdout_lines = []
        stderr_lines = []
        
        # Read stdout in real-time
        while True:
            stdout_line = process.stdout.readline()
            if stdout_line:
                print(stdout_line, end='')
                stdout_lines.append(stdout_line)
            
            # Check if process has finished
            if process.poll() is not None:
                # Read any remaining output
                remaining = process.stdout.read()
                if remaining:
                    print(remaining, end='')
                    stdout_lines.append(remaining)
                break
        
        # Read stderr after process completes
        stderr_output = process.stderr.read()
        if stderr_output:
            print(stderr_output, file=sys.stderr)
            stderr_lines.append(stderr_output)
        
        # Get exit code
        exit_code = process.returncode
        
        return {
            "success": exit_code == 0,
            "exit_code": exit_code,
            "stdout": "".join(stdout_lines),
            "stderr": "".join(stderr_lines)
        }
        
    except FileNotFoundError:
        return {
            "success": False,
            "exit_code": -1,
            "stdout": "",
            "stderr": f"Command not found: {command.split()[0]}"
        }
    except Exception as e:
        return {
            "success": False,
            "exit_code": -1,
            "stdout": "",
            "stderr": f"Error executing command: {str(e)}"
        }


def build_command_from_config(yaml_config: dict) -> str:
    """
    Build a shell command from YAML configuration.
    
    Args:
        yaml_config: Parsed YAML configuration
        
    Returns:
        Complete command string
    """
    # Reserved keys that are not CLI flags
    RESERVED_KEYS = {
        "cmd", "name", "description", "config-path", 
        "output-path", "working-directory"
    }
    
    # Get base command
    base_cmd = yaml_config.get("cmd")
    if not base_cmd:
        raise ValueError("YAML config must contain 'cmd' field")
    
    # Build argument list
    args = []
    
    for key, value in yaml_config.items():
        # Skip reserved keys
        if key in RESERVED_KEYS:
            continue
            
        # Skip None or empty values
        if value is None or value == "":
            continue
        
        # Convert key to CLI flag format (e.g., "feature-flag" -> "--feature-flag")
        flag = f"--{key}"
        
        # Handle different value types
        if isinstance(value, bool):
            # Boolean flags: only include if True
            if value:
                args.append(flag)
        else:
            # String/number flags: include with value
            args.append(f"{flag} {value}")
    
    # Join everything together
    full_command = base_cmd
    if args:
        full_command += " " + " ".join(args)
    
    return full_command

def handle_logout(args):
    """Handle the logout command"""
    config = SorenConfig()
    config.clear()
    print("✓ Logged out successfully")


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

    # Main parser
    parser = argparse.ArgumentParser(
        prog="soren",
        description="Soren AI - Evaluation framework CLI",
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    
    # Global version flag
    parser.add_argument(
        "--version",
        action="version",
        version=f"soren {__version__}",
    )
    
    # Subparsers for different commands
    subparsers = parser.add_subparsers(dest="command", help="Available commands")
    
    # API login command
    login_parser = subparsers.add_parser("login", help="Authenticate with Soren")
    login_parser.add_argument("--SOREN_API_KEY", help="Your Soren API key")
    
    # Run command (will be edited later to support their CLI)

    # THEIR EVALS CLI
    # 3. **Local**: Run `poetry run evaluate-agents-performance`
    # - **Base mode**: `poetry run evaluate-agents-performance`
    # - **With feature flags**: `poetry run evaluate-agents-performance --feature-flag is_opted_in_to_gold_examples`
    # - **With baseline agents**: `poetry run evaluate-agents-performance --include-baseline`
    # - **On backtesting data**: `poetry run evaluate-agents-performance --end-to-end`
    run_parser = subparsers.add_parser("run", help="Create and run an evaluation")
    run_parser.add_argument("config_path", help="The path to the config file")
    
    # Logout command
    logout_parser = subparsers.add_parser("logout", help="Clear stored credentials")
    
    args = parser.parse_args()
    
    if args.command is None:
        parser.print_help()
        sys.exit(0)
    
    # Route to command handlers
    if args.command == "login":
        handle_login(args)
    elif args.command == "run":
        handle_run(args)
    elif args.command == "logout":
        handle_logout(args)
    else:
        print(f"Command '{args.command}' not yet implemented")


if __name__ == "__main__":
    main()
