//! Tier capability matrix (ADR-029).
//!
//! Defines which operations and surfaces a project gains access to at each
//! tier. Used by the CLI (`ore tier status`), REST API (`/tier/status`),
//! GraphQL (`tierStatus`), and MCP (`ontoref_tier_status`).

use serde::Serialize;

use crate::tier::Tier;

/// A named capability gated on a minimum tier.
#[derive(Debug, Clone, Serialize)]
pub struct TierCapability {
    pub id: &'static str,
    pub label: &'static str,
    pub min_tier: Tier,
    pub description: &'static str,
}

static CAPABILITIES: &[TierCapability] = &[
    TierCapability {
        id: "ncl-export",
        label: "NCL export & query",
        min_tier: Tier::Tier0,
        description: "Export any .ncl file as typed JSON via daemon cache or direct nickel call",
    },
    TierCapability {
        id: "describe",
        label: "Describe / self-knowledge",
        min_tier: Tier::Tier0,
        description: "describe project, capabilities, config, connections, state — full \
                      self-knowledge surface",
    },
    TierCapability {
        id: "adrs",
        label: "Architecture Decision Records",
        min_tier: Tier::Tier0,
        description: "Author, query, and validate ADRs as typed NCL with per-constraint pass/fail",
    },
    TierCapability {
        id: "reflection-modes",
        label: "Reflection modes (DAG workflows)",
        min_tier: Tier::Tier0,
        description: "Define and execute DAG-validated operational modes via the reflection layer",
    },
    TierCapability {
        id: "positioning",
        label: "Positioning layer (ADR-035)",
        min_tier: Tier::Tier0,
        description: "Marketing as queryable protocol surface: value-props, audiences, campaigns",
    },
    TierCapability {
        id: "migrations",
        label: "Protocol migrations",
        min_tier: Tier::Tier0,
        description: "Track and apply protocol evolution migrations across project adoption",
    },
    TierCapability {
        id: "substrate",
        label: "Substrate (commit layer + state root)",
        min_tier: Tier::Tier1,
        description: "Content-addressed ontology commits with state root — enables signed \
                      attestations and audit trails (ADR-023)",
    },
    TierCapability {
        id: "signed-artifacts",
        label: "Signed artifacts & witnesses",
        min_tier: Tier::Tier1,
        description: "Cryptographically signed witnesses on any authoritative-state mutation",
    },
    TierCapability {
        id: "ops-dispatch",
        label: "Ops dispatch (typed mutations)",
        min_tier: Tier::Tier2,
        description: "All state mutations route through declared domain operations — the agent \
                      action boundary (ADR-024)",
    },
    TierCapability {
        id: "catalogued-ops",
        label: "Catalogued operations (ADR-026)",
        min_tier: Tier::Tier2,
        description: "Ops registered in the artifact catalog with validators, preconditions, and \
                      SLA declarations",
    },
    TierCapability {
        id: "runtime-mutation",
        label: "Runtime-only state mutation",
        min_tier: Tier::Tier2,
        description: "NCL files become render targets; all writes go through the ops runtime — \
                      human and AI paths unified",
    },
    TierCapability {
        id: "witness-verification",
        label: "Witness verification at boundary",
        min_tier: Tier::Tier2,
        description: "Every op produces a verifiable witness; validators reject ill-typed state \
                      without a valid witness chain",
    },
];

/// Capability split for a given tier: what is available vs locked.
#[derive(Debug, Serialize)]
pub struct CapabilityView {
    pub available: Vec<CapabilityRef>,
    pub locked: Vec<CapabilityRef>,
}

/// A serialisable reference into the static capability table.
#[derive(Debug, Clone, Serialize)]
pub struct CapabilityRef {
    pub id: &'static str,
    pub label: &'static str,
    pub min_tier: Tier,
    pub description: &'static str,
}

impl From<&'static TierCapability> for CapabilityRef {
    fn from(c: &'static TierCapability) -> Self {
        CapabilityRef {
            id: c.id,
            label: c.label,
            min_tier: c.min_tier,
            description: c.description,
        }
    }
}

/// Returns the capability split for the given tier.
pub fn capability_view(current: Tier) -> CapabilityView {
    let (available, locked): (Vec<_>, Vec<_>) =
        CAPABILITIES.iter().partition(|c| c.min_tier <= current);
    CapabilityView {
        available: available.into_iter().map(CapabilityRef::from).collect(),
        locked: locked.into_iter().map(CapabilityRef::from).collect(),
    }
}

/// The tier immediately following the given one, if any.
pub fn next_tier(current: Tier) -> Option<Tier> {
    match current {
        Tier::Tier0 => Some(Tier::Tier1),
        Tier::Tier1 => Some(Tier::Tier2),
        Tier::Tier2 => None,
    }
}

/// Forward projection: what the next tier adds, if a next tier exists.
#[derive(Debug, Serialize)]
pub struct TierProjection {
    pub from: Tier,
    pub to: Option<Tier>,
    pub adds: Vec<CapabilityRef>,
    /// Human-readable narrative summarising the value proposition of the
    /// next tier.
    pub narrative: &'static str,
}

/// Compute the forward projection from the current tier.
pub fn tier_projection(current: Tier) -> TierProjection {
    let to = next_tier(current);
    let (adds, narrative) = match to {
        Some(Tier::Tier1) => (
            CAPABILITIES
                .iter()
                .filter(|c| c.min_tier == Tier::Tier1)
                .map(CapabilityRef::from)
                .collect(),
            "Tier-1 adopts the substrate layer (ADR-023): content-addressed ontology commits, a \
             state root, and signed artifacts. Every mutation becomes auditable and reproducible. \
             Prerequisite for ops-dispatch at Tier-2.",
        ),
        Some(Tier::Tier2) => (
            CAPABILITIES
                .iter()
                .filter(|c| c.min_tier == Tier::Tier2)
                .map(CapabilityRef::from)
                .collect(),
            "Tier-2 adopts the operations layer (ADR-024): all state mutations route through \
             declared domain operations. NCL files become render targets. The runtime is the only \
             mutation path; witnesses are cryptographically verifiable; validators reject \
             ill-typed state.",
        ),
        None => (
            vec![],
            "Already at the highest tier. No further evolution is defined.",
        ),
        Some(_) => unreachable!(),
    };
    TierProjection {
        from: current,
        to,
        adds,
        narrative,
    }
}

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

    #[test]
    fn tier0_has_six_available() {
        let v = capability_view(Tier::Tier0);
        assert_eq!(v.available.len(), 6);
        assert!(!v.locked.is_empty());
    }

    #[test]
    fn tier2_all_available() {
        let v = capability_view(Tier::Tier2);
        assert_eq!(v.locked.len(), 0);
        assert_eq!(v.available.len(), CAPABILITIES.len());
    }

    #[test]
    fn next_tier_chain() {
        assert_eq!(next_tier(Tier::Tier0), Some(Tier::Tier1));
        assert_eq!(next_tier(Tier::Tier1), Some(Tier::Tier2));
        assert_eq!(next_tier(Tier::Tier2), None);
    }

    #[test]
    fn projection_from_tier0_adds_substrate_capabilities() {
        let p = tier_projection(Tier::Tier0);
        assert_eq!(p.to, Some(Tier::Tier1));
        assert!(p.adds.iter().any(|c| c.id == "substrate"));
        assert!(p.adds.iter().any(|c| c.id == "signed-artifacts"));
    }

    #[test]
    fn projection_from_tier2_is_terminal() {
        let p = tier_projection(Tier::Tier2);
        assert!(p.to.is_none());
        assert!(p.adds.is_empty());
    }
}
