use axum::body::Body;
use axum::extract::{Path, State};
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use axum::Extension;
use myfsio_common::types::Principal;
use myfsio_storage::traits::StorageEngine;

use crate::services::site_registry::{PeerSite, SiteInfo};
use crate::services::website_domains::{is_valid_domain, normalize_domain};
use crate::state::AppState;

fn json_response(status: StatusCode, value: serde_json::Value) -> Response {
    (
        status,
        [("content-type", "application/json")],
        value.to_string(),
    )
        .into_response()
}

fn json_error(code: &str, message: &str, status: StatusCode) -> Response {
    json_response(
        status,
        serde_json::json!({"error": {"code": code, "message": message}}),
    )
}

fn push_issue(result: &mut serde_json::Value, issue: serde_json::Value) {
    if let Some(items) = result
        .get_mut("issues")
        .and_then(|value| value.as_array_mut())
    {
        items.push(issue);
    }
}

fn require_admin(principal: &Principal) -> Option<Response> {
    if !principal.is_admin {
        return Some(json_error(
            "AccessDenied",
            "Admin access required",
            StatusCode::FORBIDDEN,
        ));
    }
    None
}

fn require_iam_action(state: &AppState, principal: &Principal, action: &str) -> Option<Response> {
    if !state.iam.authorize(principal, None, action, None) {
        return Some(json_error(
            "AccessDenied",
            &format!("Requires {} permission", action),
            StatusCode::FORBIDDEN,
        ));
    }
    None
}

async fn read_json_body(body: Body) -> Option<serde_json::Value> {
    let bytes = http_body_util::BodyExt::collect(body)
        .await
        .ok()?
        .to_bytes();
    serde_json::from_slice(&bytes).ok()
}

fn validate_site_id(site_id: &str) -> Option<String> {
    if site_id.is_empty() || site_id.len() > 63 {
        return Some("site_id must be 1-63 characters".to_string());
    }
    let first = site_id.chars().next().unwrap();
    if !first.is_ascii_alphanumeric() {
        return Some("site_id must start with alphanumeric".to_string());
    }
    if !site_id
        .chars()
        .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
    {
        return Some("site_id must contain only alphanumeric, hyphens, underscores".to_string());
    }
    None
}

fn validate_endpoint(endpoint: &str) -> Option<String> {
    if !endpoint.starts_with("http://") && !endpoint.starts_with("https://") {
        return Some("Endpoint must be http or https URL".to_string());
    }
    None
}

fn validate_region(region: &str) -> Option<String> {
    let re = regex::Regex::new(r"^[a-z]{2,}-[a-z]+-\d+$").unwrap();
    if !re.is_match(region) {
        return Some("Region must match format like us-east-1".to_string());
    }
    None
}

fn validate_priority(priority: i64) -> Option<String> {
    if priority < 0 || priority > 1000 {
        return Some("Priority must be between 0 and 1000".to_string());
    }
    None
}

pub async fn get_local_site(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }

    if let Some(ref registry) = state.site_registry {
        if let Some(local) = registry.get_local_site() {
            return json_response(StatusCode::OK, serde_json::to_value(&local).unwrap());
        }
    }

    json_error(
        "NotFound",
        "Local site not configured",
        StatusCode::NOT_FOUND,
    )
}

pub async fn update_local_site(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
    body: Body,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    let registry = match &state.site_registry {
        Some(r) => r,
        None => {
            return json_error(
                "InvalidRequest",
                "Site registry not available",
                StatusCode::BAD_REQUEST,
            )
        }
    };

    let payload = match read_json_body(body).await {
        Some(v) => v,
        None => {
            return json_error(
                "MalformedJSON",
                "Invalid JSON body",
                StatusCode::BAD_REQUEST,
            )
        }
    };

    let site_id = match payload.get("site_id").and_then(|v| v.as_str()) {
        Some(s) => s.to_string(),
        None => {
            return json_error(
                "ValidationError",
                "site_id is required",
                StatusCode::BAD_REQUEST,
            )
        }
    };

    if let Some(err) = validate_site_id(&site_id) {
        return json_error("ValidationError", &err, StatusCode::BAD_REQUEST);
    }

    let endpoint = payload
        .get("endpoint")
        .and_then(|v| v.as_str())
        .unwrap_or("")
        .to_string();
    if !endpoint.is_empty() {
        if let Some(err) = validate_endpoint(&endpoint) {
            return json_error("ValidationError", &err, StatusCode::BAD_REQUEST);
        }
    }

    if let Some(p) = payload.get("priority").and_then(|v| v.as_i64()) {
        if let Some(err) = validate_priority(p) {
            return json_error("ValidationError", &err, StatusCode::BAD_REQUEST);
        }
    }

    if let Some(r) = payload.get("region").and_then(|v| v.as_str()) {
        if let Some(err) = validate_region(r) {
            return json_error("ValidationError", &err, StatusCode::BAD_REQUEST);
        }
    }

    let existing = registry.get_local_site();
    let site = SiteInfo {
        site_id: site_id.clone(),
        endpoint,
        region: payload
            .get("region")
            .and_then(|v| v.as_str())
            .unwrap_or("us-east-1")
            .to_string(),
        priority: payload
            .get("priority")
            .and_then(|v| v.as_i64())
            .unwrap_or(100) as i32,
        display_name: payload
            .get("display_name")
            .and_then(|v| v.as_str())
            .unwrap_or(&site_id)
            .to_string(),
        created_at: existing.and_then(|e| e.created_at),
    };

    registry.set_local_site(site.clone());
    json_response(StatusCode::OK, serde_json::to_value(&site).unwrap())
}

pub async fn list_all_sites(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    let registry = match &state.site_registry {
        Some(r) => r,
        None => {
            return json_response(
                StatusCode::OK,
                serde_json::json!({"local": null, "peers": [], "total_peers": 0}),
            )
        }
    };

    let local = registry.get_local_site();
    let peers = registry.list_peers();

    json_response(
        StatusCode::OK,
        serde_json::json!({
            "local": local,
            "peers": peers,
            "total_peers": peers.len(),
        }),
    )
}

pub async fn register_peer_site(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
    body: Body,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    let registry = match &state.site_registry {
        Some(r) => r,
        None => {
            return json_error(
                "InvalidRequest",
                "Site registry not available",
                StatusCode::BAD_REQUEST,
            )
        }
    };

    let payload = match read_json_body(body).await {
        Some(v) => v,
        None => {
            return json_error(
                "MalformedJSON",
                "Invalid JSON body",
                StatusCode::BAD_REQUEST,
            )
        }
    };

    let site_id = match payload.get("site_id").and_then(|v| v.as_str()) {
        Some(s) => s.to_string(),
        None => {
            return json_error(
                "ValidationError",
                "site_id is required",
                StatusCode::BAD_REQUEST,
            )
        }
    };
    if let Some(err) = validate_site_id(&site_id) {
        return json_error("ValidationError", &err, StatusCode::BAD_REQUEST);
    }

    let endpoint = match payload.get("endpoint").and_then(|v| v.as_str()) {
        Some(e) => e.to_string(),
        None => {
            return json_error(
                "ValidationError",
                "endpoint is required",
                StatusCode::BAD_REQUEST,
            )
        }
    };
    if let Some(err) = validate_endpoint(&endpoint) {
        return json_error("ValidationError", &err, StatusCode::BAD_REQUEST);
    }

    let region = payload
        .get("region")
        .and_then(|v| v.as_str())
        .unwrap_or("us-east-1")
        .to_string();
    if let Some(err) = validate_region(&region) {
        return json_error("ValidationError", &err, StatusCode::BAD_REQUEST);
    }

    let priority = payload
        .get("priority")
        .and_then(|v| v.as_i64())
        .unwrap_or(100);
    if let Some(err) = validate_priority(priority) {
        return json_error("ValidationError", &err, StatusCode::BAD_REQUEST);
    }

    if registry.get_peer(&site_id).is_some() {
        return json_error(
            "AlreadyExists",
            &format!("Peer site '{}' already exists", site_id),
            StatusCode::CONFLICT,
        );
    }

    let peer = PeerSite {
        site_id: site_id.clone(),
        endpoint,
        region,
        priority: priority as i32,
        display_name: payload
            .get("display_name")
            .and_then(|v| v.as_str())
            .unwrap_or(&site_id)
            .to_string(),
        connection_id: payload
            .get("connection_id")
            .and_then(|v| v.as_str())
            .map(|s| s.to_string()),
        peer_inbound_access_key: payload
            .get("peer_inbound_access_key")
            .and_then(|v| v.as_str())
            .map(str::trim)
            .filter(|s| !s.is_empty())
            .map(|s| s.to_string()),
        created_at: Some(chrono::Utc::now().to_rfc3339()),
        is_healthy: false,
        last_health_check: None,
    };

    registry.add_peer(peer.clone());
    json_response(StatusCode::CREATED, serde_json::to_value(&peer).unwrap())
}

pub async fn get_peer_site(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
    Path(site_id): Path<String>,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    let registry = match &state.site_registry {
        Some(r) => r,
        None => {
            return json_error(
                "NotFound",
                "Site registry not available",
                StatusCode::NOT_FOUND,
            )
        }
    };

    match registry.get_peer(&site_id) {
        Some(peer) => json_response(StatusCode::OK, serde_json::to_value(&peer).unwrap()),
        None => json_error(
            "NotFound",
            &format!("Peer site '{}' not found", site_id),
            StatusCode::NOT_FOUND,
        ),
    }
}

pub async fn update_peer_site(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
    Path(site_id): Path<String>,
    body: Body,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    let registry = match &state.site_registry {
        Some(r) => r,
        None => {
            return json_error(
                "NotFound",
                "Site registry not available",
                StatusCode::NOT_FOUND,
            )
        }
    };

    let existing = match registry.get_peer(&site_id) {
        Some(p) => p,
        None => {
            return json_error(
                "NotFound",
                &format!("Peer site '{}' not found", site_id),
                StatusCode::NOT_FOUND,
            )
        }
    };

    let payload = match read_json_body(body).await {
        Some(v) => v,
        None => {
            return json_error(
                "MalformedJSON",
                "Invalid JSON body",
                StatusCode::BAD_REQUEST,
            )
        }
    };

    if let Some(ep) = payload.get("endpoint").and_then(|v| v.as_str()) {
        if let Some(err) = validate_endpoint(ep) {
            return json_error("ValidationError", &err, StatusCode::BAD_REQUEST);
        }
    }
    if let Some(p) = payload.get("priority").and_then(|v| v.as_i64()) {
        if let Some(err) = validate_priority(p) {
            return json_error("ValidationError", &err, StatusCode::BAD_REQUEST);
        }
    }
    if let Some(r) = payload.get("region").and_then(|v| v.as_str()) {
        if let Some(err) = validate_region(r) {
            return json_error("ValidationError", &err, StatusCode::BAD_REQUEST);
        }
    }

    let peer = PeerSite {
        site_id: site_id.clone(),
        endpoint: payload
            .get("endpoint")
            .and_then(|v| v.as_str())
            .unwrap_or(&existing.endpoint)
            .to_string(),
        region: payload
            .get("region")
            .and_then(|v| v.as_str())
            .unwrap_or(&existing.region)
            .to_string(),
        priority: payload
            .get("priority")
            .and_then(|v| v.as_i64())
            .unwrap_or(existing.priority as i64) as i32,
        display_name: payload
            .get("display_name")
            .and_then(|v| v.as_str())
            .unwrap_or(&existing.display_name)
            .to_string(),
        connection_id: payload
            .get("connection_id")
            .and_then(|v| v.as_str())
            .map(|s| s.to_string())
            .or(existing.connection_id),
        peer_inbound_access_key: if payload.get("peer_inbound_access_key").is_some() {
            payload
                .get("peer_inbound_access_key")
                .and_then(|v| v.as_str())
                .map(str::trim)
                .filter(|s| !s.is_empty())
                .map(|s| s.to_string())
        } else {
            existing.peer_inbound_access_key
        },
        created_at: existing.created_at,
        is_healthy: existing.is_healthy,
        last_health_check: existing.last_health_check,
    };

    registry.update_peer(peer.clone());
    json_response(StatusCode::OK, serde_json::to_value(&peer).unwrap())
}

pub async fn delete_peer_site(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
    Path(site_id): Path<String>,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    let registry = match &state.site_registry {
        Some(r) => r,
        None => {
            return json_error(
                "NotFound",
                "Site registry not available",
                StatusCode::NOT_FOUND,
            )
        }
    };

    if !registry.delete_peer(&site_id) {
        return json_error(
            "NotFound",
            &format!("Peer site '{}' not found", site_id),
            StatusCode::NOT_FOUND,
        );
    }
    StatusCode::NO_CONTENT.into_response()
}

pub async fn check_peer_health(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
    Path(site_id): Path<String>,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    let registry = match &state.site_registry {
        Some(r) => r,
        None => {
            return json_error(
                "NotFound",
                "Site registry not available",
                StatusCode::NOT_FOUND,
            )
        }
    };

    if registry.get_peer(&site_id).is_none() {
        return json_error(
            "NotFound",
            &format!("Peer site '{}' not found", site_id),
            StatusCode::NOT_FOUND,
        );
    }

    let peer = registry.get_peer(&site_id).unwrap();
    let checked_at = chrono::Utc::now().timestamp_millis() as f64 / 1000.0;
    let mut is_healthy = false;
    let mut error: Option<String> = None;

    if let Some(connection_id) = peer.connection_id.as_deref() {
        if let Some(connection) = state.connections.get(connection_id) {
            is_healthy = state.replication.check_endpoint(&connection).await;
            if !is_healthy {
                error = Some(format!(
                    "Cannot reach endpoint: {}",
                    connection.endpoint_url
                ));
            }
        } else {
            error = Some(format!("Connection '{}' not found", connection_id));
        }
    } else {
        error = Some("No connection configured for this peer".to_string());
    }

    registry.update_health(&site_id, is_healthy);

    json_response(
        StatusCode::OK,
        serde_json::json!({
            "site_id": site_id,
            "is_healthy": is_healthy,
            "error": error,
            "checked_at": checked_at,
        }),
    )
}

pub async fn get_topology(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    let registry = match &state.site_registry {
        Some(r) => r,
        None => {
            return json_response(
                StatusCode::OK,
                serde_json::json!({"sites": [], "total": 0, "healthy_count": 0}),
            )
        }
    };

    let local = registry.get_local_site();
    let peers = registry.list_peers();

    let mut sites: Vec<serde_json::Value> = Vec::new();
    if let Some(l) = local {
        let mut v = serde_json::to_value(&l).unwrap();
        v.as_object_mut()
            .unwrap()
            .insert("is_local".to_string(), serde_json::json!(true));
        v.as_object_mut()
            .unwrap()
            .insert("is_healthy".to_string(), serde_json::json!(true));
        sites.push(v);
    }
    for p in &peers {
        let mut v = serde_json::to_value(p).unwrap();
        v.as_object_mut()
            .unwrap()
            .insert("is_local".to_string(), serde_json::json!(false));
        sites.push(v);
    }

    sites.sort_by_key(|s| s.get("priority").and_then(|v| v.as_i64()).unwrap_or(100));

    let healthy_count = sites
        .iter()
        .filter(|s| {
            s.get("is_healthy")
                .and_then(|v| v.as_bool())
                .unwrap_or(false)
        })
        .count();

    json_response(
        StatusCode::OK,
        serde_json::json!({
            "sites": sites,
            "total": sites.len(),
            "healthy_count": healthy_count,
        }),
    )
}

pub async fn check_bidirectional_status(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
    Path(site_id): Path<String>,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    let registry = match &state.site_registry {
        Some(r) => r,
        None => {
            return json_error(
                "NotFound",
                "Site registry not available",
                StatusCode::NOT_FOUND,
            )
        }
    };

    if registry.get_peer(&site_id).is_none() {
        return json_error(
            "NotFound",
            &format!("Peer site '{}' not found", site_id),
            StatusCode::NOT_FOUND,
        );
    }

    let local = registry.get_local_site();
    let peer = registry.get_peer(&site_id).unwrap();
    let local_bidirectional_rules: Vec<serde_json::Value> = state
        .replication
        .list_rules()
        .into_iter()
        .filter(|rule| {
            peer.connection_id
                .as_deref()
                .map(|connection_id| rule.target_connection_id == connection_id)
                .unwrap_or(false)
                && rule.mode == crate::services::replication::MODE_BIDIRECTIONAL
        })
        .map(|rule| {
            serde_json::json!({
                "bucket_name": rule.bucket_name,
                "target_bucket": rule.target_bucket,
                "enabled": rule.enabled,
            })
        })
        .collect();

    let mut result = serde_json::json!({
        "site_id": site_id,
        "local_site_id": local.as_ref().map(|l| l.site_id.clone()),
        "local_endpoint": local.as_ref().map(|l| l.endpoint.clone()),
        "local_bidirectional_rules": local_bidirectional_rules,
        "local_site_sync_enabled": state.config.site_sync_enabled,
        "remote_status": null,
        "issues": Vec::<serde_json::Value>::new(),
        "is_fully_configured": false,
    });

    if local
        .as_ref()
        .map(|site| site.site_id.trim().is_empty())
        .unwrap_or(true)
    {
        push_issue(
            &mut result,
            serde_json::json!({
                "code": "NO_LOCAL_SITE_ID",
                "message": "Local site identity not configured",
                "severity": "error",
            }),
        );
    }
    if local
        .as_ref()
        .map(|site| site.endpoint.trim().is_empty())
        .unwrap_or(true)
    {
        push_issue(
            &mut result,
            serde_json::json!({
                "code": "NO_LOCAL_ENDPOINT",
                "message": "Local site endpoint not configured (remote site cannot reach back)",
                "severity": "error",
            }),
        );
    }

    let Some(connection_id) = peer.connection_id.as_deref() else {
        push_issue(
            &mut result,
            serde_json::json!({
                "code": "NO_CONNECTION",
                "message": "No connection configured for this peer",
                "severity": "error",
            }),
        );
        return json_response(StatusCode::OK, result);
    };

    let Some(connection) = state.connections.get(connection_id) else {
        push_issue(
            &mut result,
            serde_json::json!({
                "code": "CONNECTION_NOT_FOUND",
                "message": format!("Connection '{}' not found", connection_id),
                "severity": "error",
            }),
        );
        return json_response(StatusCode::OK, result);
    };

    if result["local_bidirectional_rules"]
        .as_array()
        .map(|rules| rules.is_empty())
        .unwrap_or(true)
    {
        push_issue(
            &mut result,
            serde_json::json!({
                "code": "NO_LOCAL_BIDIRECTIONAL_RULES",
                "message": "No bidirectional replication rules configured on this site",
                "severity": "warning",
            }),
        );
    }
    if !state.config.site_sync_enabled {
        push_issue(
            &mut result,
            serde_json::json!({
                "code": "SITE_SYNC_DISABLED",
                "message": "Site sync worker is disabled (SITE_SYNC_ENABLED=false). Pull operations will not work.",
                "severity": "warning",
            }),
        );
    }
    if !state.replication.check_endpoint(&connection).await {
        push_issue(
            &mut result,
            serde_json::json!({
                "code": "REMOTE_UNREACHABLE",
                "message": "Remote endpoint is not reachable",
                "severity": "error",
            }),
        );
        return json_response(StatusCode::OK, result);
    }

    let admin_url = format!(
        "{}/admin/sites",
        connection.endpoint_url.trim_end_matches('/')
    );
    match reqwest::Client::new()
        .get(&admin_url)
        .header("accept", "application/json")
        .header("x-access-key", &connection.access_key)
        .header("x-secret-key", &connection.secret_key)
        .timeout(std::time::Duration::from_secs(10))
        .send()
        .await
    {
        Ok(resp) if resp.status().is_success() => match resp.json::<serde_json::Value>().await {
            Ok(remote_data) => {
                let remote_local = remote_data
                    .get("local")
                    .cloned()
                    .unwrap_or(serde_json::Value::Null);
                let remote_peers = remote_data
                    .get("peers")
                    .and_then(|value| value.as_array())
                    .cloned()
                    .unwrap_or_default();
                let mut has_peer_for_us = false;
                let mut peer_connection_configured = false;

                for remote_peer in &remote_peers {
                    let matches_site = local
                        .as_ref()
                        .map(|site| {
                            remote_peer.get("site_id").and_then(|v| v.as_str())
                                == Some(site.site_id.as_str())
                                || remote_peer.get("endpoint").and_then(|v| v.as_str())
                                    == Some(site.endpoint.as_str())
                        })
                        .unwrap_or(false);
                    if matches_site {
                        has_peer_for_us = true;
                        peer_connection_configured = remote_peer
                            .get("connection_id")
                            .and_then(|v| v.as_str())
                            .map(|v| !v.trim().is_empty())
                            .unwrap_or(false);
                        break;
                    }
                }

                result["remote_status"] = serde_json::json!({
                    "reachable": true,
                    "local_site": remote_local,
                    "site_sync_enabled": serde_json::Value::Null,
                    "has_peer_for_us": has_peer_for_us,
                    "peer_connection_configured": peer_connection_configured,
                    "has_bidirectional_rules_for_us": serde_json::Value::Null,
                });

                if !has_peer_for_us {
                    push_issue(
                        &mut result,
                        serde_json::json!({
                            "code": "REMOTE_NO_PEER_FOR_US",
                            "message": "Remote site does not have this site registered as a peer",
                            "severity": "error",
                        }),
                    );
                } else if !peer_connection_configured {
                    push_issue(
                        &mut result,
                        serde_json::json!({
                            "code": "REMOTE_NO_CONNECTION_FOR_US",
                            "message": "Remote site has us as peer but no connection configured (cannot push back)",
                            "severity": "error",
                        }),
                    );
                }
            }
            Err(_) => {
                result["remote_status"] = serde_json::json!({
                    "reachable": true,
                    "invalid_response": true,
                });
                push_issue(
                    &mut result,
                    serde_json::json!({
                        "code": "REMOTE_INVALID_RESPONSE",
                        "message": "Remote admin API returned invalid JSON",
                        "severity": "warning",
                    }),
                );
            }
        },
        Ok(resp)
            if resp.status() == StatusCode::UNAUTHORIZED
                || resp.status() == StatusCode::FORBIDDEN =>
        {
            result["remote_status"] = serde_json::json!({
                "reachable": true,
                "admin_access_denied": true,
            });
            push_issue(
                &mut result,
                serde_json::json!({
                    "code": "REMOTE_ADMIN_ACCESS_DENIED",
                    "message": "Cannot verify remote configuration (admin access denied)",
                    "severity": "warning",
                }),
            );
        }
        Ok(resp) => {
            result["remote_status"] = serde_json::json!({
                "reachable": true,
                "admin_api_error": resp.status().as_u16(),
            });
            push_issue(
                &mut result,
                serde_json::json!({
                    "code": "REMOTE_ADMIN_API_ERROR",
                    "message": format!("Remote admin API returned status {}", resp.status().as_u16()),
                    "severity": "warning",
                }),
            );
        }
        Err(_) => {
            result["remote_status"] = serde_json::json!({
                "reachable": false,
                "error": "Connection failed",
            });
            push_issue(
                &mut result,
                serde_json::json!({
                    "code": "REMOTE_ADMIN_UNREACHABLE",
                    "message": "Could not reach remote admin API",
                    "severity": "warning",
                }),
            );
        }
    }

    let has_errors = result["issues"]
        .as_array()
        .map(|items| {
            items.iter().any(|issue| {
                issue.get("severity").and_then(|value| value.as_str()) == Some("error")
            })
        })
        .unwrap_or(true);
    result["is_fully_configured"] = serde_json::json!(
        !has_errors
            && result["local_bidirectional_rules"]
                .as_array()
                .map(|rules| !rules.is_empty())
                .unwrap_or(false)
    );

    json_response(StatusCode::OK, result)
}

pub async fn iam_list_users(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
) -> Response {
    if let Some(err) = require_iam_action(&state, &principal, "iam:list_users") {
        return err;
    }
    let users = state.iam.list_users().await;
    json_response(StatusCode::OK, serde_json::json!({"users": users}))
}

pub async fn iam_get_user(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
    Path(identifier): Path<String>,
) -> Response {
    if let Some(err) = require_iam_action(&state, &principal, "iam:get_user") {
        return err;
    }
    match state.iam.get_user(&identifier).await {
        Some(user) => json_response(StatusCode::OK, user),
        None => json_error(
            "NotFound",
            &format!("User '{}' not found", identifier),
            StatusCode::NOT_FOUND,
        ),
    }
}

pub async fn iam_get_user_policies(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
    Path(identifier): Path<String>,
) -> Response {
    if let Some(err) = require_iam_action(&state, &principal, "iam:get_policy") {
        return err;
    }
    match state.iam.get_user_policies(&identifier) {
        Some(policies) => json_response(StatusCode::OK, serde_json::json!({"policies": policies})),
        None => json_error(
            "NotFound",
            &format!("User '{}' not found", identifier),
            StatusCode::NOT_FOUND,
        ),
    }
}

pub async fn iam_create_access_key(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
    Path(identifier): Path<String>,
) -> Response {
    if let Some(err) = require_iam_action(&state, &principal, "iam:create_key") {
        return err;
    }
    match state.iam.create_access_key(&identifier) {
        Ok(result) => json_response(StatusCode::CREATED, result),
        Err(e) => json_error("InvalidRequest", &e, StatusCode::BAD_REQUEST),
    }
}

pub async fn iam_delete_access_key(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
    Path((_identifier, access_key)): Path<(String, String)>,
) -> Response {
    if let Some(err) = require_iam_action(&state, &principal, "iam:delete_key") {
        return err;
    }
    match state.iam.delete_access_key(&access_key) {
        Ok(()) => StatusCode::NO_CONTENT.into_response(),
        Err(e) => json_error("InvalidRequest", &e, StatusCode::BAD_REQUEST),
    }
}

pub async fn iam_disable_user(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
    Path(identifier): Path<String>,
) -> Response {
    if let Some(err) = require_iam_action(&state, &principal, "iam:disable_user") {
        return err;
    }
    match state.iam.set_user_enabled(&identifier, false).await {
        Ok(()) => json_response(StatusCode::OK, serde_json::json!({"status": "disabled"})),
        Err(e) => json_error("InvalidRequest", &e, StatusCode::BAD_REQUEST),
    }
}

pub async fn iam_enable_user(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
    Path(identifier): Path<String>,
) -> Response {
    if let Some(err) = require_iam_action(&state, &principal, "iam:disable_user") {
        return err;
    }
    match state.iam.set_user_enabled(&identifier, true).await {
        Ok(()) => json_response(StatusCode::OK, serde_json::json!({"status": "enabled"})),
        Err(e) => json_error("InvalidRequest", &e, StatusCode::BAD_REQUEST),
    }
}

pub async fn list_website_domains(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    let store = match &state.website_domains {
        Some(s) => s,
        None => {
            return json_error(
                "InvalidRequest",
                "Website hosting is not enabled",
                StatusCode::BAD_REQUEST,
            )
        }
    };
    json_response(StatusCode::OK, serde_json::json!(store.list_all()))
}

pub async fn create_website_domain(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
    body: Body,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    let store = match &state.website_domains {
        Some(s) => s,
        None => {
            return json_error(
                "InvalidRequest",
                "Website hosting is not enabled",
                StatusCode::BAD_REQUEST,
            )
        }
    };

    let payload = match read_json_body(body).await {
        Some(v) => v,
        None => {
            return json_error(
                "MalformedJSON",
                "Invalid JSON body",
                StatusCode::BAD_REQUEST,
            )
        }
    };

    let domain = normalize_domain(payload.get("domain").and_then(|v| v.as_str()).unwrap_or(""));
    if domain.is_empty() {
        return json_error(
            "ValidationError",
            "domain is required",
            StatusCode::BAD_REQUEST,
        );
    }
    if !is_valid_domain(&domain) {
        return json_error(
            "ValidationError",
            &format!("Invalid domain: '{}'", domain),
            StatusCode::BAD_REQUEST,
        );
    }

    let bucket = payload
        .get("bucket")
        .and_then(|v| v.as_str())
        .unwrap_or("")
        .trim()
        .to_string();
    if bucket.is_empty() {
        return json_error(
            "ValidationError",
            "bucket is required",
            StatusCode::BAD_REQUEST,
        );
    }

    match state.storage.bucket_exists(&bucket).await {
        Ok(true) => {}
        _ => {
            return json_error(
                "NoSuchBucket",
                &format!("Bucket '{}' does not exist", bucket),
                StatusCode::NOT_FOUND,
            )
        }
    }

    if store.get_bucket(&domain).is_some() {
        return json_error(
            "Conflict",
            &format!("Domain '{}' is already mapped", domain),
            StatusCode::CONFLICT,
        );
    }

    store.set_mapping(&domain, &bucket);
    json_response(
        StatusCode::CREATED,
        serde_json::json!({"domain": domain, "bucket": bucket}),
    )
}

pub async fn get_website_domain(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
    Path(domain): Path<String>,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    let store = match &state.website_domains {
        Some(s) => s,
        None => {
            return json_error(
                "InvalidRequest",
                "Website hosting is not enabled",
                StatusCode::BAD_REQUEST,
            )
        }
    };

    let domain = normalize_domain(&domain);
    match store.get_bucket(&domain) {
        Some(bucket) => json_response(
            StatusCode::OK,
            serde_json::json!({"domain": domain, "bucket": bucket}),
        ),
        None => json_error(
            "NotFound",
            &format!("No mapping found for domain '{}'", domain),
            StatusCode::NOT_FOUND,
        ),
    }
}

pub async fn update_website_domain(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
    Path(domain): Path<String>,
    body: Body,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    let store = match &state.website_domains {
        Some(s) => s,
        None => {
            return json_error(
                "InvalidRequest",
                "Website hosting is not enabled",
                StatusCode::BAD_REQUEST,
            )
        }
    };

    let domain = normalize_domain(&domain);
    let payload = match read_json_body(body).await {
        Some(v) => v,
        None => {
            return json_error(
                "MalformedJSON",
                "Invalid JSON body",
                StatusCode::BAD_REQUEST,
            )
        }
    };

    let bucket = payload
        .get("bucket")
        .and_then(|v| v.as_str())
        .unwrap_or("")
        .trim()
        .to_string();
    if bucket.is_empty() {
        return json_error(
            "ValidationError",
            "bucket is required",
            StatusCode::BAD_REQUEST,
        );
    }

    match state.storage.bucket_exists(&bucket).await {
        Ok(true) => {}
        _ => {
            return json_error(
                "NoSuchBucket",
                &format!("Bucket '{}' does not exist", bucket),
                StatusCode::NOT_FOUND,
            )
        }
    }

    if store.get_bucket(&domain).is_none() {
        return json_error(
            "NotFound",
            &format!("No mapping found for domain '{}'", domain),
            StatusCode::NOT_FOUND,
        );
    }

    store.set_mapping(&domain, &bucket);
    json_response(
        StatusCode::OK,
        serde_json::json!({"domain": domain, "bucket": bucket}),
    )
}

pub async fn delete_website_domain(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
    Path(domain): Path<String>,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    let store = match &state.website_domains {
        Some(s) => s,
        None => {
            return json_error(
                "InvalidRequest",
                "Website hosting is not enabled",
                StatusCode::BAD_REQUEST,
            )
        }
    };

    let domain = normalize_domain(&domain);
    if !store.delete_mapping(&domain) {
        return json_error(
            "NotFound",
            &format!("No mapping found for domain '{}'", domain),
            StatusCode::NOT_FOUND,
        );
    }
    StatusCode::NO_CONTENT.into_response()
}

#[derive(serde::Deserialize, Default)]
pub struct PaginationQuery {
    pub limit: Option<usize>,
    pub offset: Option<usize>,
}

pub async fn gc_status(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    match &state.gc {
        Some(gc) => json_response(StatusCode::OK, gc.status().await),
        None => json_response(
            StatusCode::OK,
            serde_json::json!({"enabled": false, "message": "GC is not enabled. Set GC_ENABLED=true to enable."}),
        ),
    }
}

pub async fn gc_run(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
    body: Body,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    let gc = match &state.gc {
        Some(gc) => gc,
        None => {
            return json_error(
                "InvalidRequest",
                "GC is not enabled",
                StatusCode::BAD_REQUEST,
            )
        }
    };

    let payload = read_json_body(body).await.unwrap_or(serde_json::json!({}));
    let dry_run = payload
        .get("dry_run")
        .and_then(|v| v.as_bool())
        .unwrap_or(false);

    match gc.run_now(dry_run).await {
        Ok(result) => json_response(StatusCode::OK, result),
        Err(e) => json_error("Conflict", &e, StatusCode::CONFLICT),
    }
}

pub async fn gc_history(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    match &state.gc {
        Some(gc) => json_response(
            StatusCode::OK,
            serde_json::json!({"executions": gc.history().await}),
        ),
        None => json_response(StatusCode::OK, serde_json::json!({"executions": []})),
    }
}

pub async fn integrity_status(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    match &state.integrity {
        Some(checker) => json_response(StatusCode::OK, checker.status().await),
        None => json_response(
            StatusCode::OK,
            serde_json::json!({"enabled": false, "message": "Integrity checker is not enabled. Set INTEGRITY_ENABLED=true to enable."}),
        ),
    }
}

pub async fn integrity_run(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
    body: Body,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    let checker = match &state.integrity {
        Some(c) => c,
        None => {
            return json_error(
                "InvalidRequest",
                "Integrity checker is not enabled",
                StatusCode::BAD_REQUEST,
            )
        }
    };

    let payload = read_json_body(body).await.unwrap_or(serde_json::json!({}));
    let dry_run = payload
        .get("dry_run")
        .and_then(|v| v.as_bool())
        .unwrap_or(false);
    let auto_heal = payload
        .get("auto_heal")
        .and_then(|v| v.as_bool())
        .unwrap_or(false);

    match checker.run_now(dry_run, auto_heal).await {
        Ok(result) => json_response(StatusCode::OK, result),
        Err(e) => json_error("Conflict", &e, StatusCode::CONFLICT),
    }
}

pub async fn integrity_history(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    match &state.integrity {
        Some(checker) => json_response(
            StatusCode::OK,
            serde_json::json!({"executions": checker.history().await}),
        ),
        None => json_response(StatusCode::OK, serde_json::json!({"executions": []})),
    }
}

fn require_admin_or_registered_peer(state: &AppState, principal: &Principal) -> Option<Response> {
    if principal.is_admin {
        return None;
    }
    let registry = match &state.site_registry {
        Some(r) => r,
        None => {
            return Some(json_error(
                "AccessDenied",
                "Admin access required",
                StatusCode::FORBIDDEN,
            ))
        }
    };
    for peer in registry.list_peers() {
        if peer.peer_inbound_access_key.as_deref() == Some(principal.access_key.as_str()) {
            return None;
        }
    }
    Some(json_error(
        "AccessDenied",
        "Admin or registered peer required",
        StatusCode::FORBIDDEN,
    ))
}

pub async fn build_cluster_overview_public(state: &AppState) -> serde_json::Value {
    build_cluster_overview(state).await
}

async fn build_cluster_overview(state: &AppState) -> serde_json::Value {
    let local_site = state
        .site_registry
        .as_ref()
        .and_then(|r| r.get_local_site());

    let buckets = state.storage.list_buckets().await.unwrap_or_default();
    let bucket_count = buckets.len() as u64;
    let mut total_objects: u64 = 0;
    let mut size_bytes: u64 = 0;
    for b in &buckets {
        if let Ok(stats) = state.storage.bucket_stats(&b.name).await {
            total_objects += stats.total_objects();
            size_bytes += stats.total_bytes();
        }
    }

    let (disk_total, disk_free) =
        crate::services::system_metrics::sample_disk(&state.config.storage_root);

    let system = match state.system_metrics.as_ref() {
        Some(svc) => {
            let history = svc.get_history(Some(1)).await;
            history
                .last()
                .map(|s| {
                    serde_json::json!({
                        "cpu_percent": s.cpu_percent,
                        "memory_percent": s.memory_percent,
                        "disk_percent": s.disk_percent,
                        "storage_bytes": s.storage_bytes,
                    })
                })
                .unwrap_or_else(|| serde_json::json!({}))
        }
        None => serde_json::json!({}),
    };

    let sync_snapshot = state
        .site_sync
        .as_ref()
        .map(|w| w.snapshot_stats())
        .unwrap_or_default();
    let mut sync_errors: u64 = 0;
    let mut last_sync_at: Option<f64> = None;
    for s in sync_snapshot.values() {
        sync_errors += s.errors;
        if let Some(ts) = s.last_sync_at {
            last_sync_at = match last_sync_at {
                Some(prev) if prev > ts => Some(prev),
                _ => Some(ts),
            };
        }
    }

    let now = chrono::Utc::now().timestamp_millis() as f64 / 1000.0;
    serde_json::json!({
        "site_id": local_site.as_ref().map(|s| s.site_id.clone()),
        "display_name": local_site.as_ref().map(|s| s.display_name.clone()),
        "endpoint": local_site.as_ref().map(|s| s.endpoint.clone()),
        "region": local_site.as_ref().map(|s| s.region.clone()),
        "priority": local_site.as_ref().map(|s| s.priority),
        "capacity": {
            "total_bytes": disk_total,
            "available_bytes": disk_free,
        },
        "buckets": bucket_count,
        "objects": total_objects,
        "size_bytes": size_bytes,
        "system": system,
        "sync": {
            "errors": sync_errors,
            "last_sync_at": last_sync_at,
        },
        "generated_at": now,
    })
}

pub async fn get_cluster_overview(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
) -> Response {
    if let Some(err) = require_admin_or_registered_peer(&state, &principal) {
        return err;
    }
    {
        let guard = state.cluster_overview_cache.lock();
        if let Some((at, ref value)) = *guard {
            if at.elapsed() < std::time::Duration::from_secs(10) {
                return json_response(StatusCode::OK, value.clone());
            }
        }
    }
    let value = build_cluster_overview(&state).await;
    *state.cluster_overview_cache.lock() =
        Some((std::time::Instant::now(), value.clone()));
    json_response(StatusCode::OK, value)
}

pub async fn get_sync_stats(
    State(state): State<AppState>,
    Extension(principal): Extension<Principal>,
) -> Response {
    if let Some(err) = require_admin(&principal) {
        return err;
    }
    let snapshot = match state.site_sync.as_ref() {
        Some(worker) => worker.snapshot_stats(),
        None => Default::default(),
    };
    let stats: Vec<serde_json::Value> = snapshot
        .into_iter()
        .map(|(bucket, s)| {
            serde_json::json!({
                "bucket": bucket,
                "last_sync_at": s.last_sync_at,
                "objects_pulled": s.objects_pulled,
                "objects_skipped": s.objects_skipped,
                "conflicts_resolved": s.conflicts_resolved,
                "deletions_applied": s.deletions_applied,
                "errors": s.errors,
            })
        })
        .collect();
    json_response(
        StatusCode::OK,
        serde_json::json!({
            "enabled": state.site_sync.is_some(),
            "stats": stats,
        }),
    )
}
