use crate::app::AppState;
use crate::context::RequestContext;
use crate::error::WebError;
use axum::extract::{Extension, State};
use axum::http::{HeaderMap, HeaderName, HeaderValue, StatusCode};
use axum::Json;
use bridge_runtime::{
    build_failure_response, build_success_response, execute_runtime_input, execute_runtime_query,
    runtime_input_requests_result, RuntimeInput,
};
use serde::Serialize;
use serde_json::{json, Value};

const HEADER_MNOTE_WEB_OWNER: &str = "x-mnote-web-owner";
const HEADER_AI_BRIDGE_OWNER: &str = "x-mnote-ai-bridge-owner";

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct HermesHealthResponse {
    pub ok: bool,
    pub service: String,
    pub bridge: &'static str,
    pub request_id: String,
    pub trace_id: String,
}

pub async fn health(
    State(state): State<AppState>,
    Extension(context): Extension<RequestContext>,
) -> Json<HermesHealthResponse> {
    Json(HermesHealthResponse {
        ok: true,
        service: state.config().service_name.clone(),
        bridge: "hermes",
        request_id: context.trace.request_id,
        trace_id: context.trace.trace_id,
    })
}

pub async fn bridge_runtime(
    Extension(context): Extension<RequestContext>,
    Json(payload): Json<Value>,
) -> Result<(StatusCode, HeaderMap, Json<Value>), WebError> {
    let session_id = format!("hermes_{}", context.trace.request_id);
    let runtime_input = match serde_json::from_value::<RuntimeInput>(payload.clone()) {
        Ok(runtime_input) => runtime_input,
        Err(_) => {
            return Ok((
                StatusCode::OK,
                stamp_ai_bridge_headers(),
                Json(json!({
                    "ok": true,
                    "bridge": "hermes_session",
                    "sessionId": session_id,
                    "eventStreamEndpoint": format!("/api/hermes/events/{}", context.trace.request_id),
                    "canonicalRoute": "/api/hermes/bridge",
                    "requestId": context.trace.request_id,
                    "traceId": context.trace.trace_id,
                    "contract": ai_bridge_contract(&session_id),
                    "structuredWrite": {
                        "owner": "rust-web-hermes",
                        "allowedCommands": [
                            "page.body.save",
                            "tree.node.create",
                            "kernel.edge.attach"
                        ]
                    },
                    "compatPayload": payload,
                })),
            ));
        }
    };
    let payload = if runtime_input_requests_result(&runtime_input) {
        match execute_runtime_query(runtime_input) {
            Ok(result) => json!({
                "ok": true,
                "bridge": "hermes_runtime_result",
                "sessionId": session_id,
                "eventStreamEndpoint": format!("/api/hermes/events/{}", context.trace.request_id),
                "canonicalRoute": "/api/hermes/bridge",
                "requestId": context.trace.request_id,
                "traceId": context.trace.trace_id,
                "contract": ai_bridge_contract(&session_id),
                "result": result,
            }),
            Err(error) => {
                let failure = build_failure_response(error);
                return Ok((
                    StatusCode::BAD_REQUEST,
                    stamp_ai_bridge_headers(),
                    Json(serde_json::to_value(failure).expect("runtime failure 应可序列化")),
                ));
            }
        }
    } else {
        match execute_runtime_input(runtime_input) {
            Ok(plan) => {
                let success = build_success_response(plan);
                json!({
                    "ok": success.ok,
                    "bridge": "hermes_runtime_plan",
                    "sessionId": session_id,
                    "eventStreamEndpoint": format!("/api/hermes/events/{}", context.trace.request_id),
                    "canonicalRoute": "/api/hermes/bridge",
                    "requestId": context.trace.request_id,
                    "traceId": context.trace.trace_id,
                    "contract": ai_bridge_contract(&session_id),
                    "plan": success.plan,
                })
            }
            Err(error) => {
                let failure = build_failure_response(error);
                return Ok((
                    StatusCode::BAD_REQUEST,
                    stamp_ai_bridge_headers(),
                    Json(serde_json::to_value(failure).expect("runtime failure 应可序列化")),
                ));
            }
        }
    };

    Ok((StatusCode::OK, stamp_ai_bridge_headers(), Json(payload)))
}

fn ai_bridge_contract(session_id: &str) -> Value {
    json!({
        "schema": "mnote.ai_bridge.v1",
        "owner": "mnote-web",
        "bridge": "hermes",
        "sessionId": session_id,
        "eventStreamEndpoint": format!("/api/hermes/events/{session_id}"),
        "canonicalRoute": "/api/hermes/bridge",
        "sessionOwner": "rust-web-hermes",
        "toolEventOwner": "rust-web-hermes",
        "clientActionOwner": "rust-web-hermes",
        "structuredWriteOwner": "rust-web-hermes"
    })
}

fn stamp_ai_bridge_headers() -> HeaderMap {
    let mut headers = HeaderMap::new();
    if let Ok(name) = HeaderName::from_lowercase(HEADER_MNOTE_WEB_OWNER.as_bytes()) {
        headers.insert(name, HeaderValue::from_static("mnote-web"));
    }
    if let Ok(name) = HeaderName::from_lowercase(HEADER_AI_BRIDGE_OWNER.as_bytes()) {
        headers.insert(name, HeaderValue::from_static("rust-web-hermes"));
    }
    headers
}

#[cfg(test)]
mod tests {
    use crate::app::{build_app, AppConfig, AppState};
    use axum::body::{to_bytes, Body};
    use axum::http::{Request, StatusCode};
    use serde_json::{json, Value};
    use tower::util::ServiceExt;

    fn app() -> axum::Router {
        build_app(AppState::new(AppConfig {
            service_name: "mnote-web".into(),
            service_version: "0.1.0".into(),
            bind_addr: "127.0.0.1:0".into(),
            public_bind_addr: "127.0.0.1:3000".into(),
            legacy_next_base_url: Some("http://127.0.0.1:3100".into()),
            enable_legacy_next_compat: true,
            enable_debug_shell_routes: false,
            enable_editor_actor: true,
            hermes_base_path: "/api/hermes".into(),
            compat_next_base_path: "/api/compat/next".into(),
            convex_url: None,
            convex_admin_key: None,
            allow_dev_fixtures: true,
            query_fixtures_json: None,
            mutation_fixtures_json: None,
            dev_user_id: "dev-user".into(),
            dev_user_name: "开发用户".into(),
            dev_user_email: "dev@mnote.local".into(),
        }))
    }

    #[tokio::test]
    async fn ai_bridge_route_returns_hermes_owner_contract() {
        let response = app()
            .oneshot(
                Request::builder()
                    .method("POST")
                    .uri("/api/hermes/bridge")
                    .header("content-type", "application/json")
                    .body(Body::from(
                        json!({
                            "kind": "tool",
                            "context": {
                                "deploymentId": null,
                                "projectId": null,
                                "workspaceId": "ws_demo",
                                "requestId": "req_1",
                                "traceId": "trace_1",
                                "actor": {
                                    "actorType": "user",
                                    "actorId": "user_1",
                                    "sessionId": null
                                },
                                "source": {
                                    "channel": "rust-web",
                                    "client": "mnote-web"
                                },
                                "tenantId": null,
                                "authToken": null,
                                "idempotencyKey": null,
                                "validateOnly": false,
                                "dryRun": false
                            },
                            "tool": {
                                "tool": "docs_search",
                                "kind": "query",
                                "mode": "plan",
                                "argsJson": {"query": "Rust Web"},
                                "target": null,
                                "reason": "owner gate",
                                "refs": []
                            },
                            "data": null
                        })
                        .to_string(),
                    ))
                    .expect("request"),
            )
            .await
            .expect("response");

        assert_eq!(response.status(), StatusCode::OK);
        assert_eq!(
            response
                .headers()
                .get("x-mnote-web-owner")
                .and_then(|value| value.to_str().ok()),
            Some("mnote-web")
        );
        assert_eq!(
            response
                .headers()
                .get("x-mnote-ai-bridge-owner")
                .and_then(|value| value.to_str().ok()),
            Some("rust-web-hermes")
        );
        let body = to_bytes(response.into_body(), usize::MAX)
            .await
            .expect("body");
        let payload: Value = serde_json::from_slice(&body).expect("json");
        assert_eq!(payload["contract"]["schema"], "mnote.ai_bridge.v1");
        assert_eq!(payload["contract"]["sessionOwner"], "rust-web-hermes");
        assert_eq!(payload["contract"]["toolEventOwner"], "rust-web-hermes");
        assert_eq!(payload["contract"]["clientActionOwner"], "rust-web-hermes");
        assert_eq!(payload["canonicalRoute"], "/api/hermes/bridge");
        assert!(payload["eventStreamEndpoint"]
            .as_str()
            .unwrap_or_default()
            .contains("/api/hermes/events/"));
    }

    #[tokio::test]
    async fn ai_bridge_accepts_legacy_intent_payload_as_hermes_session() {
        let response = app()
            .oneshot(
                Request::builder()
                    .method("POST")
                    .uri("/api/hermes/bridge")
                    .header("content-type", "application/json")
                    .body(Body::from(
                        json!({
                            "stream": true,
                            "scope": "document",
                            "messages": [{"role": "user", "content": "生成摘要"}],
                            "context": {"documentId": "doc_1"}
                        })
                        .to_string(),
                    ))
                    .expect("request"),
            )
            .await
            .expect("response");

        assert_eq!(response.status(), StatusCode::OK);
        let body = to_bytes(response.into_body(), usize::MAX)
            .await
            .expect("body");
        let payload: Value = serde_json::from_slice(&body).expect("json");
        assert_eq!(payload["bridge"], "hermes_session");
        assert_eq!(payload["canonicalRoute"], "/api/hermes/bridge");
        assert_eq!(
            payload["contract"]["structuredWriteOwner"],
            "rust-web-hermes"
        );
    }
}
