//! ADR-0019 Authority Pluralism — integration tests for the
//! `Principal` source-URI round trip and the `compose`/`root_operator`
//! invariants.
//!
//! In-crate unit tests cover the same cases plus a few negative paths
//! (`src/principal.rs#tests`). This file is the externally-visible
//! contract test: it exercises the public API exactly as a downstream
//! crate would, so a refactor that accidentally collapses a `pub`
//! surface fails here too.

use cellos_core::{
    AuthorityScope, Capability, DelegateId, ExternalId, OperatorId, PlatformId, Principal,
    TrustRoot,
};

fn op(id: &str) -> Principal {
    Principal::Operator {
        id: OperatorId(id.to_owned()),
    }
}

#[test]
fn operator_round_trip() {
    let p = op("op-123");
    let uri = p.to_source_uri();
    assert_eq!(uri, "principal://operator/op-123");
    assert_eq!(Principal::from_source_uri(&uri).unwrap(), p);
}

#[test]
fn platform_round_trip() {
    let p = Principal::Platform {
        id: PlatformId("hosted-ctrl-plane-prod".to_owned()),
    };
    let uri = p.to_source_uri();
    assert_eq!(uri, "principal://platform/hosted-ctrl-plane-prod");
    assert_eq!(Principal::from_source_uri(&uri).unwrap(), p);
}

#[test]
fn federated_round_trip() {
    let p = Principal::Federated {
        trust_root: TrustRoot::Ado {
            realm: "realm-acme".to_owned(),
        },
        identity: ExternalId("user-7842".to_owned()),
    };
    let uri = p.to_source_uri();
    assert_eq!(
        uri,
        "principal://federated/ado/realm-acme/identity/user-7842"
    );
    assert_eq!(Principal::from_source_uri(&uri).unwrap(), p);
}

#[test]
fn delegate_round_trip_one_level() {
    let p = Principal::compose(
        op("op-123"),
        DelegateId("llm-claude-456".to_owned()),
        AuthorityScope::from_capabilities([Capability::ToolApply, Capability::ToolGet]),
    )
    .expect("narrowing scope must compose");
    let uri = p.to_source_uri();
    assert_eq!(Principal::from_source_uri(&uri).unwrap(), p);
}

#[test]
fn delegate_round_trip_two_levels() {
    let outer = Principal::compose(
        op("op-123"),
        DelegateId("bridge-A".to_owned()),
        AuthorityScope::from_capabilities([Capability::ToolApply, Capability::ToolGet]),
    )
    .unwrap();
    let inner = Principal::compose(
        outer,
        DelegateId("bridge-B".to_owned()),
        AuthorityScope::from_capabilities([Capability::ToolGet]),
    )
    .unwrap();
    let uri = inner.to_source_uri();
    assert_eq!(Principal::from_source_uri(&uri).unwrap(), inner);
}

#[test]
fn compose_narrows_scope_returns_ok() {
    Principal::compose(
        op("op-123"),
        DelegateId("session-1".to_owned()),
        AuthorityScope::from_capabilities([Capability::ToolApply]),
    )
    .expect("subset of root scope must compose");
}

#[test]
fn compose_broadens_scope_returns_err() {
    let narrow = Principal::compose(
        op("op-123"),
        DelegateId("session-A".to_owned()),
        AuthorityScope::from_capabilities([Capability::ToolGet]),
    )
    .unwrap();
    let err = Principal::compose(
        narrow,
        DelegateId("session-B".to_owned()),
        AuthorityScope::from_capabilities([Capability::ToolApply]),
    )
    .unwrap_err();
    assert_eq!(err.missing, Capability::ToolApply);
}

#[test]
fn root_operator_finds_human_at_depth() {
    let d1 = Principal::compose(
        op("op-human"),
        DelegateId("d1".to_owned()),
        AuthorityScope::from_capabilities([Capability::ToolApply]),
    )
    .unwrap();
    let d2 = Principal::compose(
        d1,
        DelegateId("d2".to_owned()),
        AuthorityScope::from_capabilities([Capability::ToolApply]),
    )
    .unwrap();
    assert_eq!(d2.root_operator(), Some(&OperatorId("op-human".to_owned())));
}

#[test]
fn root_operator_returns_none_for_platform() {
    let p = Principal::Platform {
        id: PlatformId("hosted-ctrl-plane-prod".to_owned()),
    };
    assert_eq!(p.root_operator(), None);
}

#[test]
fn root_operator_returns_none_for_federated() {
    let p = Principal::Federated {
        trust_root: TrustRoot::GitHub {
            org: "anthropic".to_owned(),
        },
        identity: ExternalId("octocat".to_owned()),
    };
    assert_eq!(p.root_operator(), None);
}
