import sys
import os
import argparse 
import atexit 
import difflib 
import math 
from typing import List, Any

# Import core components from your language's implementation
try:
    from .core import (
        Lexer, Parser, Interpreter, ChotuFunction, 
        ChotuRuntimeError, ChotuSyntaxError
    )
except ImportError:
    # Fallback to help direct execution, though package import is preferred
    print("Warning: Could not import .core. Ensure cli.py is run as part of the 'chotulang' package.")
    # Define placeholder classes/functions to prevent hard crash if necessary for local testing
    class ChotuFunction: pass
    class Interpreter: 
        def __init__(self): self.variables = {}
        def interpret(self, ast): pass
    class Lexer: 
        def __init__(self, code): pass
        def tokenize(self): return []
    class Parser: 
        def __init__(self, tokens, source_name): pass
        def parse(self): return None
    class ChotuRuntimeError(Exception): pass
    class ChotuSyntaxError(Exception): pass
    

# --- CONSTANTS AND LICENSING ---
CHOTULANG_VERSION: str = "2.5.2"
CHOTULANG_OWNER: str = "Vishnu Deviprasad Shetty"
PASTE_TRIGGER: str = '--'

# Define valid REPL commands and their aliases
REPL_COMMANDS: List[str] = [
    'help', 'docs', 'vars', 'clear', 'cls', 'load', 'exit', 'quit', 
    'version', 'license', 'debug'
]

# Proprietary License Statement
CHOTULANG_LICENSE: str = f"""
=========================================
 Chotulang Software License (v{CHOTULANG_VERSION})
=========================================
All rights reserved. This software is proprietary and is the sole property of
{CHOTULANG_OWNER}.

Unauthorized copying, distribution, or modification of this software, or any 
portion of it, is strictly prohibited. Use of this software is subject to 
the terms of the end-user license agreement (EULA) provided by the owner.

Copyright (c) 2024 {CHOTULANG_OWNER}.
"""

# --- Full Documentation String (The Chotulang Development Guide) ---
# NOTE: Curly braces inside the documentation (e.g., in code examples like if x > 5 { ... }) 
# need to be escaped with {{ and }} if using an f-string, but since the raw text 
# provided is being inserted, we'll ensure they are correct (single curly braces)
# and remove the f-string formatting unless a variable is strictly needed (like OWNER).
CHOTULANG_DOCUMENTATION: str = """
==========================================
CHOTULANG DEVELOPMENT GUIDE (Version 2.5.1)
==========================================

Chotulang is a dynamic, Python-compatible language that emphasizes clean, readable syntax and supports powerful functional features like pipes and lambdas.

----------------------------
MODULE 1: CORE FUNDAMENTALS
----------------------------

1. PRINTING AND VARIABLES
Use 'out' to print. Use 'var' to declare a variable.

var name = "Alex"      // String
var count = 5          // Number
var is_active = true   // Boolean

out "Hello,", name, "!"
out "The value is:", count

// User Input
out "Enter a number:"
var user_input = ask() // Reads a line of text
out "You entered:", user_input

2. COMMENTS
Use the '#' symbol for comments.

# This line is ignored by the interpreter

----------------------------
MODULE 2: OPERATOR PRECEDENCE
----------------------------

Understanding the order of operations ensures your calculations are correct.

| Precedence | Operator | Description | Example |
| :--- | :--- | :--- | :--- |
| Highest | () | Parentheses (Forces order) | `(5 + 2) * 3` |
| 2 | ** | Power/Exponent (Right-associative) | `2 ** 3 ** 2` is `2 ** (3 ** 2)` (512) |
| 3 | *, /, % | Multiplication, Division, Modulo | `10 / 2 * 5` (Left-to-right) |
| 4 | +, - | Addition, Subtraction | `3 + 4 - 1` (Left-to-right) |
| 5 | <, >, ==, !=| Comparison | `x > 5` |
| 6 | &&, || | Logical AND, OR | `true && false` |
| Lowest | =, +=, -=, *=, /= | Assignment | `x = 10` |

// Power Operator Example (Exponent)
var result_power = 8 ** 2
out "8 squared is:", result_power // 64

var complex_calc = 10 - 2 ** 3 * 2
// Order: 2**3 (8), then 8*2 (16), then 10-16 (-6)
out "Complex calculation:", complex_calc

----------------------------
MODULE 3: CONTROL FLOW & LOOP JUMPS
----------------------------

1. IF/ELSE STATEMENTS
Conditional execution blocks. The curly braces {} are mandatory.

var temperature = 30

if temperature > 25 {
    out "It is hot."
} else {
    out "It is mild."
}

2. LOOPS (ITERATION)

// The 'do' Loop (Similar to While)
var i = 0
do i < 3 {
    out "Do loop iteration:", i
    i += 1
}

// The 'for' Loop (Iterates over a list or range)
for number in 0..4 {
    out "Current number:", number
}

3. LOOP JUMP STATEMENTS

// WARNING: These must only be used **inside** a 'do' or 'for' loop!

// 'break'
// Stops the current loop entirely and execution continues after the loop.
do true {
    out "Running once..."
    break // Exits the 'do' loop
}

// 'next'
// Skips the rest of the code in the current loop body and starts the next iteration.
for i in 1..5 {
    if i == 3 {
        next // Skip printing 3, go straight to 4
    }
    out "Number:", i
}


----------------------------
MODULE 4: DATA STRUCTURES
----------------------------

1. LISTS (Ordered Collections)

var fruits = ["apple", "banana", "kiwi"]
out fruits[1]          // Access: banana
fruits.append("grape") // Add to end

2. DICTIONARIES (Key-Value Pairs)

var user = {
    "id": 101,
    "status": "online"
}

out user["status"]     // Access: online
user.set("city", "London") // Add/Update a key

----------------------------
MODULE 5: FUNCTIONS AND PIPES
----------------------------

1. STANDARD FUNCTIONS ('fn')
Use 'fn' for reusable code blocks. Use 'ret' to return a value.

fn multiply(x, y) {
    ret x * y
}

var product = multiply(8, 6)
out "Product:", product

2. LAMBDA FUNCTIONS (Anonymous Functions)

// Syntax: fn(params) => expression
var is_even = fn(n) => n % 2 == 0
out is_even(4) // true

3. THE PIPE OPERATOR (`|>`)
Pass the result of one expression as the first argument to the next function.

fn increment(x) { ret x + 1 }
fn double(x) { ret x * 2 }

var value = 10 |> increment |> double // (10 + 1) * 2 = 22
out value

----------------------------
MODULE 6: COMPLEX MATHEMATICAL OPERATIONS
----------------------------

1. USING THE PYTHON MATH LIBRARY
Import the standard Python 'math' module using the 'use' keyword.

use math

// Square Root
var root = math.sqrt(64)
out "Root of 64:", root

// Trigonometry and Constants
var angle = math.radians(45)
var sine_val = math.sin(angle)
out "Sine of 45 deg:", sine_val
out "Pi value:", math.pi // Access constant

2. COMPLEX CALCULATION WITH PIPES

fn area_calc(r) { ret math.pi * r ** 2 } // Using ** for r squared
fn round_2(n) { ret round(n, 2) }

var r = 5
var final_area = r |> area_calc |> round_2 // 5 -> area_calc -> round_2
out "Rounded Area:", final_area

----------------------------
PRACTICE PROJECTS
----------------------------

1. THE GUESSING GAME
Use the 'random' library (`use random`). Generate a random number. Use a 'do' loop and 'if/else' to let the user guess until correct, providing 'Too High' or 'Too Low' hints.

2. MATH & DATA PIPELINE
Create a list of numbers. Use `map` (or a loop) and a lambda to cube every number, then use the pipe operator to feed the resulting list to a function that calculates the sum of all elements.

3. GEOMETRY FUNCTION
Write a function that accepts two numbers (A and B). This function must check if A and B are positive numbers. If they are, it uses the 'math' library to calculate the perimeter of a rectangle (2A + 2B) and returns it. If not, it returns the string "Error: Invalid dimensions".

=========================================
 1. CLI Usage
---------------------------------
The interpreter can be run from your terminal.

* `chotulang`
  Starts the Interactive REPL (Read-Eval-Print Loop).

* `chotulang <filename>`
  Executes the specified Chotulang source file (e.g., `my_script.chotu`).

* `chotulang --debug <filename>`
  Executes the file in debug mode, printing Lexer tokens and Parser AST.

* `chotulang -h` or `--help`
  Displays the command-line help message.

* `chotulang -v` or `--version`
  Displays the interpreter version.

* `chotulang --license`
  Displays the software license.

---------------------------------
 2. REPL Meta-Commands
---------------------------------
Inside the REPL, the following commands are available:
(No leading '/' is required)

* `help` (or `docs`)
  Prints this full documentation.

* `clear` (or `cls`)
  Clears the terminal screen.

* `debug`
  Toggles debug mode (Tokens/AST) ON or OFF for the current session.

* `vars`
  Displays all currently defined global variables.

* `load <filename>`
  Loads and executes a .chotu file into the current REPL session.
  Variables and functions from the file will be available to use.
  Example: `load my_functions.chotu`

* `exit` (or `quit`)
  Exits the REPL.

* '""" + PASTE_TRIGGER + """ ... """ + PASTE_TRIGGER + """' (Multi-line Paste)
  Enter '""" + PASTE_TRIGGER + """' on a line by itself to start multi-line input.
  Paste or type your code, then enter '""" + PASTE_TRIGGER + """' on a new line to execute.

---------------------------------
 3. Language Keywords & Syntax
---------------------------------
| Category      | Keyword   | Description |
| :------------- | :-------- | :---------- |
| Declaration | `var`     | Declares a mutable variable (e.g., `var x = 10`). |
| Output | `out`     | Prints expressions (e.g., `out "hello", 1 + 2`). |
| Logic | `if`, `else` | Conditional execution (e.g., `if x > 5 { ... } else { ... }`). |
| Loops | `do`      | Defines a 'while' loop (e.g., `do x < 10 { ... }`). |
| Iteration | `for`, `in` | Iterates over an iterable (e.g., `for i in [1,2,3] { ... }`). |
| Flow Control | `break`, `next` | Exits a loop (`break`) or skips to the next iteration (`next`). |
| Functions | `fn`, `ret` | Defines a function (`fn f(a) { ... }`) and returns a value (`ret a * 2`). |
| Lambda | `=>`      | Defines an anonymous function (e.g., `fn(x) => x + 1`). |
| Error Handle | `try`, `catch` | Handles runtime errors (e.g., `try { ... } catch err_var { ... }`). |
| Module | `use`, `as` | Imports Python modules (e.g., `use math as m`). |
| Static Type | `strict`  | Toggles optional static type checking (e.g., `strict true`). |
| Literals | `true`, `false`, `null` | Boolean and null values. |

---------------------------------
 4. Operators
---------------------------------
* Arithmetic: `+`, `-`, `*`, `/`, `%`
* Compound: `+=`, `-=`, `*=`, `/=`, `%=` (e.g., `x += 1`)
* Comparison: `==`, `!=`, `<`, `>`, `<=`, `>=`
* Logical: `and`, `or`, `not`
* **Pipe: `|>`** (e.g., `10 |> add_five |> double`)
* Range: `..` (e.g., `1..10` creates a list from 1 to 9)
* Null Coalesce: `??` (e.g., `a ?? "default"`)

---------------------------------
 5. Data Types
---------------------------------
* Numbers: `123`, `45.6`
* Strings: `"hello"`, `'world'`
* Lists: `[1, "two", true]`
* Dicts: `{"a": 1, "b": 2}`
* Functions: First-class citizens. They can be assigned to variables and
             passed to other functions.

---------------------------------
 6. Built-in Functions
---------------------------------
* `len()`, `input()`, `type()`, `range()`
* `num()`, `str()`, `bool()`, `list()` (Type conversions)
* `map()`, `filter()`, `reduce()` (Supports Chotulang `fn` types)
* `keys()`, `values()`, `items()`, `sorted()`, `reversed()`
* `read_file()`, `write_file()`, `append_file()`
* `json_parse()`, `json_stringify()`
* `py_import()`, `py_call()`, `py_get()`, `py_set()` (Python Interop)
* ...and many more math/utility functions.

---------------------------------
 7. Method Calls
---------------------------------
* String: `my_str.upper()`, `my_str.lower()`, `my_str.split(',')`
* List: `my_list.append(x)`, `my_list.pop()`, `my_list.sort()`
* Dict: `my_dict.keys()`, `my_dict.values()`, `my_dict.get(key)`

Author: """ + CHOTULANG_OWNER + """ (a.k.a idkshetty a.k.a chotu)
"""


def run_code(interpreter: Interpreter, code_string: str, debug: bool = False, source_name: str = "<input>"):
    """Lexes, Parses, and Interprets the code string, handling errors."""
    try:
        lexer = Lexer(code_string)
        tokens = list(lexer.tokenize())
        
        if debug:
            print(f"\n[DEBUG] Tokens: {tokens}")
        
        parser = Parser(tokens, source_name=source_name)
        ast = parser.parse()
        
        if debug:
            print(f"[DEBUG] AST: {ast}")
        
        interpreter.interpret(ast)

    except ChotuSyntaxError as e:
        print(f"Syntax Error: {e}")
    except ChotuRuntimeError as e:
        print(f"Runtime Error: {e}")
    except Exception as e:
        print(f"Error: An unexpected internal error occurred: {e}")


def repl(debug_mode: bool = False):
    """
    The Read-Eval-Print Loop for interactive Chotulang coding.
    """
    interpreter = Interpreter()
    pasting_mode = False
    code_block: List[str] = []

    print(f"Chotulang Interpreter (v{CHOTULANG_VERSION})")
    print("Type 'help' for commands or 'exit' to quit.")

    current_debug_mode: List[bool] = [debug_mode]

    # Register exit handler
    def cleanup():
        print("Interpreter shutting down...")
    atexit.register(cleanup)


    while True:
        try:
            # Change prompt based on pasting mode
            prompt = ">>> " if not pasting_mode else "... "
            line = input(prompt)
            trimmed_line = line.strip()

            # --- Multi-line Paste Mode Toggle (using '--') ---
            if trimmed_line == PASTE_TRIGGER:
                if not pasting_mode:
                    print(f"--- Entering multi-line paste mode. End with '{PASTE_TRIGGER}' on a new line to execute. ---")
                    pasting_mode = True
                else:
                    pasting_mode = False
                    code_to_run = '\n'.join(code_block)
                    code_block = [] # Reset block
                    if code_to_run.strip():
                        run_code(interpreter, code_to_run, debug=current_debug_mode[0])
                continue
            
            if pasting_mode:
                code_block.append(line)
                continue
            
            if not trimmed_line:
                continue 

            # --- REPL Command Handling (No leading '/') ---
            first_word_raw = trimmed_line.split(maxsplit=1)[0]
            first_word = first_word_raw.lower()
            
            if first_word in REPL_COMMANDS:
                command = first_word
                arg = trimmed_line[len(first_word_raw):].strip() 

                if command == 'exit' or command == 'quit':
                    print("Bye!")
                    sys.exit(0)

                if command == 'version':
                    print(f"Chotulang Version: {CHOTULANG_VERSION}")
                    continue
                    
                if command == 'license':
                    print(CHOTULANG_LICENSE)
                    continue

                if command == 'clear' or command == 'cls':
                    os.system('cls' if os.name == 'nt' else 'clear')
                    continue

                if command == 'help' or command == 'docs':
                    print(CHOTULANG_DOCUMENTATION)
                    continue
                
                if command == 'debug':
                    current_debug_mode[0] = not current_debug_mode[0]
                    print(f"Debug mode is now {'ON' if current_debug_mode[0] else 'OFF'} (shows tokens and AST).")
                    continue

                if command == 'vars':
                    print("\n--- Current Global Variables ---")
                    if interpreter.variables:
                        for name, value in interpreter.variables.items():
                            type_name = type(value).__name__
                            
                            if isinstance(value, ChotuFunction):
                                type_name = 'Function(Chotu)'
                            elif type_name == 'module':
                                type_name = f'Module: {name}'

                            display_value = repr(value)
                            if len(display_value) > 60:
                                display_value = display_value[:57] + '...'
                            
                            print(f"  {name} ({type_name}): {display_value}")
                    else:
                        print("  (No variables defined)")
                    print("--------------------------------\n")
                    continue
                
                if command == 'load':
                    filename = arg
                    if not filename:
                        print("Error: Please specify a filename. Usage: load <filename>")
                        continue
                    if not os.path.exists(filename):
                        print(f"Error: File '{filename}' not found")
                        continue
                    
                    try:
                        with open(filename, 'r', encoding='utf-8') as f:
                            file_code = f.read()
                        print(f"Loading and executing '{filename}'...")
                        run_code(interpreter, file_code, debug=current_debug_mode[0], source_name=filename) 
                    except Exception as e:
                        print(f"Error loading file: {e}")
                    continue
            
            # --- Error Suggestion for Unknown Commands ---
            if first_word and all(c.isalpha() for c in first_word):
                suggestions = difflib.get_close_matches(first_word, REPL_COMMANDS, n=3, cutoff=0.6)
                if suggestions:
                    suggestion_str = ", ".join(f"'{s}'" for s in suggestions)
                    print(f"Error: Unknown command '{first_word}'. Did you mean {suggestion_str}?")
                    continue

            # --- Execute Single-Line Chotulang Code ---
            run_code(interpreter, line, debug=current_debug_mode[0]) 

        except KeyboardInterrupt: 
            if pasting_mode:
                print("\n--- Pasting mode cancelled ---")
                pasting_mode = False
                code_block = [] 
            else:
                print("\nBye!"); 
                sys.exit(0)
        except EOFError:
            print("\nBye!")
            sys.exit(0)
        except Exception as e: 
            print(f"Internal Error in REPL loop: {e}")
            pasting_mode = False
            code_block = []


def main():
    """
    Main entry point for the CLI application, handles file execution and REPL start.
    """
    parser = argparse.ArgumentParser(
        description=f"Chotulang Interpreter (Version {CHOTULANG_VERSION}).",
    )

    parser.add_argument(
        'filename', 
        nargs='?', 
        default=None, 
        help='The Chotulang source file to execute.'
    )
    
    parser.add_argument(
        '--debug', 
        action='store_true', 
        help='Run interpreter in debug mode (shows tokens and AST).'
    )
    
    # CLI Version Argument (-v or --version)
    parser.add_argument(
        '-v', '--version', 
        action='version',
        version=f'%(prog)s {CHOTULANG_VERSION}',
        help='Show the interpreter version and exit.'
    )
    
    # CLI License Argument (--license)
    parser.add_argument(
        '--license', 
        action='store_true', 
        help='Show the software license and proprietary statement.'
    )

    args = parser.parse_args()
    debug_mode = args.debug

    # Handle CLI License Logic
    if args.license:
        print(CHOTULANG_LICENSE)
        sys.exit(0)

    # Note: Interpreter is initialized inside repl() for interactive use, 
    # but here for file execution.
    interpreter = Interpreter()
    
    # Handle file execution or REPL start
    if args.filename:
        if not os.path.exists(args.filename):
            print(f"Error: File not found: {args.filename}")
            sys.exit(1)
            
        try:
            with open(args.filename, 'r', encoding='utf-8') as f:
                code = f.read()
            run_code(interpreter, code, debug=debug_mode, source_name=args.filename)
        except Exception as e:
            print(f"Execution failed: {e}")
            sys.exit(1)
    else:
        # Start REPL if no filename is provided
        repl(debug_mode=debug_mode)


if __name__ == '__main__':
    main()