//! 多模型路由代理：127.0.0.1 本地 HTTP 服务（tiny_http + ureq，贴合现有 std 线程模型，零新依赖）
//!
//! - POST /v1/messages：按 body.model 查路由表 → 命中则改写为真实模型名转发到对应接口；
//!   未命中 → 原样透传到默认接口（保留"直接填真实模型名"的活口）
//! - GET /v1/models：本地聚合（路由 alias + 各接口实名模型），display_name 标注来源接口
//! - HEAD /：Claude Code 启动健康检查，直接 200
//! - 其余路径：透传到默认接口（如 /v1/messages/count_tokens）
//! - 全部请求校验占位 token（Authorization: Bearer 或 x-api-key），防本机其他进程误用
//! - SSE 字节级透传（chunked），不解析事件；请求日志环形缓冲 200 条，key 永不入日志

use serde::Serialize;
use serde_json::{json, Value};
use std::collections::VecDeque;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, OnceLock, RwLock};
use std::time::Instant;

use crate::api_inject::{ApiEndpoint, RouteRule};

/// 日志环形缓冲上限
const LOG_CAP: usize = 200;
/// 保留别名：CLI 侧会抢先解析成官方完整模型 ID（实测 T1），禁止用作路由 alias
const RESERVED_ALIASES: [&str; 4] = ["sonnet", "opus", "haiku", "fable"];

/// 代理运行时快照（GUI 改配置后热更新，无需重启代理）
#[derive(Clone, Default)]
pub struct RouterSnapshot {
    pub token: String,
    pub routes: Vec<RouteRule>,
    pub endpoints: Vec<ApiEndpoint>,
    pub default_endpoint_id: String,
}

#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RequestLog {
    pub ts: String,
    pub method: String,
    pub url: String,
    /// 请求体里的原始模型名（非 messages 请求为空）
    pub model_in: String,
    /// 路由结果：接口名；未命中走默认接口时带 "(默认)" 后缀
    pub routed_to: String,
    /// 实际转发给上游的模型名
    pub model_out: String,
    /// 上游 HTTP 状态；0 = 网络错误
    pub status: u16,
    pub elapsed_ms: u128,
    pub error: String,
}

#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RouterStatus {
    pub running: bool,
    pub port: u16,
    pub log_count: usize,
}

struct Shared {
    snap: RwLock<RouterSnapshot>,
    logs: Mutex<VecDeque<RequestLog>>,
}

pub struct RouterProxy {
    shared: Arc<Shared>,
    running: AtomicBool,
    port: Mutex<u16>,
}

static PROXY: OnceLock<RouterProxy> = OnceLock::new();

fn proxy() -> &'static RouterProxy {
    PROXY.get_or_init(|| RouterProxy {
        shared: Arc::new(Shared {
            snap: RwLock::new(RouterSnapshot::default()),
            logs: Mutex::new(VecDeque::new()),
        }),
        running: AtomicBool::new(false),
        port: Mutex::new(0),
    })
}

/// alias 是否为 CLI 保留别名（大小写不敏感）
pub fn is_reserved_alias(alias: &str) -> bool {
    let a = alias.trim().to_ascii_lowercase();
    RESERVED_ALIASES.contains(&a.as_str())
}

/// 幂等启动代理；preferred 被占用时改用系统分配端口。返回实际绑定端口。
pub fn ensure_running(preferred_port: u16, snap: RouterSnapshot) -> Result<u16, String> {
    let p = proxy();
    *p.shared.snap.write().unwrap() = snap;
    if p.running.load(Ordering::SeqCst) {
        return Ok(*p.port.lock().unwrap());
    }
    let bind = |port: u16| tiny_http::Server::http(format!("127.0.0.1:{port}"));
    let server = match bind(preferred_port) {
        Ok(s) => s,
        Err(_) => bind(0).map_err(|e| format!("代理端口绑定失败: {e}"))?,
    };
    let actual: u16 = server
        .server_addr()
        .to_string()
        .rsplit(':')
        .next()
        .and_then(|s| s.parse().ok())
        .ok_or_else(|| "无法解析代理绑定端口".to_string())?;
    *p.port.lock().unwrap() = actual;
    p.running.store(true, Ordering::SeqCst);

    let shared = p.shared.clone();
    std::thread::spawn(move || {
        for req in server.incoming_requests() {
            let st = shared.clone();
            // 每请求一线程：并发量小（claude 会话级），阻塞模型下最简实现
            std::thread::spawn(move || handle_request(req, st));
        }
    });
    Ok(actual)
}

/// 热更新路由快照（代理未运行时仅更新快照，启动时生效）
pub fn update_snapshot(snap: RouterSnapshot) {
    *proxy().shared.snap.write().unwrap() = snap;
}

pub fn status() -> RouterStatus {
    let p = proxy();
    RouterStatus {
        running: p.running.load(Ordering::SeqCst),
        port: *p.port.lock().unwrap(),
        log_count: p.shared.logs.lock().unwrap().len(),
    }
}

pub fn logs() -> Vec<RequestLog> {
    proxy()
        .shared
        .logs
        .lock()
        .unwrap()
        .iter()
        .rev() // 最新在前
        .cloned()
        .collect()
}

fn push_log(shared: &Shared, log: RequestLog) {
    let mut logs = shared.logs.lock().unwrap();
    if logs.len() >= LOG_CAP {
        logs.pop_front();
    }
    logs.push_back(log);
}

fn now_ts() -> String {
    humantime::format_rfc3339_seconds(std::time::SystemTime::now()).to_string()
}

/// 上游 base 归一化：去掉尾部 `/`，若末尾是 `/v1` 则去掉（请求路径自带 /v1 前缀）
fn normalize_base(base: &str) -> String {
    let b = base.trim().trim_end_matches('/');
    b.strip_suffix("/v1").unwrap_or(b).to_string()
}

fn json_response(status: u16, body: Value) -> tiny_http::Response<std::io::Cursor<Vec<u8>>> {
    let raw = serde_json::to_vec(&body).unwrap_or_else(|_| b"{}".to_vec());
    tiny_http::Response::new(
        tiny_http::StatusCode(status),
        vec![tiny_http::Header::from_bytes(&b"Content-Type"[..], &b"application/json"[..]).unwrap()],
        std::io::Cursor::new(raw),
        None,
        None,
    )
}

fn handle_request(mut req: tiny_http::Request, shared: Arc<Shared>) {
    let started = Instant::now();
    let method = req.method().as_str().to_ascii_uppercase();
    // url 含 path + query（如 /v1/messages?beta=true），透传时原样拼接
    let url = req.url().to_string();
    let path = url.split('?').next().unwrap_or("/").to_string();

    // Claude Code 启动健康检查：HEAD /
    if method == "HEAD" {
        let _ = req.respond(tiny_http::Response::empty(200));
        return;
    }

    let snap = shared.snap.read().unwrap().clone();

    // 先提取全部需要的请求头为 owned 值（后续读 body 需要可变借用 req）
    let header = |name: &str| -> Option<String> {
        req.headers()
            .iter()
            .find(|h| h.field.as_str().as_str().eq_ignore_ascii_case(name))
            .map(|h| h.value.as_str().to_string())
    };
    let h_authorization = header("authorization");
    let h_x_api_key = header("x-api-key");
    let h_anthropic_version = header("anthropic-version");
    let h_anthropic_beta = header("anthropic-beta");

    // 占位 token 校验：Authorization: Bearer <token> 或 x-api-key: <token>
    let auth_ok = !snap.token.is_empty()
        && (h_authorization.as_deref() == Some(&format!("Bearer {}", snap.token))
            || h_x_api_key.as_deref() == Some(snap.token.as_str()));
    if !auth_ok {
        let _ = req.respond(json_response(
            401,
            json!({"type":"error","error":{"type":"authentication_error","message":"CCGUI 路由代理：token 校验失败（本服务仅接受 CCGUI 注入的占位 token）"}}),
        ));
        return;
    }

    // 读取请求体（非 messages 的其他 POST 也要透传原文）
    let mut body = String::new();
    if method == "POST" || method == "PUT" || method == "PATCH" {
        if req.as_reader().read_to_string(&mut body).is_err() {
            let _ = req.respond(json_response(
                400,
                json!({"type":"error","error":{"type":"invalid_request_error","message":"CCGUI 路由代理：请求体读取失败"}}),
            ));
            return;
        }
    }

    // GET /v1/models：本地聚合，不上游
    if method == "GET" && path == "/v1/models" {
        let _ = req.respond(json_response(200, aggregate_models(&snap)));
        return;
    }

    // 路由决策：messages 按 model 查表；其余一律默认接口
    let mut model_in = String::new();
    let mut model_out = String::new();
    let mut body_out = body.clone();
    let mut target_id = snap.default_endpoint_id.clone();
    let mut routed_via_default = true;

    if method == "POST" && path == "/v1/messages" {
        match serde_json::from_str::<Value>(&body) {
            Ok(mut j) => {
                if let Some(m) = j.get("model").and_then(Value::as_str) {
                    model_in = m.to_string();
                    if let Some(rule) = snap.routes.iter().find(|r| r.alias == m) {
                        target_id = rule.endpoint_id.clone();
                        routed_via_default = false;
                        model_out = rule.model.clone();
                        j["model"] = json!(rule.model);
                        body_out = j.to_string();
                    } else {
                        model_out = m.to_string();
                    }
                }
            }
            Err(_) => {
                let _ = req.respond(json_response(
                    400,
                    json!({"type":"error","error":{"type":"invalid_request_error","message":"CCGUI 路由代理：请求体不是合法 JSON"}}),
                ));
                return;
            }
        }
    }

    let Some(ep) = snap.endpoints.iter().find(|e| e.id == target_id) else {
        let _ = req.respond(json_response(
            502,
            json!({"type":"error","error":{"type":"api_error","message":"CCGUI 路由代理：目标接口不存在（可能已被删除），请到 API 注入页检查路由表与默认接口"}}),
        ));
        return;
    };

    // 转发上游
    let upstream = format!("{}{}", normalize_base(&ep.base_url), url);
    let mut call = upstream_agent().request(&method, &upstream);
    call = call
        .set("Authorization", &format!("Bearer {}", ep.key))
        .set("x-api-key", &ep.key)
        .set("Content-Type", "application/json")
        .set(
            "anthropic-version",
            &h_anthropic_version.unwrap_or_else(|| "2023-06-01".into()),
        );
    if let Some(beta) = h_anthropic_beta {
        call = call.set("anthropic-beta", &beta);
    }

    let result = if body_out.is_empty() {
        call.call()
    } else {
        call.send_string(&body_out)
    };

    let mut log = RequestLog {
        ts: now_ts(),
        method: method.clone(),
        url: url.chars().take(120).collect(),
        model_in,
        routed_to: if routed_via_default {
            format!("{}(默认)", ep.name)
        } else {
            ep.name.clone()
        },
        model_out,
        status: 0,
        elapsed_ms: 0,
        error: String::new(),
    };

    match result {
        // 2xx 与错误状态统一处理：ureq 的 Err(Status) 里也带响应体，原样透传给 claude
        Ok(resp) => {
            log.status = resp.status();
            respond_upstream(req, resp, &mut log, started, &shared);
        }
        Err(ureq::Error::Status(_, resp)) => {
            log.status = resp.status();
            respond_upstream(req, resp, &mut log, started, &shared);
        }
        Err(e) => {
            log.status = 0;
            log.elapsed_ms = started.elapsed().as_millis();
            log.error = format!("网络错误: {e}");
            push_log(&shared, log);
            let _ = req.respond(json_response(
                502,
                json!({"type":"error","error":{"type":"api_error","message":format!("CCGUI 路由代理：上游连接失败（{}）: {e}", ep.name)}}),
            ));
        }
    }
}

/// 把上游响应字节级透传给 claude（SSE 走 chunked，不缓冲整响应）
fn respond_upstream(
    req: tiny_http::Request,
    resp: ureq::Response,
    log: &mut RequestLog,
    started: Instant,
    shared: &Arc<Shared>,
) {
    let status = resp.status();
    let content_type = resp
        .header("content-type")
        .unwrap_or("application/json")
        .to_string();
    let reader = resp.into_reader();
    let mut headers = Vec::new();
    if let Ok(h) = tiny_http::Header::from_bytes(&b"Content-Type"[..], content_type.as_bytes()) {
        headers.push(h);
    }
    if let Ok(h) = tiny_http::Header::from_bytes(&b"Cache-Control"[..], &b"no-cache"[..]) {
        headers.push(h);
    }
    // data_length=None → chunked transfer，SSE 事件随到随发
    let response = tiny_http::Response::new(tiny_http::StatusCode(status), headers, reader, None, None);
    let io_result = req.respond(response);
    log.elapsed_ms = started.elapsed().as_millis();
    if let Err(e) = io_result {
        log.error = format!("响应写入失败: {e}");
    }
    push_log(shared, log.clone());
}

/// 聚合模型清单：路由 alias（display_name 标来源）+ 各接口实名模型（去重，alias 优先）
pub(crate) fn aggregate_models(snap: &RouterSnapshot) -> Value {
    let mut seen = std::collections::HashSet::new();
    let mut data: Vec<Value> = Vec::new();
    let ep_name = |id: &str| -> String {
        snap.endpoints
            .iter()
            .find(|e| e.id == id)
            .map(|e| e.name.clone())
            .unwrap_or_else(|| "?".into())
    };
    let mut push = |id: String, from: String| {
        if seen.insert(id.clone()) {
            data.push(json!({
                "type": "model",
                "id": id,
                "display_name": format!("{id}（{from}）"),
                "created_at": "2025-01-01T00:00:00Z"
            }));
        }
    };
    for r in &snap.routes {
        push(r.alias.clone(), ep_name(&r.endpoint_id));
    }
    for ep in &snap.endpoints {
        for m in &ep.models {
            push(m.clone(), ep.name.clone());
        }
    }
    json!({
        "data": data,
        "has_more": false,
        "first_id": data.first().and_then(|m| m.get("id").cloned()).unwrap_or(json!("")),
        "last_id": data.last().and_then(|m| m.get("id").cloned()).unwrap_or(json!("")),
    })
}

/// 上游连接复用 Agent：连接超时 30s；读超时 300s（SSE 有 ping/增量保活，
/// 300s 无任何字节视为连接已死——不设总超时，防止挂死上游让请求线程永久泄漏）
fn upstream_agent() -> &'static ureq::Agent {
    static AGENT: OnceLock<ureq::Agent> = OnceLock::new();
    AGENT.get_or_init(|| {
        ureq::AgentBuilder::new()
            .timeout_connect(std::time::Duration::from_secs(30))
            .timeout_read(std::time::Duration::from_secs(300))
            .build()
    })
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn normalize_base_strips_trailing_v1() {
        assert_eq!(normalize_base("https://a.com/"), "https://a.com");
        assert_eq!(normalize_base("https://a.com/v1"), "https://a.com");
        assert_eq!(normalize_base("https://a.com/v1/"), "https://a.com");
        assert_eq!(normalize_base("https://a.com/coding"), "https://a.com/coding");
    }

    #[test]
    fn reserved_alias_case_insensitive() {
        assert!(is_reserved_alias("sonnet"));
        assert!(is_reserved_alias("Opus"));
        assert!(is_reserved_alias("HAIKU"));
        assert!(!is_reserved_alias("glm-cheap"));
    }

    #[test]
    fn aggregate_dedup_alias_first() {
        let snap = RouterSnapshot {
            token: "t".into(),
            routes: vec![RouteRule {
                id: "r1".into(),
                alias: "glm-cheap".into(),
                endpoint_id: "a".into(),
                model: "glm-4.5-air".into(),
            }],
            endpoints: vec![ApiEndpoint {
                id: "a".into(),
                name: "甲接口".into(),
                base_url: "https://a.com".into(),
                key: "k".into(),
                model: "glm-4.5".into(),
                models: vec!["glm-4.5".into(), "glm-cheap".into()],
            }],
            default_endpoint_id: "a".into(),
        };
        let out = aggregate_models(&snap);
        let ids: Vec<&str> = out["data"]
            .as_array()
            .unwrap()
            .iter()
            .map(|m| m["id"].as_str().unwrap())
            .collect();
        // alias 在前，实名 glm-4.5 次之，与 alias 同名的 glm-cheap 被去重
        assert_eq!(ids, vec!["glm-cheap", "glm-4.5"]);
    }

    /// 端到端：假上游 + 真代理 + 真客户端，验证路由命中、模型改写、key 替换、query 透传、SSE 字节完整
    #[test]
    fn end_to_end_routes_and_rewrites_model() {
        // 假上游：记录请求并返回一段 SSE
        let upstream = tiny_http::Server::http("127.0.0.1:0").unwrap();
        let upstream_port: u16 = upstream
            .server_addr()
            .to_string()
            .rsplit(':')
            .next()
            .unwrap()
            .parse()
            .unwrap();
        let seen = Arc::new(Mutex::new(Vec::<(String, String, String)>::new()));
        let seen2 = seen.clone();
        std::thread::spawn(move || {
            for mut req in upstream.incoming_requests() {
                let url = req.url().to_string();
                let auth = req
                    .headers()
                    .iter()
                    .find(|h| h.field.as_str().as_str().eq_ignore_ascii_case("authorization"))
                    .map(|h| h.value.as_str().to_string())
                    .unwrap_or_default();
                let mut body = String::new();
                let _ = req.as_reader().read_to_string(&mut body);
                seen2.lock().unwrap().push((url, auth, body));
                let sse = "data: {\"type\":\"message_start\"}\n\nevent: message_stop\ndata: {\"type\":\"message_stop\"}\n\n";
                let resp = tiny_http::Response::new(
                    tiny_http::StatusCode(200),
                    vec![tiny_http::Header::from_bytes(
                        &b"Content-Type"[..],
                        &b"text/event-stream"[..],
                    )
                    .unwrap()],
                    std::io::Cursor::new(sse.as_bytes().to_vec()),
                    None,
                    None,
                );
                let _ = req.respond(resp);
            }
        });

        let snap = RouterSnapshot {
            token: "tok-abc".into(),
            routes: vec![RouteRule {
                id: "r1".into(),
                alias: "alias-x".into(),
                endpoint_id: "e1".into(),
                model: "real-model-x".into(),
            }],
            endpoints: vec![ApiEndpoint {
                id: "e1".into(),
                name: "甲接口".into(),
                base_url: format!("http://127.0.0.1:{upstream_port}"),
                key: "ep-secret-key".into(),
                model: String::new(),
                models: vec![],
            }],
            default_endpoint_id: "e1".into(),
        };
        let port = ensure_running(0, snap).unwrap();

        // 无 token：应 401
        let r401 = ureq::post(&format!("http://127.0.0.1:{port}/v1/messages"))
            .send_json(json!({"model":"alias-x","messages":[]}));
        assert!(matches!(r401, Err(ureq::Error::Status(401, _))));

        // 正确 token + 命中路由 + query 透传
        let resp = ureq::post(&format!("http://127.0.0.1:{port}/v1/messages?beta=true"))
            .set("Authorization", "Bearer tok-abc")
            .send_json(json!({"model":"alias-x","stream":true,"messages":[]}));
        let text = resp.expect("代理应返回 200").into_string().unwrap();
        assert!(text.contains("message_stop"), "SSE 内容应完整透传: {text}");

        let seen = seen.lock().unwrap();
        let (url, auth, body) = &seen[0];
        assert_eq!(url, "/v1/messages?beta=true", "query 应原样透传");
        assert_eq!(auth, "Bearer ep-secret-key", "上游应收到接口真实 key");
        let j: Value = serde_json::from_str(body).unwrap();
        assert_eq!(j["model"], "real-model-x", "模型名应被改写为真实模型");
    }
}
