        if let Some(idx) = gates.iter().position(|g| {
            g.action == q.action
                && g.target == q.target
                && g.namespace == q.namespace
                && g.params == q.params
        }) {
            found = Some(gates[idx].clone());
            if q.consume {
                to_remove = Some(idx);
            }
        }
        if let Some(idx) = to_remove {
            gates.remove(idx);
        }
    });

    match found {
        Some(record) => {
            if record.status == "authorized" {
                (axum::http::StatusCode::OK, "Authorized").into_response()
            } else {
                let msg = if let Some(r) = record.reason {
                    format!("Action blocked. Reason: {}", r)
                } else {
                    "Action blocked.".to_string()
                };
                (axum::http::StatusCode::FORBIDDEN, msg).into_response()
            }
        }
        None => (
            axum::http::StatusCode::NOT_FOUND,
            "Action not yet authorized (no gate record found).",
        )
            .into_response(),
    }
}

async fn gate_set_handler(
    State(app_state): State<Arc<AppState>>,
    Json(body): Json<GateSetReq>,
) -> axum::response::Response {
    let status = if body.block.unwrap_or(false) {
        "blocked".to_string()
    } else if body.authorize.unwrap_or(false) {
        "authorized".to_string()
    } else {
        "pending".to_string()
    };

    let record = GateRecord {
        id: uuid::Uuid::new_v4().to_string(),
        action: body.action.clone(),
        target: body.target.clone(),
        namespace: body.namespace.clone(),
        params: body.params.clone(),
        status,
        reason: body.reason.clone(),
        timestamp: crate::handlers::utils::now_secs(),
    };
    app_state.handler.state.gates.modify(|gates| {
        gates.retain(|g| !(g.action == record.action && g.target == record.target));
        gates.push(record);
    });
    (axum::http::StatusCode::OK, "Gate state updated.").into_response()
}

async fn run_server(state: Arc<MemoryState>) -> Result<(), Box<dyn std::error::Error>> {
    state.rebuild_index().await;
    tokio::spawn(index_committer_worker(Arc::clone(&state)));
    let app_state = Arc::new(AppState {
        handler: Arc::new(MemoryHandler::new(Arc::clone(&state))),
        clients: RwLock::new(HashMap::new()),
        next_id: AtomicUsize::new(1),
    });

    let app_state_clone = Arc::clone(&app_state);
    let mut rx = state.activity_tx.subscribe();
    tokio::spawn(async move {
        while let Ok(msg) = rx.recv().await {
            let senders: Vec<_> = app_state_clone
                .clients
                .read()
                .unwrap_or_else(|e| e.into_inner())
                .values()
                .cloned()
                .collect();
            for client_tx in senders {
                let _ = client_tx.try_send(msg.clone());
            }
        }
    });

    let app = Router::new()
            .route(
                "/api/version",
                get(|| async move {
                    axum::Json(serde_json::json!({
                        "version": env!("APP_VERSION"),
                        "git_hash": option_env!("GIT_HASH").unwrap_or("unknown")
                    }))
                }),
            )
            .route("/ws", get(ws_handler))
            .route("/health", get(health_handler))
            .route("/nvim/telemetry", post(nvim_telemetry_handler))
            .route("/gate/verify", get(gate_verify_handler))
            .route("/gate/set", post(gate_set_handler))
            .route(
                "/shutdown",
                post(
                    |headers: axum::http::HeaderMap, State(state): State<Arc<AppState>>| async move {
                        let token_path = state.handler.state.base_dir.join("admin.token");
                        let expected_token = tokio::fs::read_to_string(&token_path)
                            .await
                            .unwrap_or_default()
                            .trim()
                            .to_string();

                        let auth_header = headers
                            .get(axum::http::header::AUTHORIZATION)
                            .and_then(|h| h.to_str().ok())
                            .unwrap_or_default();

                        if expected_token.is_empty() || auth_header != format!("Bearer {}", expected_token) {
                            return (axum::http::StatusCode::UNAUTHORIZED, "Unauthorized").into_response();
                        }

                        std::thread::spawn(|| {
                            tracing::info!(
                                "Received shutdown request via /shutdown endpoint. Exiting process cleanly."
                            );
                            std::thread::sleep(std::time::Duration::from_millis(100));
                            std::process::exit(0);
                        });
                        (axum::http::StatusCode::OK, "Shutting down...").into_response()
                    },
                ),
            )
            .route(
                "/",
                get(|| async move { axum::response::Html(include_str!("dashboard.html")) }),
            )
            .route(
                "/api/graph",
                get({
                    let state_clone = app_state.handler.state.clone();
                    move || async move {
                        let graph_json = state_clone.read_graph(|g| serde_json::to_string(g).unwrap_or_else(|_| "{}".to_string()));
                        ([(axum::http::header::CONTENT_TYPE, "application/json")], graph_json)
                    }
                }),
            )
            .route(
                "/api/tasks/{id}/complete",
                post({
                    let state_clone = app_state.handler.state.clone();
                    move |axum::extract::Path(id): axum::extract::Path<String>| async move {
                        state_clone.tasks.modify(|tasks| {
                            for t in tasks.iter_mut() {
                                if t.id == id {
                                    t.status = "completed".to_string();
                                    break;
                                }
                            }
                        });
                        axum::Json(serde_json::json!({"status": "success"}))
                    }
                }),
            )
            .route(
                "/api/tasks",
                get({
                    let state_clone = app_state.handler.state.clone();
                    move || async move {
                        let tasks_json = state_clone.tasks.read_with(|t| serde_json::to_string(t).unwrap_or_else(|_| "[]".to_string()));
                        ([(axum::http::header::CONTENT_TYPE, "application/json")], tasks_json)
                    }
                }),
            )
            .route(
                "/api/sticky",
                get({
                    let state_clone = app_state.handler.state.clone();
                    move || async move {
                        let sticky_json = state_clone.sticky.read_with(|s| serde_json::to_string(s).unwrap_or_else(|_| "[]".to_string()));
                        ([(axum::http::header::CONTENT_TYPE, "application/json")], sticky_json)
                    }
                }),
            )
            .route(
                "/api/search",
                get({
                    let state_clone = app_state.handler.state.clone();
                    move |axum::extract::Query(params): axum::extract::Query<
                        std::collections::HashMap<String, String>,
                    >| async move {
                        if let Some(q) = params.get("q")
                            && let Ok(idx) = state_clone.search_index.read()
                                && let Ok(results) = idx.search(q, None) {
                                    let mut formatted_results = Vec::new();
                                    for (id, doc_type, title, body, score) in results {
                                        formatted_results.push(serde_json::json!({
                                            "id": id,
                                            "type_name": doc_type,
                                            "title": title,
                                            "content": body,
                                            "score": score
                                        }));
                                    }
                                    return axum::Json(
                                        serde_json::json!({ "results": formatted_results }),
                                    );
                                }
                        axum::Json(serde_json::json!({ "results": [] }))
                    }
                }),
            )
            .route(
                "/api/activity",
                get({
                    let state_clone = app_state.handler.state.clone();
                    move || async move {
                        let activities_json = state_clone.recent_activities.read_with(|a| serde_json::to_string(a).unwrap_or_else(|_| "[]".to_string()));
                        ([(axum::http::header::CONTENT_TYPE, "application/json")], activities_json)
                    }
                }),
            )
            .route(
                "/api/stats",
                get({
                    let state_clone = app_state.handler.state.clone();
                    move || async move {
                        let (entities, relations) = state_clone.read_graph(|g| (g.entities.len(), g.relations.len()));
                        let tasks = state_clone.tasks.read_with(|items| items.len());
                        let snippets = state_clone.snippets.read_with(|items| items.len());
                        let tech_debts = state_clone.tech_debts.read_with(|items| items.len());
                        let adrs = state_clone.adrs.read_with(|items| items.len());

                        let ledger = state_clone.ledger.read_with(|items| items.len());
                        let error_fixes = state_clone.error_fixes.read_with(|items| items.len());
                        let session_summaries = state_clone.session_summaries.read_with(|items| items.len());
                        let handoff_memos = state_clone.handoff_memos.read_with(|items| items.len());
                        let env_fingerprints = state_clone.env_fingerprints.read_with(|items| items.len());
                        let env_requirements = state_clone.env_requirements.read_with(|items| items.len());
                        let milestones = state_clone.milestones.read_with(|items| items.len());
                        let environments = state_clone.environments.read_with(|items| items.len());
                        let gates = state_clone.gates.read_with(|items| items.len());

                        axum::Json(serde_json::json!({
                            "entities": entities,
                            "relations": relations,
                            "tasks": tasks,
                            "snippets": snippets,
                            "tech_debts": tech_debts,
                            "adrs": adrs,
                            "ledger": ledger,
                            "error_fixes": error_fixes,
                            "session_summaries": session_summaries,
                            "handoff_memos": handoff_memos,
                            "env_fingerprints": env_fingerprints,
                            "env_requirements": env_requirements,
                            "milestones": milestones,
                            "environments": environments,
                            "gates": gates
                        }))
                    }
                }),
            )
            .with_state(app_state);

    tracing::info!("MCP Memory Server running on http://127.0.0.1:3000/sse");
    let addr = std::env::var("MCP_PORT").unwrap_or_else(|_| "3000".to_string());
    let addr: std::net::SocketAddr = format!("127.0.0.1:{}", addr)
        .parse()
        .expect("Invalid bind address");

    let listener = match tokio::net::TcpListener::bind(&addr).await {
        Ok(l) => l,
        Err(e) => {
            let log_path = dirs::home_dir()
                .unwrap_or_default()
                .join(".gemini/mcp_memory/daemon_error.log");
            let _ =
                tokio::fs::write(&log_path, format!("Failed to bind to {}: {}\n", addr, e)).await;
            return Ok(());
        }
    };
    if let Err(e) = axum::serve(listener, app.into_make_service()).await {
        let log_path = dirs::home_dir()
            .unwrap_or_default()
            .join(".gemini/mcp_memory/daemon_error.log");
        let _ = tokio::fs::write(&log_path, format!("Server crashed: {}\n", e)).await;
    }
