use crate::config::OmScopeConfig;
use crate::error::{AxiomError, Result};
use crate::om::{OmScope, build_scope_key};

use super::OmScopeBinding;

const DEFAULT_OM_SCOPE: &str = "session";
pub(super) const ENV_OM_SCOPE: &str = "AXIOMSYNC_OM_SCOPE";

pub(super) fn resolve_om_scope_binding_for_session_with_config(
    session_id: &str,
    config: &OmScopeConfig,
) -> Result<OmScopeBinding> {
    resolve_om_scope_binding(
        session_id,
        config.scope.as_deref(),
        config.thread_id.as_deref(),
        config.resource_id.as_deref(),
    )
}

pub(super) fn resolve_om_scope_binding(
    session_id: &str,
    scope_raw: Option<&str>,
    thread_id: Option<&str>,
    resource_id: Option<&str>,
) -> Result<OmScopeBinding> {
    let scope_token = scope_raw
        .map(str::trim)
        .filter(|value| !value.is_empty())
        .unwrap_or(DEFAULT_OM_SCOPE)
        .to_ascii_lowercase();
    let scope = match scope_token.as_str() {
        "session" => OmScope::Session,
        "thread" => OmScope::Thread,
        "resource" => OmScope::Resource,
        other => {
            return Err(AxiomError::Validation(format!(
                "invalid {ENV_OM_SCOPE}: {other} (expected: session|thread|resource)"
            )));
        }
    };

    resolve_om_scope_binding_explicit(session_id, scope, thread_id, resource_id)
}

pub(super) fn resolve_om_scope_binding_explicit(
    session_id: &str,
    scope: OmScope,
    thread_id: Option<&str>,
    resource_id: Option<&str>,
) -> Result<OmScopeBinding> {
    let thread_id = normalize_scope_identifier(thread_id);
    let resource_id = normalize_scope_identifier(resource_id);
    let scope_key = build_scope_key(
        scope,
        Some(session_id),
        thread_id.as_deref(),
        resource_id.as_deref(),
    )
    .map_err(|err| AxiomError::Validation(err.to_string()))?;

    let (resolved_session_id, resolved_thread_id, resolved_resource_id) = match scope {
        OmScope::Session => (Some(session_id.to_string()), None, None),
        OmScope::Thread => (None, thread_id, resource_id),
        OmScope::Resource => (None, None, resource_id),
    };
    Ok(OmScopeBinding {
        scope,
        scope_key,
        session_id: resolved_session_id,
        thread_id: resolved_thread_id,
        resource_id: resolved_resource_id,
    })
}

#[cfg(test)]
pub(super) fn parse_env_enabled_default_true(raw: Option<&str>) -> bool {
    let Some(raw) = raw.map(str::trim) else {
        return true;
    };
    if raw.is_empty() {
        return true;
    }
    !matches!(
        raw.to_ascii_lowercase().as_str(),
        "0" | "false" | "no" | "off" | "disabled"
    )
}

fn normalize_scope_identifier(raw: Option<&str>) -> Option<String> {
    raw.map(str::trim)
        .filter(|value| !value.is_empty())
        .map(ToString::to_string)
}
