use crate::api::error::ApiError;
use crate::api::state::AppState;

/// Share-rule levels for a shared address book. Wire integers match the
/// Flutter client's `ShareRule` enum at flutter/lib/common/hbbs/hbbs.dart:210.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Rule {
    Read = 1,
    ReadWrite = 2,
    Full = 3,
}

impl Rule {
    pub fn from_i64(v: i64) -> Option<Self> {
        match v {
            1 => Some(Rule::Read),
            2 => Some(Rule::ReadWrite),
            3 => Some(Rule::Full),
            _ => None,
        }
    }
}

/// Enforce that `caller` has at least `needed` access on `ab_guid`. Used at
/// the top of every AB handler. Resolution lives in
/// `Database::ab_resolve_rule` and considers (a) AB ownership and (b) the
/// largest matching rule across direct and device-group shares.
pub async fn enforce(
    state: &AppState,
    caller_user_id: i64,
    ab_guid: &str,
    needed: Rule,
) -> Result<(), ApiError> {
    let resolved = state
        .db
        .ab_resolve_rule(caller_user_id, ab_guid)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;
    let Some(have) = resolved.and_then(Rule::from_i64) else {
        // Either the AB doesn't exist or the caller has no relationship with
        // it. Surface as "not allowed" so we don't leak existence.
        return Err(ApiError::Forbidden("not allowed".into()));
    };
    if have >= needed {
        Ok(())
    } else {
        Err(ApiError::Forbidden("read-only".into()))
    }
}
