//! REST HTTP API handlers for FlareDB
//!
//! Implements REST endpoints as specified in T050.S3:
//! - POST /api/v1/sql - Execute SQL query
//! - GET /api/v1/tables - List tables
//! - GET /api/v1/kv/{key} - KV get
//! - PUT /api/v1/kv/{key} - KV put
//! - GET /api/v1/scan - Range scan
//! - GET /health - Health check

use crate::pd_client::PdClient;
use axum::{
    extract::{Path, Query, State},
    http::StatusCode,
    routing::{get, post, put},
    Json, Router,
};
use flaredb_client::RdbClient;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;

/// REST API state
#[derive(Clone)]
pub struct RestApiState {
    pub server_addr: String,
    pub pd_endpoints: Vec<String>,
    pub store_id: u64,
    pub configured_peers: HashMap<u64, String>,
}

/// Standard REST error response
#[derive(Debug, Serialize)]
pub struct ErrorResponse {
    pub error: ErrorDetail,
    pub meta: ResponseMeta,
}

#[derive(Debug, Serialize)]
pub struct ErrorDetail {
    pub code: String,
    pub message: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub details: Option<serde_json::Value>,
}

#[derive(Debug, Serialize)]
pub struct ResponseMeta {
    pub request_id: String,
    pub timestamp: String,
}

impl ResponseMeta {
    fn new() -> Self {
        Self {
            request_id: uuid::Uuid::new_v4().to_string(),
            timestamp: chrono::Utc::now().to_rfc3339(),
        }
    }
}

/// Standard REST success response
#[derive(Debug, Serialize)]
pub struct SuccessResponse<T> {
    pub data: T,
    pub meta: ResponseMeta,
}

impl<T> SuccessResponse<T> {
    fn new(data: T) -> Self {
        Self {
            data,
            meta: ResponseMeta::new(),
        }
    }
}

/// SQL execution request
#[derive(Debug, Deserialize)]
pub struct SqlRequest {
    pub query: String,
}

/// SQL execution response
#[derive(Debug, Serialize)]
pub struct SqlResponse {
    pub rows_affected: Option<u64>,
    pub rows: Option<Vec<serde_json::Value>>,
}

/// KV Put request body
#[derive(Debug, Deserialize)]
pub struct PutRequest {
    pub value: String,
    #[serde(default)]
    pub namespace: String,
}

/// KV Get response
#[derive(Debug, Serialize)]
pub struct GetResponse {
    pub key: String,
    pub value: String,
}

/// Tables list response
#[derive(Debug, Serialize)]
pub struct TablesResponse {
    pub tables: Vec<String>,
}

/// Query parameters for scan
#[derive(Debug, Deserialize)]
pub struct ScanQuery {
    pub start: Option<String>,
    pub end: Option<String>,
    #[serde(default)]
    pub namespace: String,
}

/// Scan response item
#[derive(Debug, Serialize)]
pub struct KvItem {
    pub key: String,
    pub value: String,
}

/// Scan response
#[derive(Debug, Serialize)]
pub struct ScanResponse {
    pub items: Vec<KvItem>,
}

/// Add peer request
#[derive(Debug, Deserialize)]
pub struct AddPeerRequest {
    pub peer_id: u64,
}

/// Legacy/admin add member request for first-boot compatibility.
#[derive(Debug, Deserialize)]
pub struct AddMemberRequestLegacy {
    pub id: String,
    pub raft_addr: String,
    #[serde(default)]
    pub addr: Option<String>,
}

/// Region info response
#[derive(Debug, Serialize)]
pub struct RegionResponse {
    pub id: u64,
    pub peers: Vec<u64>,
    pub leader_id: u64,
}

/// Build the REST API router
pub fn build_router(state: RestApiState) -> Router {
    Router::new()
        .route("/api/v1/sql", post(execute_sql))
        .route("/api/v1/tables", get(list_tables))
        .route("/api/v1/kv/{key}", get(get_kv).put(put_kv))
        .route("/api/v1/scan", get(scan_kv))
        .route("/api/v1/regions/{id}", get(get_region))
        .route("/api/v1/regions/{id}/add_peer", post(add_peer_to_region))
        .route("/admin/member/add", post(add_member_legacy))
        .route("/health", get(health_check))
        .with_state(state)
}

/// Health check endpoint
async fn health_check() -> (StatusCode, Json<SuccessResponse<serde_json::Value>>) {
    (
        StatusCode::OK,
        Json(SuccessResponse::new(
            serde_json::json!({ "status": "healthy" }),
        )),
    )
}

/// POST /api/v1/sql - Execute SQL query
async fn execute_sql(
    State(_state): State<RestApiState>,
    Json(req): Json<SqlRequest>,
) -> Result<Json<SuccessResponse<SqlResponse>>, (StatusCode, Json<ErrorResponse>)> {
    // SQL execution requires Arc<Mutex<RdbClient>> which is complex to set up in REST context
    // For now, return a placeholder indicating SQL should be accessed via gRPC
    // Full implementation would require refactoring to share SQL executor state
    Ok(Json(SuccessResponse::new(SqlResponse {
        rows_affected: None,
        rows: Some(vec![serde_json::json!({
            "message": format!("SQL execution via REST not yet implemented. Query received: {}", req.query),
            "hint": "Use gRPC SqlService for SQL queries or implement Arc<Mutex<RdbClient>> sharing"
        })]),
    })))
}

/// GET /api/v1/tables - List tables
async fn list_tables(
    State(_state): State<RestApiState>,
) -> Result<Json<SuccessResponse<TablesResponse>>, (StatusCode, Json<ErrorResponse>)> {
    // Listing tables requires SQL executor with Arc<Mutex<RdbClient>>
    // For now, return empty list with hint
    Ok(Json(SuccessResponse::new(TablesResponse {
        tables: vec!["(Table listing via REST not yet implemented - use gRPC)".to_string()],
    })))
}

/// GET /api/v1/kv/{key} - Get value
async fn get_kv(
    State(state): State<RestApiState>,
    Path(key): Path<String>,
) -> Result<Json<SuccessResponse<GetResponse>>, (StatusCode, Json<ErrorResponse>)> {
    let mut client = RdbClient::connect_direct(state.server_addr.clone(), "default")
        .await
        .map_err(|e| {
            error_response(
                StatusCode::SERVICE_UNAVAILABLE,
                "SERVICE_UNAVAILABLE",
                &format!("Failed to connect: {}", e),
            )
        })?;

    let value = client
        .raw_get(key.as_bytes().to_vec())
        .await
        .map_err(|e| {
            error_response(
                StatusCode::INTERNAL_SERVER_ERROR,
                "INTERNAL_ERROR",
                &e.to_string(),
            )
        })?
        .ok_or_else(|| error_response(StatusCode::NOT_FOUND, "NOT_FOUND", "Key not found"))?;

    Ok(Json(SuccessResponse::new(GetResponse {
        key,
        value: String::from_utf8_lossy(&value).to_string(),
    })))
}

/// PUT /api/v1/kv/{key} - Put value
async fn put_kv(
    State(state): State<RestApiState>,
    Path(key): Path<String>,
    Json(req): Json<PutRequest>,
) -> Result<(StatusCode, Json<SuccessResponse<serde_json::Value>>), (StatusCode, Json<ErrorResponse>)>
{
    let mut client = RdbClient::connect_direct(state.server_addr.clone(), &req.namespace)
        .await
        .map_err(|e| {
            error_response(
                StatusCode::SERVICE_UNAVAILABLE,
                "SERVICE_UNAVAILABLE",
                &format!("Failed to connect: {}", e),
            )
        })?;

    client
        .raw_put(key.as_bytes().to_vec(), req.value.as_bytes().to_vec())
        .await
        .map_err(|e| {
            error_response(
                StatusCode::INTERNAL_SERVER_ERROR,
                "INTERNAL_ERROR",
                &e.to_string(),
            )
        })?;

    Ok((
        StatusCode::OK,
        Json(SuccessResponse::new(
            serde_json::json!({ "key": key, "success": true }),
        )),
    ))
}

/// GET /api/v1/scan - Range scan
async fn scan_kv(
    State(state): State<RestApiState>,
    Query(params): Query<ScanQuery>,
) -> Result<Json<SuccessResponse<ScanResponse>>, (StatusCode, Json<ErrorResponse>)> {
    let mut client = RdbClient::connect_direct(state.server_addr.clone(), &params.namespace)
        .await
        .map_err(|e| {
            error_response(
                StatusCode::SERVICE_UNAVAILABLE,
                "SERVICE_UNAVAILABLE",
                &format!("Failed to connect: {}", e),
            )
        })?;

    let start_key = params.start.unwrap_or_default();
    let end_key = params.end.unwrap_or_else(|| format!("{}~", start_key));

    let (keys, values, _next) = client
        .raw_scan(
            start_key.as_bytes().to_vec(),
            end_key.as_bytes().to_vec(),
            100,
        )
        .await
        .map_err(|e| {
            error_response(
                StatusCode::INTERNAL_SERVER_ERROR,
                "INTERNAL_ERROR",
                &e.to_string(),
            )
        })?;

    let items: Vec<KvItem> = keys
        .into_iter()
        .zip(values.into_iter())
        .map(|(key, value)| KvItem {
            key: String::from_utf8_lossy(&key).to_string(),
            value: String::from_utf8_lossy(&value).to_string(),
        })
        .collect();

    Ok(Json(SuccessResponse::new(ScanResponse { items })))
}

/// GET /api/v1/regions/{id} - Get region info
async fn get_region(
    State(state): State<RestApiState>,
    Path(id): Path<u64>,
) -> Result<Json<SuccessResponse<RegionResponse>>, (StatusCode, Json<ErrorResponse>)> {
    let mut pd_client = PdClient::connect_any(&state.pd_endpoints)
        .await
        .map_err(|e| {
            error_response(
                StatusCode::SERVICE_UNAVAILABLE,
                "PD_UNAVAILABLE",
                &format!("Failed to connect to PD: {}", e),
            )
        })?;

    let region = pd_client
        .get_region(id)
        .await
        .map_err(|e| {
            error_response(
                StatusCode::INTERNAL_SERVER_ERROR,
                "INTERNAL_ERROR",
                &e.to_string(),
            )
        })?
        .ok_or_else(|| {
            error_response(
                StatusCode::NOT_FOUND,
                "NOT_FOUND",
                &format!("Region {} not found", id),
            )
        })?;

    Ok(Json(SuccessResponse::new(RegionResponse {
        id: region.id,
        peers: region.peers,
        leader_id: region.leader_id,
    })))
}

/// POST /api/v1/regions/{id}/add_peer - Add peer to region
async fn add_peer_to_region(
    State(state): State<RestApiState>,
    Path(id): Path<u64>,
    Json(req): Json<AddPeerRequest>,
) -> Result<Json<SuccessResponse<RegionResponse>>, (StatusCode, Json<ErrorResponse>)> {
    let mut pd_client = PdClient::connect_any(&state.pd_endpoints)
        .await
        .map_err(|e| {
            error_response(
                StatusCode::SERVICE_UNAVAILABLE,
                "PD_UNAVAILABLE",
                &format!("Failed to connect to PD: {}", e),
            )
        })?;

    let mut region = pd_client
        .get_region(id)
        .await
        .map_err(|e| {
            error_response(
                StatusCode::INTERNAL_SERVER_ERROR,
                "INTERNAL_ERROR",
                &e.to_string(),
            )
        })?
        .ok_or_else(|| {
            error_response(
                StatusCode::NOT_FOUND,
                "NOT_FOUND",
                &format!("Region {} not found", id),
            )
        })?;

    // Add peer if not already present
    if !region.peers.contains(&req.peer_id) {
        region.peers.push(req.peer_id);
        region.peers.sort();

        pd_client.put_region(region.clone()).await.map_err(|e| {
            error_response(
                StatusCode::INTERNAL_SERVER_ERROR,
                "INTERNAL_ERROR",
                &e.to_string(),
            )
        })?;
    }

    Ok(Json(SuccessResponse::new(RegionResponse {
        id: region.id,
        peers: region.peers,
        leader_id: region.leader_id,
    })))
}

/// POST /admin/member/add - first-boot compatible cluster join hook.
async fn add_member_legacy(
    State(state): State<RestApiState>,
    Json(req): Json<AddMemberRequestLegacy>,
) -> Result<(StatusCode, Json<SuccessResponse<serde_json::Value>>), (StatusCode, Json<ErrorResponse>)>
{
    let (peer_id, peer_addr) = resolve_join_peer(&state, &req).ok_or_else(|| {
        error_response(
            StatusCode::BAD_REQUEST,
            "INVALID_MEMBER",
            "Unable to resolve FlareDB peer id/address from join request",
        )
    })?;

    let mut pd_client = PdClient::connect_any(&state.pd_endpoints)
        .await
        .map_err(|e| {
            error_response(
                StatusCode::SERVICE_UNAVAILABLE,
                "PD_UNAVAILABLE",
                &format!("Failed to connect to PD: {}", e),
            )
        })?;

    let stores = pd_client.list_stores().await;
    let already_registered = stores.iter().any(|store| store.id == peer_id);

    pd_client
        .register_store(peer_id, peer_addr.clone())
        .await
        .map_err(|e| {
            error_response(
                StatusCode::INTERNAL_SERVER_ERROR,
                "INTERNAL_ERROR",
                &e.to_string(),
            )
        })?;

    let mut regions = pd_client.list_regions().await;
    if regions.is_empty() {
        pd_client
            .init_default_region(vec![state.store_id, peer_id])
            .await
            .map_err(|e| {
                error_response(
                    StatusCode::INTERNAL_SERVER_ERROR,
                    "INTERNAL_ERROR",
                    &e.to_string(),
                )
            })?;
        regions = vec![crate::pd_client::RegionInfo {
            id: 1,
            start_key: Vec::new(),
            end_key: Vec::new(),
            peers: vec![state.store_id, peer_id],
            leader_id: 0,
        }];
    }

    let mut updated_regions = Vec::new();
    for mut region in regions {
        if !region.peers.contains(&peer_id) {
            region.peers.push(peer_id);
            region.peers.sort_unstable();
            pd_client.put_region(region.clone()).await.map_err(|e| {
                error_response(
                    StatusCode::INTERNAL_SERVER_ERROR,
                    "INTERNAL_ERROR",
                    &e.to_string(),
                )
            })?;
            updated_regions.push(region.id);
        }
    }

    let status = if already_registered && updated_regions.is_empty() {
        StatusCode::CONFLICT
    } else if already_registered {
        StatusCode::OK
    } else {
        StatusCode::CREATED
    };

    Ok((
        status,
        Json(SuccessResponse::new(serde_json::json!({
            "peer_id": peer_id,
            "addr": peer_addr,
            "updated_regions": updated_regions,
            "already_registered": already_registered,
        }))),
    ))
}

fn resolve_join_peer(state: &RestApiState, req: &AddMemberRequestLegacy) -> Option<(u64, String)> {
    if let Ok(peer_id) = req.id.parse::<u64>() {
        if let Some(addr) = req
            .addr
            .clone()
            .or_else(|| state.configured_peers.get(&peer_id).cloned())
        {
            return Some((peer_id, addr));
        }
    }

    let candidate_host = socket_host(req.addr.as_deref().unwrap_or(&req.raft_addr));
    state
        .configured_peers
        .iter()
        .find(|(_, addr)| socket_host(addr) == candidate_host)
        .map(|(peer_id, addr)| (*peer_id, addr.clone()))
}

fn socket_host(addr: &str) -> String {
    let normalized = addr
        .trim()
        .trim_start_matches("http://")
        .trim_start_matches("https://")
        .split('/')
        .next()
        .unwrap_or(addr)
        .to_string();

    normalized
        .parse::<std::net::SocketAddr>()
        .map(|socket_addr| socket_addr.ip().to_string())
        .unwrap_or_else(|_| {
            normalized
                .rsplit_once(':')
                .map(|(host, _)| host.trim_matches(['[', ']']).to_string())
                .unwrap_or(normalized)
        })
}

/// Helper to create error response
fn error_response(
    status: StatusCode,
    code: &str,
    message: &str,
) -> (StatusCode, Json<ErrorResponse>) {
    (
        status,
        Json(ErrorResponse {
            error: ErrorDetail {
                code: code.to_string(),
                message: message.to_string(),
                details: None,
            },
            meta: ResponseMeta::new(),
        }),
    )
}

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

    fn test_state() -> RestApiState {
        RestApiState {
            server_addr: "127.0.0.1:50052".to_string(),
            pd_endpoints: vec!["127.0.0.1:2479".to_string()],
            store_id: 1,
            configured_peers: HashMap::from([
                (1, "10.100.0.11:50052".to_string()),
                (2, "10.100.0.12:50052".to_string()),
                (3, "10.100.0.13:50052".to_string()),
            ]),
        }
    }

    #[test]
    fn resolve_join_peer_uses_numeric_id_when_available() {
        let state = test_state();
        let req = AddMemberRequestLegacy {
            id: "2".to_string(),
            raft_addr: "10.100.0.12:2380".to_string(),
            addr: None,
        };

        assert_eq!(
            resolve_join_peer(&state, &req),
            Some((2, "10.100.0.12:50052".to_string()))
        );
    }

    #[test]
    fn resolve_join_peer_matches_host_from_raft_addr() {
        let state = test_state();
        let req = AddMemberRequestLegacy {
            id: "node02".to_string(),
            raft_addr: "10.100.0.12:2380".to_string(),
            addr: None,
        };

        assert_eq!(
            resolve_join_peer(&state, &req),
            Some((2, "10.100.0.12:50052".to_string()))
        );
    }
}
