//! main.rs - CLI minima para este prototipo. Uso:
//!
//!     sipi_runtime_rust '<expresion>' [nombre=valor ...]
//!
//! Cada 'nombre=valor' declara una variable antes de evaluar; 'valor'
//! puede ser un numero, `"texto entre comillas"`, `verdadero`/`falso`
//! o `nulo`. Imprime en stdout un JSON de una sola linea, a proposito
//! escrito a mano (sin depender de ninguna libreria externa, misma
//! filosofia que `sipi.py`/`sipi_lsp.py` del lado Python):
//!
//!     {"ok":true,"tipo":"numero","valor":14}
//!     {"ok":true,"tipo":"texto","valor":"Puntaje: 10"}
//!     {"ok":false,"error":"Division por cero al evaluar..."}
//!
//! Este binario es el tercer lado de la validacion cruzada que hace
//! `tests/test_paridad_rust.py`: el mismo lote de expresiones se corre
//! aca, en `ast_sipi.py` y en `Interprete.evaluar_expresion` (el motor
//! de produccion real), y las tres respuestas deben coincidir.

use sipi_runtime_rust::parser::{evaluar_texto, Valor};
use std::collections::HashMap;
use std::env;

fn escapar_json(s: &str) -> String {
    let mut out = String::with_capacity(s.len() + 2);
    for c in s.chars() {
        match c {
            '"' => out.push_str("\\\""),
            '\\' => out.push_str("\\\\"),
            '\n' => out.push_str("\\n"),
            '\t' => out.push_str("\\t"),
            '\r' => out.push_str("\\r"),
            c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
            c => out.push(c),
        }
    }
    out
}

fn parsear_valor_variable(crudo: &str) -> Result<Valor, String> {
    let crudo = crudo.trim();
    if crudo == "verdadero" {
        return Ok(Valor::Booleano(true));
    }
    if crudo == "falso" {
        return Ok(Valor::Booleano(false));
    }
    if crudo == "nulo" {
        return Ok(Valor::Nulo);
    }
    if crudo.starts_with('"') && crudo.ends_with('"') && crudo.len() >= 2 {
        return Ok(Valor::Texto(crudo[1..crudo.len() - 1].to_string()));
    }
    match crudo.parse::<f64>() {
        Ok(n) => Ok(Valor::Numero(n)),
        Err(_) => Err(format!("No se pudo interpretar el valor de variable: '{}'", crudo)),
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
        eprintln!("Uso: sipi_runtime_rust '<expresion>' [nombre=valor ...]");
        std::process::exit(2);
    }

    let expresion = &args[1];
    let mut variables: HashMap<String, Valor> = HashMap::new();

    for asignacion in &args[2..] {
        match asignacion.split_once('=') {
            Some((nombre, valor_crudo)) => match parsear_valor_variable(valor_crudo) {
                Ok(v) => {
                    variables.insert(nombre.trim().to_string(), v);
                }
                Err(e) => {
                    println!("{{\"ok\":false,\"error\":\"{}\"}}", escapar_json(&e));
                    std::process::exit(1);
                }
            },
            None => {
                println!(
                    "{{\"ok\":false,\"error\":\"Argumento de variable invalido (se esperaba nombre=valor): '{}'\"}}",
                    escapar_json(asignacion)
                );
                std::process::exit(1);
            }
        }
    }

    match evaluar_texto(expresion, &variables) {
        Ok(Valor::Numero(n)) => println!("{{\"ok\":true,\"tipo\":\"numero\",\"valor\":{}}}", n),
        Ok(Valor::Texto(s)) => println!("{{\"ok\":true,\"tipo\":\"texto\",\"valor\":\"{}\"}}", escapar_json(&s)),
        Ok(Valor::Booleano(b)) => println!("{{\"ok\":true,\"tipo\":\"booleano\",\"valor\":{}}}", b),
        Ok(Valor::Nulo) => println!("{{\"ok\":true,\"tipo\":\"nulo\",\"valor\":null}}"),
        Err(mensaje) => {
            println!("{{\"ok\":false,\"error\":\"{}\"}}", escapar_json(&mensaje));
            std::process::exit(1);
        }
    }
}
