//! Read/write helpers for the `gateway_projects` LWW-map collection.
//!
//! Gateway-project entries are keyed by `name` and store the URL of each
//! project container registered with the gateway.

use bft_json_crdt::json_crdt::{JsonValue, *};
use bft_json_crdt::op::ROOT_ID;
use serde_json::json;

use super::super::state::{apply_and_persist, get_crdt, rebuild_gateway_project_index};
use super::super::types::{GatewayProjectCrdt, GatewayProjectView};
use super::list_id_at;

/// Write or update a gateway-project entry keyed by `name`.
///
/// If an entry for `name` already exists it is updated in place;
/// otherwise a new entry is inserted.
pub fn write_gateway_project(name: &str, url: &str) {
    let Some(state_mutex) = get_crdt() else {
        return;
    };
    let Ok(mut state) = state_mutex.lock() else {
        return;
    };

    if let Some(&idx) = state.gateway_project_index.get(name) {
        apply_and_persist(&mut state, |s| {
            s.crdt.doc.gateway_projects[idx].url.set(url.to_string())
        });
    } else {
        let entry: JsonValue = json!({
            "name": name,
            "url": url,
        })
        .into();
        apply_and_persist(&mut state, |s| {
            s.crdt.doc.gateway_projects.insert(ROOT_ID, entry)
        });
        state.gateway_project_index = rebuild_gateway_project_index(&state.crdt);
    }
}

/// Read all gateway-project entries.
pub fn read_all_gateway_projects() -> Option<Vec<GatewayProjectView>> {
    let state_mutex = get_crdt()?;
    let state = state_mutex.lock().ok()?;
    let mut out = Vec::new();
    for entry in state.crdt.doc.gateway_projects.iter() {
        if let Some(v) = extract_gateway_project_view(entry) {
            out.push(v);
        }
    }
    Some(out)
}

/// Read a single gateway-project entry by `name`.
pub fn read_gateway_project(name: &str) -> Option<GatewayProjectView> {
    let state_mutex = get_crdt()?;
    let state = state_mutex.lock().ok()?;
    let &idx = state.gateway_project_index.get(name)?;
    extract_gateway_project_view(&state.crdt.doc.gateway_projects[idx])
}

/// Tombstone a gateway-project entry by `name`.
///
/// Returns `true` if the entry existed and a delete op was issued.
pub fn delete_gateway_project(name: &str) -> bool {
    let Some(state_mutex) = get_crdt() else {
        return false;
    };
    let Ok(mut state) = state_mutex.lock() else {
        return false;
    };
    let Some(&idx) = state.gateway_project_index.get(name) else {
        return false;
    };
    let Some(op_id) = list_id_at(&state.crdt.doc.gateway_projects, idx) else {
        return false;
    };
    apply_and_persist(&mut state, |s| s.crdt.doc.gateway_projects.delete(op_id));
    state.gateway_project_index = rebuild_gateway_project_index(&state.crdt);
    true
}

/// Convert a CRDT gateway-project entry into its read-only view representation.
pub(super) fn extract_gateway_project_view(
    entry: &GatewayProjectCrdt,
) -> Option<GatewayProjectView> {
    let name = match entry.name.view() {
        JsonValue::String(s) if !s.is_empty() => s,
        _ => return None,
    };
    let url = match entry.url.view() {
        JsonValue::String(s) => s,
        _ => String::new(),
    };
    Some(GatewayProjectView { name, url })
}
