/// haiku_poc — librings proof-of-concept
///
/// Three rings. Three gates. Each gate that opens reveals a line.
///
///   "Carbon in our pies"
///   "iron, calcium, all forged"
///   "in the death of stars"
///
/// Run with:  cargo run --example haiku_poc
///            cargo run --features cirt --example haiku_poc

use std::sync::{Arc, Mutex};
use bytes::Bytes;
use librings::audit::{AuditEntry, AuditSink, AuditError, GateOutcome};
use librings::pipeline::{Ring, RingInput, RingPipeline, PipelineResult};
use librings::test_types::Assertion;

// ── Haiku ────────────────────────────────────────────────────────────────────

const HAIKU: [&str; 3] = [
    "Carbon in our pies",
    "iron, calcium, all forged",
    "in the death of stars",
];

// ── Live audit sink — captures entries and prints haiku lines on gate pass ──

struct HaikuSink {
    entries: Mutex<Vec<AuditEntry>>,
}

impl HaikuSink {
    fn new() -> Self {
        Self { entries: Mutex::new(Vec::new()) }
    }

}

impl AuditSink for HaikuSink {
    fn record(&self, entry: AuditEntry) -> Result<(), AuditError> {
        if let GateOutcome::Pass = entry.outcome {
            let ring_id = entry.ring_id as usize;
            if ring_id < HAIKU.len() {
                println!(
                    "\n  ┌─ Ring {} gate opens ──────────────────────────────────",
                    ring_id
                );
                println!("  │  \"{}\"", HAIKU[ring_id]);
                println!(
                    "  │  nonce  {:.16}…",
                    &entry.nonce_hex[..entry.nonce_hex.len().min(16)]
                );
                println!(
                    "  │  B      {:.16}…",
                    &entry.b_hex[..entry.b_hex.len().min(16)]
                );
                println!("  └──────────────────────────────────────────────────────");
            }
        }
        self.entries.lock().expect("lock").push(entry);
        Ok(())
    }
}

// ── SHA-256 helper ────────────────────────────────────────────────────────────

fn sha256(data: &[u8]) -> [u8; 32] {
    use sha2::Digest;
    let mut h = sha2::Sha256::new();
    h.update(data);
    h.finalize().into()
}

fn hex(b: &[u8]) -> String {
    b.iter().fold(String::new(), |mut s, byte| {
        use std::fmt::Write;
        write!(s, "{:02x}", byte).unwrap();
        s
    })
}

// ── Pipeline construction ─────────────────────────────────────────────────────

fn build_pipeline(sink: Arc<dyn AuditSink>) -> RingPipeline {
    // Seed: SHA-256("librings haiku poc")
    let seed = sha256(b"librings haiku poc");

    // Ring 0 — pass if input is non-empty (existence check)
    let ring0 = Ring {
        id: 0,
        test: Box::new(Assertion {
            test_fn_id: 0,
            test_fn: Box::new(|output: &Bytes| !output.is_empty()),
        }),
        worker_count: 1,
        work_fn: Box::new(|input: RingInput| {
            // Append ring 0 stamp
            let mut out = input.data.to_vec();
            out.extend_from_slice(b" [ring:0]");
            Bytes::from(out)
        }),
    };

    // Ring 1 — pass if the payload contains the word "starstuff"
    let ring1 = Ring {
        id: 1,
        test: Box::new(Assertion {
            test_fn_id: 1,
            test_fn: Box::new(|output: &Bytes| {
                String::from_utf8_lossy(output).contains("starstuff")
            }),
        }),
        worker_count: 1,
        work_fn: Box::new(|input: RingInput| {
            // Enrich: append "starstuff" provenance note
            let mut out = input.data.to_vec();
            out.extend_from_slice(b" starstuff [ring:1]");
            Bytes::from(out)
        }),
    };

    // Ring 2 — pass if the payload has been through both prior rings
    let ring2 = Ring {
        id: 2,
        test: Box::new(Assertion {
            test_fn_id: 2,
            test_fn: Box::new(|output: &Bytes| {
                let s = String::from_utf8_lossy(output);
                s.contains("[ring:0]") && s.contains("[ring:1]")
            }),
        }),
        worker_count: 1,
        work_fn: Box::new(|input: RingInput| {
            let mut out = input.data.to_vec();
            out.extend_from_slice(b" [ring:2]");
            Bytes::from(out)
        }),
    };

    RingPipeline::new(seed)
        .with_audit(sink)
        .add_ring(ring0)
        .add_ring(ring1)
        .add_ring(ring2)
}

// ── Main ──────────────────────────────────────────────────────────────────────

fn main() {
    let input = Bytes::from_static(
        b"We are made of starstuff -- the nitrogen in our DNA, \
          the calcium in our teeth, the iron in our blood, \
          the carbon in our apple pies."
    );

    println!();
    println!("  librings — RINGS Isolating Nested Gated Security");
    println!("  ─────────────────────────────────────────────────");
    println!("  Input  ({} bytes):", input.len());
    println!("  \"{}…\"", &String::from_utf8_lossy(&input)[..60]);
    println!();
    println!("  Executing 3-ring cryptographic gate pipeline…");

    let sink = Arc::new(HaikuSink::new());
    let pipeline = build_pipeline(Arc::clone(&sink) as Arc<dyn AuditSink>);

    let result = pipeline.execute(input);

    println!();
    match result {
        PipelineResult::Success {
            output,
            final_nonce,
            execution_counter,
            audit_entries,
            capabilities,
        } => {
            println!("  ══════════════════════════════════════════════════════");
            println!("  PIPELINE SUCCESS");
            println!("  ══════════════════════════════════════════════════════");
            println!();
            println!("  Output ({} bytes):", output.len());
            println!("  \"{}\"", String::from_utf8_lossy(&output));
            println!();
            println!("  Execution counter : {}", execution_counter);
            println!("  Final nonce       : {}…", &hex(&final_nonce)[..32]);
            println!();
            println!("  Audit trail ({} entries):", audit_entries.len());
            for entry in &audit_entries {
                println!(
                    "    ring={} outcome={:?} nonce={:.16}…",
                    entry.ring_id,
                    entry.outcome,
                    &entry.nonce_hex[..entry.nonce_hex.len().min(16)]
                );
            }
            println!();
            println!("  Capabilities issued ({}):", capabilities.len());
            for cap in &capabilities {
                let expiry = cap.expires_at_ms
                    .map(|e| format!("expires_at={}", e))
                    .unwrap_or_else(|| "no expiry".to_string());
                println!(
                    "    ring={} scope={:?} {}",
                    cap.ring_id, cap.scope, expiry
                );
            }
            println!();
            println!("  ─────────────────────────────────────────────────────");
            println!("  The poem, complete:");
            for line in &HAIKU {
                println!("    \"{}\"", line);
            }
            println!("  ─────────────────────────────────────────────────────");
            println!();
        }
        PipelineResult::Failure { failed_at_ring, reason } => {
            eprintln!("  PIPELINE FAILED at ring {}: {:?}", failed_at_ring, reason);
            std::process::exit(1);
        }
    }
}
