use lichen_core::ContractAbi;

use crate::codegen_shared_support::{clean_param_name, is_readonly, py_type, to_pascal_case};

/// Generate a Python client class from a contract ABI
pub(super) fn generate_python(abi: &ContractAbi) -> String {
    let class_name = format!("{}Client", to_pascal_case(&abi.name));
    let mut out = String::with_capacity(4096);

    out.push_str(&format!(
        "# Auto-generated by: lichen contract generate-client\n\
         # Contract: {} | ABI version: {}\n\
         # DO NOT EDIT — regenerate with: lichen contract generate-client --abi {}/abi.json\n\n\
         from lichen_sdk import Connection, Keypair\n\n\n",
        abi.name, abi.version, abi.name,
    ));

    out.push_str(&format!("class {}:\n", class_name));
    out.push_str("    def __init__(self, connection: Connection, contract_address: str):\n");
    out.push_str("        self.connection = connection\n");
    out.push_str("        self.contract_address = contract_address\n");

    for function in &abi.functions {
        let readonly = is_readonly(function);

        let mut params = vec!["self".to_string()];
        if !readonly {
            params.push("signer: Keypair".to_string());
        }
        for parameter in &function.params {
            let clean = clean_param_name(&parameter.name);
            params.push(format!("{}: {}", clean, py_type(&parameter.param_type)));
        }

        let args: Vec<String> = function
            .params
            .iter()
            .map(|parameter| {
                let clean = clean_param_name(&parameter.name);
                format!("'{}': {}", parameter.name, clean)
            })
            .collect();

        let return_hint = if readonly { "-> any" } else { "-> str" };

        let param_desc: Vec<String> = function
            .params
            .iter()
            .map(|parameter| format!("{}: {:?}", parameter.name, parameter.param_type))
            .collect();
        out.push_str(&format!(
            "\n    def {}({}) {}:\n",
            function.name,
            params.join(", "),
            return_hint,
        ));
        out.push_str(&format!(
            "        \"\"\"{}({})\"\"\"\n",
            function.name,
            param_desc.join(", "),
        ));

        let call_method = if readonly {
            "query_contract"
        } else {
            "call_contract"
        };

        if readonly {
            out.push_str(&format!(
                "        return self.connection.{}(\n\
                 \x20           self.contract_address,\n\
                 \x20           '{}',\n\
                 \x20           {{{}}},\n\
                 \x20       )\n",
                call_method,
                function.name,
                args.join(", "),
            ));
        } else {
            out.push_str(&format!(
                "        return self.connection.{}(\n\
                 \x20           signer,\n\
                 \x20           self.contract_address,\n\
                 \x20           '{}',\n\
                 \x20           {{{}}},\n\
                 \x20       )\n",
                call_method,
                function.name,
                args.join(", "),
            ));
        }
    }

    out
}
