// SPDX-License-Identifier: LGPL-3.0-or-later
// Ternlang — RFI-IRFOS Ternary Intelligence Stack
// Copyright (C) 2026 RFI-IRFOS
// Open-core compiler. See LICENSE-LGPL in the repository root.

//! ternlang-codegen — AST → C transpiler backend.
//!
//! Converts a ternlang `Program` (produced by `ternlang-core::Parser`) into
//! a valid, self-contained C source file that can be compiled with any C11
//! compiler.
//!
//! # Ternary representation
//! Trits are represented as `int8_t` with values `-1`, `0`, `+1`.
//! The generated file includes a small header of inline trit primitives so it
//! has no external dependencies beyond `<stdint.h>` and `<stdio.h>`.
//!
//! # Usage
//! ```rust
//! use ternlang_codegen::CTranspiler;
//! use ternlang_core::{Parser, StdlibLoader};
//!
//! let src = r#"fn main() -> trit { return consensus(1, -1); }"#;
//! let mut parser = ternlang_core::Parser::new(src);
//! let mut prog = parser.parse_program().unwrap();
//! StdlibLoader::resolve(&mut prog);
//!
//! let c_src = CTranspiler::new().emit(&prog);
//! println!("{c_src}");
//! ```

use ternlang_core::ast::*;

// ─── C file header (trit primitives) ─────────────────────────────────────────

const C_HEADER: &str = r#"/* Generated by ternlang-codegen — do not edit. */
#include <stdint.h>
#include <stdio.h>

typedef int8_t trit;

/* Ternary primitives */
static inline trit trit_neg(trit a) { return (trit)(-a); }
static inline trit trit_add(trit a, trit b) {
    int s = (int)a + (int)b;
    if (s > 1)  return  1;
    if (s < -1) return -1;
    return (trit)s;
}
static inline trit trit_mul(trit a, trit b)       { return (trit)((int)a * (int)b); }
static inline trit trit_consensus(trit a, trit b) { return trit_add(a, b); }
static inline trit trit_invert(trit a)             { return trit_neg(a); }
static inline trit trit_truth()    { return  1; }
static inline trit trit_hold()     { return  0; }
static inline trit trit_conflict() { return -1; }
static inline trit trit_abs(trit a){ return a < 0 ? (trit)-a : a; }
static inline trit trit_min(trit a, trit b) { return a < b ? a : b; }
static inline trit trit_max(trit a, trit b) { return a > b ? a : b; }

"#;

// ─── Transpiler ──────────────────────────────────────────────────────────────

pub struct CTranspiler {
    indent: usize,
    output: String,
}

impl CTranspiler {
    pub fn new() -> Self {
        Self { indent: 0, output: String::new() }
    }

    /// Transpile a `Program` to a complete C source string.
    pub fn emit(mut self, program: &Program) -> String {
        self.output.push_str(C_HEADER);

        // Forward-declare structs
        for s in &program.structs {
            self.emit_struct_decl(s);
        }
        if !program.structs.is_empty() { self.output.push('\n'); }

        // Forward-declare all functions (needed for mutual recursion)
        for f in &program.functions {
            self.emit_fn_forward(f);
        }
        if !program.functions.is_empty() { self.output.push('\n'); }

        // Full function bodies
        for f in &program.functions {
            self.emit_function(f);
            self.output.push('\n');
        }

        // C entry point: call ternlang main() and print the trit result
        self.output.push_str(
            "int main(void) {\n    trit result = tern_main();\n    printf(\"trit: %d\\n\", (int)result);\n    return result == -1 ? 1 : 0;\n}\n"
        );

        self.output
    }

    // ── Struct declarations ───────────────────────────────────────────────────

    fn emit_struct_decl(&mut self, s: &StructDef) {
        self.push(&format!("typedef struct {{\n"));
        self.indent += 1;
        for (field, ty) in &s.fields {
            self.push(&format!("{} {};\n", self.c_type(ty), field));
        }
        self.indent -= 1;
        self.push(&format!("}} {};\n", s.name));
    }

    // ── Function forward declarations ─────────────────────────────────────────

    fn emit_fn_forward(&mut self, f: &Function) {
        let params = self.c_params(&f.params);
        let name   = self.mangle_name(&f.name);
        self.push(&format!("{} {}({});\n", self.c_type(&f.return_type), name, params));
    }

    // ── Function bodies ───────────────────────────────────────────────────────

    fn emit_function(&mut self, f: &Function) {
        let params = self.c_params(&f.params);
        let name   = self.mangle_name(&f.name);
        self.push(&format!("{} {}({}) {{\n", self.c_type(&f.return_type), name, params));
        self.indent += 1;
        for stmt in &f.body {
            self.emit_stmt(stmt);
        }
        self.indent -= 1;
        self.push("}\n");
    }

    // ── Statements ────────────────────────────────────────────────────────────

    fn emit_stmt(&mut self, stmt: &Stmt) {
        match stmt {
            Stmt::Let { name, ty, value } => {
                let cty = self.c_type(ty);
                let val = self.emit_expr(value);
                self.push(&format!("{cty} {name} = {val};\n"));
            }
            Stmt::Return(expr) => {
                let val = self.emit_expr(expr);
                self.push(&format!("return {val};\n"));
            }
            Stmt::Expr(expr) => {
                let val = self.emit_expr(expr);
                self.push(&format!("{val};\n"));
            }
            Stmt::Block(stmts) => {
                self.push("{\n");
                self.indent += 1;
                for s in stmts { self.emit_stmt(s); }
                self.indent -= 1;
                self.push("}\n");
            }
            Stmt::IfTernary { condition, on_pos, on_zero, on_neg } => {
                let cond = self.emit_expr(condition);
                self.push(&format!("if ({cond} > 0) "));
                self.emit_stmt(on_pos);
                self.push("else if (0 == (int)");
                // re-emit condition (cheap for simple exprs)
                let cond2 = self.emit_expr(condition);
                self.push(&format!("{cond2}) "));
                self.emit_stmt(on_zero);
                self.push("else ");
                self.emit_stmt(on_neg);
            }
            Stmt::Match { condition, arms } => {
                let cond = self.emit_expr(condition);
                self.push(&format!("switch ((int){cond}) {{\n"));
                self.indent += 1;
                for (pattern, arm) in arms {
                    if matches!(pattern, Pattern::Wildcard) {
                        self.push("default: ");
                        self.emit_stmt(arm);
                        self.push("break;\n");
                        continue;
                    }
                    let val = match pattern {
                        Pattern::Int(v) => *v,
                        Pattern::Trit(t) => *t as i64,
                        Pattern::Float(f) => *f as i64,
                        Pattern::Wildcard => unreachable!(),
                    };
                    self.push(&format!("case {val}: "));
                    self.emit_stmt(arm);
                    self.push("break;\n");
                }
                self.indent -= 1;
                self.push("}\n");
            }
            Stmt::WhileTernary { condition, on_pos, on_zero, on_neg } => {
                let cond = self.emit_expr(condition);
                // Emit as a do-while that re-evaluates each iteration
                self.push(&format!("while (1) {{\n"));
                self.indent += 1;
                self.push(&format!("trit __cond = {cond};\n"));
                self.push("if (__cond > 0) ");
                self.emit_stmt(on_pos);
                self.push("else if (__cond == 0) ");
                self.emit_stmt(on_zero);
                self.push("else ");
                self.emit_stmt(on_neg);
                self.indent -= 1;
                self.push("}\n");
            }
            Stmt::Loop { body } => {
                self.push("for (;;) ");
                self.emit_stmt(body);
            }
            Stmt::ForIn { var, iter, body } => {
                // For simplicity: emit a comment and skip (tensor iteration requires heap)
                let iter_expr = self.emit_expr(iter);
                self.push(&format!("/* for {var} in {iter_expr}: tensor iteration omitted in C backend */\n"));
                let _ = body;
            }
            Stmt::Break    => self.push("break;\n"),
            Stmt::Continue => self.push("continue;\n"),
            Stmt::Use { .. } => { /* module imports resolved before codegen */ }
            Stmt::FromImport { .. } => { /* module imports resolved before codegen */ }
            Stmt::Send { target, message } => {
                let t = self.emit_expr(target);
                let m = self.emit_expr(message);
                self.push(&format!("/* send {m} to agent {t} — actor model not implemented in C backend */\n"));
            }
            Stmt::FieldSet { object, field, value } => {
                let val = self.emit_expr(value);
                self.push(&format!("{object}.{field} = {val};\n"));
            }
            Stmt::Decorated { stmt, .. } => self.emit_stmt(stmt),
            Stmt::IndexSet { object, row, col, value } => {
                let r = self.emit_expr(row);
                let c = self.emit_expr(col);
                let val = self.emit_expr(value);
                self.push(&format!("{object}[{r}][{c}] = {val};\n"));
            }
            Stmt::Set { name, value } => {
                let val = self.emit_expr(value);
                self.push(&format!("{name} = {val};\n"));
            }
        }
    }

    // ── Expressions → C string ────────────────────────────────────────────────

    fn emit_expr(&self, expr: &Expr) -> String {
        match expr {
            Expr::TritLiteral(v)    => format!("((trit){v})"),
            Expr::IntLiteral(v)     => format!("{v}"),
            Expr::StringLiteral(s)  => format!("\"{}\"", s.replace('"', "\\\"")),
            Expr::Ident(name)       => name.clone(),

            Expr::BinaryOp { op, lhs, rhs } => {
                let l = self.emit_expr(lhs);
                let r = self.emit_expr(rhs);
                match op {
                    BinOp::Add      => format!("trit_add({l}, {r})"),
                    BinOp::Sub      => format!("trit_add({l}, trit_neg({r}))"),
                    BinOp::Mul      => format!("trit_mul({l}, {r})"),
                    BinOp::Equal    => format!("trit_consensus({l}, {r})"),
                    BinOp::NotEqual => format!("trit_neg(trit_consensus({l}, {r}))"),
                    BinOp::And      => format!("trit_mul({l}, {r})"),
                    BinOp::Or       => format!("trit_consensus({l}, {r})"),
                    BinOp::Less        => format!("(({l}) < ({r}) ? 1 : (({l}) == ({r}) ? 0 : -1))"),
                    BinOp::Greater     => format!("(({l}) > ({r}) ? 1 : (({l}) == ({r}) ? 0 : -1))"),
                    BinOp::LessEqual   => format!("(({l}) <= ({r}) ? 1 : -1)"),
                    BinOp::GreaterEqual=> format!("(({l}) >= ({r}) ? 1 : -1)"),
                    BinOp::Div         => format!("(({l}) / ({r}))"),
                    BinOp::Mod         => format!("(({l}) % ({r}))"),
                }
            }

            Expr::UnaryOp { op: UnOp::Neg, expr } => {
                let inner = self.emit_expr(expr);
                format!("trit_neg({inner})")
            }

            Expr::Call { callee, args } => {
                let a: Vec<String> = args.iter().map(|a| self.emit_expr(a)).collect();
                let args_str = a.join(", ");
                // Map built-in names to C primitives
                match callee.as_str() {
                    "consensus" => format!("trit_consensus({args_str})"),
                    "invert"    => format!("trit_invert({args_str})"),
                    "truth"     => "trit_truth()".into(),
                    "hold"      => "trit_hold()".into(),
                    "conflict"  => "trit_conflict()".into(),
                    "abs"       => format!("trit_abs({args_str})"),
                    "min"       => format!("trit_min({args_str})"),
                    "max"       => format!("trit_max({args_str})"),
                    _           => format!("{}({args_str})", self.mangle_name(callee)),
                }
            }

            Expr::Cast { expr, .. } => self.emit_expr(expr),

            Expr::FieldAccess { object, field } => {
                let obj = self.emit_expr(object);
                format!("{obj}.{field}")
            }

            Expr::Propagate { expr } => {
                // `expr?` — emit inline early-return idiom via a GCC/Clang statement expression.
                // C doesn't have a built-in propagation operator, so we use a local variable.
                // The caller's emit_stmt wraps this in a let-binding, giving us the variable name.
                // Here we emit a ternary expression that the caller stores; the early-return
                // is approximated as: ((__prop = expr) == -1 ? (return -1, __prop) : __prop)
                // We emit a helper macro call instead for clarity.
                let inner = self.emit_expr(expr);
                format!("__TERN_PROPAGATE({inner})")
            }

            Expr::Spawn { agent_name, .. } =>
                format!("/* spawn {agent_name} — actor model not implemented in C backend */ 0"),
            Expr::Await { target } => {
                let t = self.emit_expr(target);
                format!("/* await {t} — actor model not implemented in C backend */ 0")
            }
            Expr::NodeId => "/* nodeid */ 0".into(),
            Expr::Index { object, row, col } => {
                let obj = self.emit_expr(object);
                let r = self.emit_expr(row);
                let c = self.emit_expr(col);
                format!("{obj}[{r}][{c}]")
            }
            Expr::FloatLiteral(v) => format!("{v}"),
            Expr::TritTensorLiteral(elems) => {
                let parts: Vec<String> = elems.iter().map(|e| e.to_string()).collect();
                format!("/* trittensor{{{}}} */ 0", parts.join(", "))
            }
            Expr::StructLiteral { name, fields } => {
                let f: Vec<String> = fields.iter()
                    .map(|(fname, val)| format!(".{fname} = {}", self.emit_expr(val)))
                    .collect();
                format!("({}){{ {} }}", name, f.join(", "))
            }
            Expr::Slice { object, .. } => {
                let obj = self.emit_expr(object);
                format!("/* slice on {obj} — zero-copy views not implemented in C backend */ {obj}")
            }
        }
    }

    // ── Helpers ───────────────────────────────────────────────────────────────

    fn c_type(&self, ty: &Type) -> &'static str {
        match ty {
            Type::Trit              => "trit",
            Type::Int               => "int64_t",
            Type::Bool              => "int8_t",
            Type::Float             => "double",
            Type::String            => "const char*",
            Type::TritTensor { .. } => "trit*",
            Type::IntTensor { .. }  => "int64_t*",
            Type::FloatTensor { .. } => "double*",
            Type::Named(_)          => "trit",  // struct handled separately
            Type::AgentRef          => "int",   // opaque ID in C backend
            Type::PackedTritTensor { .. } => "uint8_t*", // opaque packed buffer
        }
    }

    fn c_params(&self, params: &[(String, Type)]) -> String {
        if params.is_empty() {
            return "void".into();
        }
        params.iter()
            .map(|(name, ty)| format!("{} {name}", self.c_type(ty)))
            .collect::<Vec<_>>()
            .join(", ")
    }

    /// Map `main` → `tern_main` to avoid clashing with C's `main`.
    fn mangle_name(&self, name: &str) -> String {
        match name {
            "main" => "tern_main".into(),
            other  => other.to_string(),
        }
    }

    fn push(&mut self, s: &str) {
        let indent = "    ".repeat(self.indent);
        for line in s.split_inclusive('\n') {
            if line == "\n" {
                self.output.push('\n');
            } else {
                self.output.push_str(&indent);
                self.output.push_str(line);
            }
        }
    }
}

impl Default for CTranspiler {
    fn default() -> Self { Self::new() }
}

// ─── Tests ────────────────────────────────────────────────────────────────────

#[cfg(test)]
mod tests {
    use super::*;
    use ternlang_core::{Parser, StdlibLoader};

    fn transpile(src: &str) -> String {
        let mut parser = Parser::new(src);
        let mut prog = parser.parse_program().expect("parse failed");
        StdlibLoader::resolve(&mut prog);
        CTranspiler::new().emit(&prog)
    }

    #[test]
    fn emits_valid_c_header() {
        let c = transpile("fn main() -> trit { return 1; }");
        assert!(c.contains("typedef int8_t trit;"), "missing trit typedef");
        assert!(c.contains("trit_consensus"), "missing consensus primitive");
    }

    #[test]
    fn simple_return_emits_return_stmt() {
        let c = transpile("fn main() -> trit { return 1; }");
        assert!(c.contains("return 1;"), "missing return 1");
    }

    #[test]
    fn consensus_call_maps_to_primitive() {
        let c = transpile("fn main() -> trit { return consensus(1, -1); }");
        assert!(c.contains("trit_consensus("), "consensus not mapped to trit_consensus");
    }

    #[test]
    fn function_forward_declared() {
        let c = transpile("fn helper() -> trit { return 0; } fn main() -> trit { return helper(); }");
        // Forward declarations should appear before function bodies
        // Note: only "main" is mangled to "tern_main"; other functions keep their names.
        let fwd_pos  = c.find("trit helper(void);").unwrap_or(usize::MAX);
        let body_pos = c.find("trit helper(void) {").unwrap_or(usize::MAX);
        assert!(fwd_pos < body_pos, "forward declaration must precede body");
    }

    #[test]
    fn match_emits_switch() {
        let c = transpile(r#"
fn main() -> trit {
    let x: trit = 1;
    match x {
        1  => { return 1;  }
        0  => { return 0;  }
        -1 => { return -1; }
    }
}
"#);
        assert!(c.contains("switch"), "match should emit switch");
        assert!(c.contains("case 1:"),  "missing case 1");
        assert!(c.contains("case 0:"),  "missing case 0");
        assert!(c.contains("case -1:"), "missing case -1");
    }

    #[test]
    fn struct_emits_typedef_struct() {
        let c = transpile(r#"
struct Point { x: trit, y: trit }
fn main() -> trit { return 0; }
"#);
        assert!(c.contains("typedef struct {"), "missing typedef struct");
        assert!(c.contains("trit x;"), "missing field x");
    }

    #[test]
    fn c_entry_point_calls_tern_main() {
        let c = transpile("fn main() -> trit { return 1; }");
        assert!(c.contains("int main(void)"), "missing C main");
        assert!(c.contains("tern_main()"), "missing tern_main call");
    }

    #[test]
    fn propagate_emits_helper_macro() {
        let c = transpile(r#"
fn check() -> trit { return -1; }
fn main() -> trit { return check()?; }
"#);
        assert!(c.contains("__TERN_PROPAGATE"), "missing propagate macro");
    }

}
