//! Daemon mode — stdin/stdout JSON API.
//!
//! Pipe-friendly protocol: one JSON object per line.
//! Suitable for MCP server wrappers, shell scripts, and inter-process calls.
//!
//! # Protocol
//!
//! ```text
//! Request:   {"tool":"ohm","current":2,"resistance":4}
//! Response:  {"tool":"ohm","power":"16 Watts(W)","current":"2 Amps(A)",...}
//!
//! {"tool":"list"}                                          → available tools
//! {"tool":"help","name":"ohm"}                             → parameter docs
//! {"tool":"ohm","current":2,"resistance":4}                → calculate
//! ```

use crate::calc::*;
use serde_json::{json, Value};
use std::collections::BTreeMap;
use std::io::{self, BufRead, Write};

/// Start the daemon event loop. Blocks until stdin closes.
pub fn run() {
    eprintln!("MCP-Relational-Data daemon v2.1.0 — stdin/stdout JSON");
    eprintln!("  {{\"tool\":\"list\"}}           available tools");
    eprintln!("  {{\"tool\":\"help\",\"name\":\"ohm\"}}  parameter docs");
    eprintln!("  {{\"tool\":\"ohm\",...}}         run a calculation");

    let stdin = io::stdin();
    let mut stdout = io::stdout();

    for line in stdin.lock().lines() {
        let input = match line {
            Ok(l) => l,
            Err(e) => {
                eprintln!("stdin error: {}", e);
                break;
            }
        };

        let trimmed = input.trim();
        if trimmed.is_empty() {
            continue;
        }

        let response = handle(trimmed);
        let output = serde_json::to_string(&response).unwrap_or_else(|e| {
            json!({"error": e.to_string()}).to_string()
        });

        let _ = writeln!(stdout, "{}", output);
        let _ = stdout.flush();
    }
}

// ── dispatch ─────────────────────────────────────────────────────────

fn handle(input: &str) -> Value {
    let req: Value = match serde_json::from_str(input) {
        Ok(v) => v,
        Err(e) => return json!({"error": format!("invalid JSON: {}", e)}),
    };

    let tool = match req.get("tool").and_then(|v| v.as_str()) {
        Some(t) => t,
        None => return json!({"error": "missing \"tool\" field"}),
    };

    match tool {
        "list"        => json!({"tools": [
            "ohm", "margin", "roi",
            "token_cost", "electricity", "break_even"
        ]}),
        "help"        => tool_help(
            req.get("name").and_then(|v| v.as_str()).unwrap_or(""),
        ),
        "ohm"          => do_ohm(&req),
        "margin"       => do_margin(&req),
        "roi"          => do_roi(&req),
        "token_cost"   => do_token(&req),
        "electricity"  => do_electricity(&req),
        "break_even"   => do_break_even(&req),
        _ => json!({"error": format!(
            "unknown tool: \"{}\". Send {{\"tool\":\"list\"}} for options.", tool
        )}),
    }
}

// ── per-tool handlers ────────────────────────────────────────────────

fn get_f64(req: &Value, key: &str) -> Option<f64> {
    req.get(key).and_then(|v| v.as_f64())
}

fn do_ohm(req: &Value) -> Value {
    let p = get_f64(req, "power").unwrap_or(0.0);
    let i = get_f64(req, "current").unwrap_or(0.0);
    let r = get_f64(req, "resistance").unwrap_or(0.0);
    let v = get_f64(req, "voltage").unwrap_or(0.0);

    if p < 0.0 || r < 0.0 {
        return json!({"error": "power and resistance must be non-negative"});
    }

    let (p, i, r, v) = ohm_calculate(p, i, r, v);
    let mut m: BTreeMap<String, Value> = BTreeMap::new();
    m.insert("tool".into(), json!("ohm"));
    m.insert("power".into(),     json!(fmt_eng(p, "Watts(W)")));
    m.insert("current".into(),   json!(fmt_eng(i, "Amps(A)")));
    m.insert("resistance".into(), json!(fmt_eng(r, "Ohms(\u{03A9})")));
    m.insert("voltage".into(),   json!(fmt_eng(v, "Volts(V)")));
    json!(m)
}

fn do_margin(req: &Value) -> Value {
    let cost = get_f64(req, "cost").unwrap_or(0.0);
    let sell = get_f64(req, "sell").unwrap_or(0.0);
    let (profit, margin, markup) = margin_calculate(cost, sell);

    let mut m: BTreeMap<String, Value> = BTreeMap::new();
    m.insert("tool".into(),   json!("margin"));
    m.insert("profit".into(), json!(fmt_num(profit)));
    m.insert("margin".into(), json!(fmt_pct(margin)));
    m.insert("markup".into(), json!(fmt_pct(markup)));
    json!(m)
}

fn do_roi(req: &Value) -> Value {
    let inv = get_f64(req, "investment").unwrap_or(0.0);
    let rev = get_f64(req, "revenue").unwrap_or(0.0);
    let (profit, roi) = roi_calculate(inv, rev);

    let mut m: BTreeMap<String, Value> = BTreeMap::new();
    m.insert("tool".into(),   json!("roi"));
    m.insert("profit".into(), json!(fmt_num(profit)));
    m.insert("roi".into(),    json!(fmt_pct(roi)));
    json!(m)
}

fn do_token(req: &Value) -> Value {
    let model_name = req.get("model").and_then(|v| v.as_str()).unwrap_or("gpt-4o");
    let model = parse_model(model_name).unwrap_or(AiModel::Gpt4o);
    let in_tok  = get_f64(req, "input_tokens").unwrap_or(0.0);
    let out_tok = get_f64(req, "output_tokens").unwrap_or(0.0);
    let (in_cost, out_cost, total) = token_cost_calculate(model, in_tok, out_tok);

    let mut m: BTreeMap<String, Value> = BTreeMap::new();
    m.insert("tool".into(),        json!("token_cost"));
    m.insert("model".into(),       json!(model.label()));
    m.insert("input_cost".into(),  json!(fmt_dollar(in_cost)));
    m.insert("output_cost".into(), json!(fmt_dollar(out_cost)));
    m.insert("total_cost".into(),  json!(fmt_dollar(total)));
    json!(m)
}

fn do_electricity(req: &Value) -> Value {
    let watts = get_f64(req, "watts").unwrap_or(0.0);
    let hours = get_f64(req, "hours").unwrap_or(0.0);
    let days  = get_f64(req, "days").unwrap_or(30.0);
    let rate  = get_f64(req, "rate").unwrap_or(0.0);
    let (daily, monthly_kwh, monthly_cost, yearly) =
        electricity_calculate(watts, hours, days, rate);

    let mut m: BTreeMap<String, Value> = BTreeMap::new();
    m.insert("tool".into(),         json!("electricity"));
    m.insert("daily_kwh".into(),    json!(fmt_eng(daily, "kWh")));
    m.insert("monthly_kwh".into(),  json!(fmt_eng(monthly_kwh, "kWh")));
    m.insert("monthly_cost".into(), json!(fmt_dollar(monthly_cost)));
    m.insert("yearly_cost".into(),  json!(fmt_dollar(yearly)));
    json!(m)
}

fn do_break_even(req: &Value) -> Value {
    let fixed    = get_f64(req, "fixed").unwrap_or(0.0);
    let price    = get_f64(req, "price").unwrap_or(0.0);
    let variable = get_f64(req, "variable").unwrap_or(0.0);
    let (units, revenue) = break_even_calculate(fixed, price, variable);

    let mut m: BTreeMap<String, Value> = BTreeMap::new();
    m.insert("tool".into(),    json!("break_even"));
    m.insert("units".into(),   json!(fmt_num(units)));
    m.insert("revenue".into(), json!(fmt_dollar(revenue)));
    json!(m)
}

// ── help metadata ────────────────────────────────────────────────────

fn tool_help(name: &str) -> Value {
    let (desc, params) = match name {
        "ohm" => (
            "Ohm's Law & Watts Law — enter any 2 of 4 values to solve for the other 2. \
             Applies to circuit design, audio impedance matching, MOSFET PSU design, \
             and LED driver sizing.",
            vec!["power", "current", "resistance", "voltage"],
        ),
        "margin" => (
            "Margin & Markup — calculate profit, margin %, and markup % \
             from cost and selling price.",
            vec!["cost", "sell"],
        ),
        "roi" => (
            "Return on Investment — measure profitability of ad spend, \
             marketing campaigns, and capital expenditures.",
            vec!["investment", "revenue"],
        ),
        "token_cost" => (
            "AI Token Cost — estimate LLM API costs per million tokens \
             (GPT-4o, Claude 3.5 Sonnet, etc.).",
            vec!["model", "input_tokens", "output_tokens"],
        ),
        "electricity" => (
            "Electricity Cost — project monthly and yearly power costs \
             for GPU servers, datacenter racks, and mining rigs.",
            vec!["watts", "hours", "days", "rate"],
        ),
        "break_even" => (
            "Break-Even Analysis — how many units must you sell to cover \
             all fixed and variable costs?",
            vec!["fixed", "price", "variable"],
        ),
        _ => return json!({"error": format!(
            "unknown tool: \"{}\". Send {{\"tool\":\"list\"}} for options.", name
        )}),
    };

    json!({ "name": name, "description": desc, "params": params })
}