"""
REPL (Read-Eval-Print Loop) for the Alloy Programming Language

This module provides an interactive shell for Alloy, similar to Python's
interactive interpreter.
"""

import sys
import readline
from typing import Optional, List

from .interpreter import Interpreter
from .parser import AlloyParser


class AlloyREPL:
    """Interactive REPL for Alloy"""
    
    def __init__(self, debug: bool = False, model: str = "gpt-4o-mini"):
        """Initialize the REPL"""
        self.parser = AlloyParser()
        self.interpreter = Interpreter(use_gpt4_mini=(model == "gpt-4o-mini"))
        self.debug = debug
        self.model = model
        self.multiline_buffer: List[str] = []
        self.in_multiline = False
        
        # Set up readline for better user experience
        self._setup_readline()
    
    def _setup_readline(self):
        """Configure readline for command history and editing"""
        try:
            import readline
            # Enable tab completion (basic)
            readline.parse_and_bind("tab: complete")
            
            # Load command history if available
            import os
            history_file = os.path.expanduser("~/.alloy_history")
            try:
                readline.read_history_file(history_file)
            except (FileNotFoundError, PermissionError, OSError):
                # History file doesn't exist or can't be read - that's fine
                pass
            
            # Save history on exit
            import atexit
            def save_history():
                try:
                    readline.write_history_file(history_file)
                except (PermissionError, OSError):
                    # Can't save history - that's fine
                    pass
            atexit.register(save_history)
            
        except ImportError:
            # readline not available (e.g., on Windows)
            pass
    
    def run(self):
        """Start the REPL"""
        self._print_welcome()
        
        while True:
            try:
                if self.in_multiline:
                    prompt = "... "
                else:
                    prompt = ">>> "
                
                line = input(prompt)
                
                # Handle special commands
                if self._handle_special_commands(line):
                    continue
                
                # Check if we need to start/continue multiline input
                if self._needs_multiline(line):
                    self.multiline_buffer.append(line)
                    self.in_multiline = True
                    continue
                
                # If we're in multiline mode and get empty line, execute
                if self.in_multiline and line.strip() == "":
                    source = "\n".join(self.multiline_buffer)
                    self._execute_code(source)
                    self.multiline_buffer = []
                    self.in_multiline = False
                    continue
                
                # If we're in multiline mode, add to buffer
                if self.in_multiline:
                    self.multiline_buffer.append(line)
                    continue
                
                # Single line execution
                if line.strip():
                    self._execute_code(line)
                    
            except KeyboardInterrupt:
                print("\nKeyboardInterrupt")
                self.multiline_buffer = []
                self.in_multiline = False
                continue
            except EOFError:
                print("\nExiting Alloy REPL...")
                break
            except Exception as e:
                print(f"Unexpected error: {e}")
                if self.debug:
                    import traceback
                    traceback.print_exc()
    
    def _print_welcome(self):
        """Print welcome message"""
        print("Alloy Programming Language")
        print(f"Version 0.1.0 (using {self.model})")
        print('Type "help" for more information, "exit" to quit.')
        print()
    
    def _handle_special_commands(self, line: str) -> bool:
        """Handle special REPL commands"""
        line = line.strip()
        
        if line in ("exit", "quit"):
            print("Exiting Alloy REPL...")
            sys.exit(0)
        
        elif line == "help":
            self._print_help()
            return True
        
        elif line == "clear":
            import os
            os.system('cls' if os.name == 'nt' else 'clear')
            return True
        
        elif line.startswith("load "):
            filename = line[5:].strip()
            self._load_file(filename)
            return True
        
        elif line == "debug on":
            self.debug = True
            print("Debug mode enabled")
            return True
        
        elif line == "debug off":
            self.debug = False
            print("Debug mode disabled")
            return True
        
        elif line == "env":
            self._show_environment()
            return True
        
        elif line == "reset":
            self.interpreter = Interpreter(use_gpt4_mini=(self.model == "gpt-4o-mini"))
            print("Environment reset")
            return True
        
        return False
    
    def _print_help(self):
        """Print help information"""
        print("""
Alloy REPL Commands:
  help          - Show this help message
  exit, quit    - Exit the REPL
  clear         - Clear the screen
  load <file>   - Load and execute an .ay file
  debug on/off  - Toggle debug mode
  env           - Show current environment (variables, tools, agents)
  reset         - Reset the interpreter environment

Language Keywords:
  agent, alloy, command, do, false, for, in, means, must, null, otherwise, returns, should, takes, then, tool, tools, true, when, while, with

Syntax Patterns:
  Tool Definition:
    tool <name> takes <params> returns <var> { ... }
  Agent Definition:
    agent <name> "<model>" with tools <tools> { ... }
  Assignment:
    <var> = <expression>
  Tuple Assignment:
    <var1>, <var2> = <expression>
  Control Flow:
    when <condition> then <action> otherwise <fallback>
    for <var> in <iterable> do { ... }
    while <condition> do { ... }
  Agent Commands:
    <result>, <error> = <agent> <Command> <data>
  Chaining:
    <result> = <operation1> then <operation2>

For multiline input, press Enter on an empty line to execute.
        """)
    
    def _load_file(self, filename: str):
        """Load and execute an Alloy file"""
        try:
            with open(filename, 'r', encoding='utf-8') as f:
                source = f.read()
            
            print(f"Loading {filename}...")
            self._execute_code(source)
            print(f"Loaded {filename}")
            
        except FileNotFoundError:
            print(f"Error: File '{filename}' not found")
        except Exception as e:
            print(f"Error loading {filename}: {e}")
    
    def _show_environment(self):
        """Show current environment state"""
        env = self.interpreter.global_env
        
        print("Current Environment:")
        print("-" * 20)
        
        # Show variables
        if env.vars:
            print("Variables:")
            for name, value in env.vars.items():
                if not name.startswith("_"):  # Skip internal variables
                    print(f"  {name}: {self._format_value(value)}")
        
        # Show tools
        if env.tools:
            print("Tools:")
            for name in env.tools.keys():
                print(f"  {name}()")
        
        # Show agents
        if env.agents:
            print("Agents:")
            for name, agent_info in env.agents.items():
                model = agent_info.get('model', 'unknown')
                print(f"  {name} ({model})")
        
        if not env.vars and not env.tools and not env.agents:
            print("  (empty)")
    
    def _format_value(self, value) -> str:
        """Format a value for display"""
        if isinstance(value, str):
            return f'"{value}"'
        elif isinstance(value, (list, tuple)):
            return f"[{len(value)} items]"
        elif isinstance(value, dict):
            return f"{{{len(value)} items}}"
        elif callable(value):
            return "<function>"
        else:
            return str(value)
    
    def _needs_multiline(self, line: str) -> bool:
        """Check if line needs multiline input"""
        stripped = line.strip()
        
        # Check for opening braces/blocks
        multiline_starters = [
            "tool ", "agent ", "when ", "for ", "while ",
            "{", "must", "should"
        ]
        
        return any(stripped.startswith(starter) for starter in multiline_starters)
    
    def _execute_code(self, source: str):
        """Execute Alloy source code"""
        try:
            if self.debug:
                print(f"Parsing: {source}")
            
            ast = self.parser.parse(source, debug=self.debug)
            
            if self.debug:
                print(f"AST: {ast}")
            
            result = self.interpreter.interpret(ast)
            
            # Print result if it's not None
            if result is not None:
                print(self._format_result(result))
                
        except Exception as e:
            print(f"Error: {e}")
            if self.debug:
                import traceback
                traceback.print_exc()
    
    def _format_result(self, result) -> str:
        """Format result for display"""
        if isinstance(result, tuple) and len(result) == 2:
            # Handle (result, error) tuples
            res, err = result
            if err:
                return f"Error: {err}"
            else:
                return str(res)
        else:
            return str(result)