use mumu::parser::interpreter::{Interpreter, DynamicFnInfo};
use mumu::parser::types::{FunctionValue, Value};
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use crate::functions::short_uuid::generate_short_uuid;
use lazy_static::lazy_static;
use indexmap::IndexMap;

/* ────────────────────────────────────────────────────────────────────────── */
/*  Helper: wrap an error string into a Value::KeyedArray                    */
/* ────────────────────────────────────────────────────────────────────────── */
fn make_error(msg: impl Into<String>) -> Value {
    let mut map = IndexMap::new();
    map.insert("ok".to_string(), Value::Bool(false));
    map.insert("error".to_string(), Value::SingleString(msg.into()));
    Value::KeyedArray(map)
}
/* ────────────────────────────────────────────────────────────────────────── */

#[derive(Clone)]
pub struct AwsSession {
    pub access_key: String,
    pub secret_key: String,
    pub region: String,
    pub endpoint: Option<String>,
}

impl AwsSession {
    pub fn is_valid(&self) -> bool {
        !self.access_key.is_empty() && !self.secret_key.is_empty() && !self.region.is_empty()
    }
}

lazy_static! {
    pub static ref SESSIONS: Arc<Mutex<HashMap<String, AwsSession>>> =
        Arc::new(Mutex::new(HashMap::new()));
}

pub fn register_aws_credentials(interp: &mut Interpreter) {
    /* -------------------------------------------------------------------- */
    /*  Modified wrapper – never returns Err(...) to the host               */
    /* -------------------------------------------------------------------- */
    let f = Arc::new(Mutex::new(|interp: &mut Interpreter, args: Vec<Value>| {
        if interp.is_verbose() {
            eprintln!("[aws:credentials] called with args: {:?}", args);
        }
        let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
            aws_credentials_function(interp, args)
        }));
        match res {
            Ok(Ok(v)) => Ok(v),
            Ok(Err(msg)) => Ok(make_error(format!("aws:credentials: {}", msg))),
            Err(e) => Ok(make_error(format!("aws:credentials: fatal plugin panic: {:?}", e))),
        }
    }));
    /* -------------------------------------------------------------------- */
    let info = DynamicFnInfo::new(f, false);
    interp.register_dynamic_function_ex("aws:credentials", info);
    interp.set_variable(
        "aws:credentials",
        Value::Function(Box::new(FunctionValue::Named("aws:credentials".to_string()))),
    );
}

pub fn aws_credentials_function(interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if interp.is_verbose() {
        eprintln!("[aws:credentials] ENTER, raw args: {:?}", args);
    }

    if args.len() != 1 {
        return Err("Expected a keyed array as a single argument. Required keys: accessKeyId, secretAccessKey, region.".to_string());
    }

    let arg = match &args[0] {
        Value::KeyedArray(map) => map,
        _ => {
            return Err("Expected a keyed array as the first argument. Required keys: accessKeyId, secretAccessKey, region.".to_string());
        }
    };

    let required_keys = ["accessKeyId", "secretAccessKey", "region"];
    for key in &required_keys {
        if !arg.contains_key(*key) {
            return Err(format!("Missing required key '{}'.", key));
        }
    }
    for key in arg.keys() {
        if !required_keys.contains(&key.as_str()) && key != "endpoint" {
            return Err(format!("Invalid key '{}'.", key));
        }
    }

    let access_key = match arg.get("accessKeyId") {
        Some(Value::SingleString(s)) => s.clone(),
        _ => return Err("Invalid or missing 'accessKeyId'.".to_string()),
    };
    let secret_key = match arg.get("secretAccessKey") {
        Some(Value::SingleString(s)) => s.clone(),
        _ => return Err("Invalid or missing 'secretAccessKey'.".to_string()),
    };
    let region = match arg.get("region") {
        Some(Value::SingleString(s)) => s.clone(),
        _ => return Err("Invalid or missing 'region'.".to_string()),
    };
    let endpoint = match arg.get("endpoint") {
        Some(Value::SingleString(s)) => Some(s.clone()),
        _ => None,
    };

    if interp.is_verbose() {
        eprintln!(
            "[aws:credentials] Using access_key={}, region={}, endpoint={:?}",
            access_key, region, endpoint
        );
    }

    let session = AwsSession {
        access_key,
        secret_key,
        region,
        endpoint,
    };

    let session_id = generate_short_uuid();
    let mut sessions = SESSIONS.lock().map_err(|_| "aws:credentials: session store unavailable")?;
    sessions.insert(session_id.clone(), session);

    Ok(Value::SingleString(session_id))
}
