use std::collections::BTreeSet;
use std::fs;
use std::path::Path;
use std::time::{SystemTime, UNIX_EPOCH};

use chio_core::{Keypair, PublicKey};
use chio_credentials::{
    build_agent_passport, create_passport_presentation_challenge_with_reference,
    create_signed_passport_verifier_policy,
    default_oid4vci_passport_issuer_metadata_with_signing_key,
    default_oid4vci_passport_issuer_metadata_with_status_distribution,
    ensure_signed_passport_verifier_policy_active, evaluate_agent_passport,
    issue_reputation_credential_with_enterprise_identity, passport_artifact_id,
    present_agent_passport, respond_to_oid4vp_request, respond_to_passport_presentation_challenge,
    verify_agent_passport, verify_passport_presentation_response_with_policy,
    verify_signed_oid4vp_request_object_with_any_key, verify_signed_passport_verifier_policy,
    AgentPassport, ChioCredentialEvidence, AttestationWindow, EnterpriseIdentityProvenance,
    Oid4vciCredentialOffer, Oid4vciCredentialRequest, Oid4vciTokenRequest, Oid4vciTokenResponse,
    Oid4vpPresentationVerification, Oid4vpRequestObject, Oid4vpVerifierMetadata,
    PassportLifecycleResolution, PassportLifecycleState, PassportPresentationChallenge,
    PassportPresentationOptions, PassportPresentationResponse, PassportStatusDistribution,
    PassportVerifierPolicy, PassportVerifierPolicyReference, PortableJwkSet,
    SignedPassportVerifierPolicy, OID4VP_VERIFIER_METADATA_PATH,
};
use chio_credentials::{synthesize_trust_tier, TrustTier};
use chio_did::DidChio;
use chio_kernel::{
    behavioral_anomaly_score, compliance_score, ComplianceReport, ComplianceScoreConfig,
    ComplianceScoreInputs, EmaBaselineState, EvidenceChildReceiptScope, EvidenceExportQuery,
};
use chio_reputation::{compute_local_scorecard, ReputationConfig};
use chio_store_sqlite::SqliteReceiptStore;
use url::Url;

use crate::issuance::build_local_reputation_corpus;
use crate::passport_verifier::{
    PassportIssuanceOfferRegistry, PassportStatusListResponse, PassportStatusRegistry,
    PassportStatusRevocationRequest, PassportVerifierChallengeStore, PublishPassportStatusRequest,
    VerifierPolicyRegistry,
};
use crate::trust_control::{
    CreateIdentityAssertionRequest, CreateOid4vpRequest, CreatePassportChallengeRequest,
    CreatePassportChallengeResponse, VerifierPolicyListResponse, VerifyPassportChallengeRequest,
};
use crate::{load_or_create_authority_keypair, CliError};

fn unix_now() -> u64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map(|duration| duration.as_secs())
        .unwrap_or(0)
}

fn ensure_parent_dir(path: &Path) -> Result<(), CliError> {
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)?;
    }
    Ok(())
}

fn require_verifier_policy_registry_path(path: Option<&Path>) -> Result<&Path, CliError> {
    path.ok_or_else(|| {
        CliError::Other(
            "verifier policy commands require --verifier-policies-file <path> when not using --control-url"
                .to_string(),
        )
    })
}

fn require_passport_status_registry_path(path: Option<&Path>) -> Result<&Path, CliError> {
    path.ok_or_else(|| {
        CliError::Other(
            "passport lifecycle commands require --passport-statuses-file <path> when not using --control-url"
                .to_string(),
        )
    })
}

fn require_passport_issuance_registry_path(path: Option<&Path>) -> Result<&Path, CliError> {
    path.ok_or_else(|| {
        CliError::Other(
            "passport issuance commands require --passport-issuance-offers-file <path> when not using --control-url"
                .to_string(),
        )
    })
}

fn require_credential_issuer_url(value: Option<&str>) -> Result<&str, CliError> {
    value.ok_or_else(|| {
        CliError::Other(
            "passport issuance commands require --issuer-url <url> when not using --control-url"
                .to_string(),
        )
    })
}

fn load_verifier_policy_registry_for_admin(
    path: &Path,
) -> Result<VerifierPolicyRegistry, CliError> {
    if path.exists() {
        VerifierPolicyRegistry::load(path)
    } else {
        Ok(VerifierPolicyRegistry::default())
    }
}

fn load_passport_status_registry_for_admin(
    path: &Path,
) -> Result<PassportStatusRegistry, CliError> {
    if path.exists() {
        PassportStatusRegistry::load(path)
    } else {
        Ok(PassportStatusRegistry::default())
    }
}

fn load_passport_issuance_registry_for_admin(
    path: &Path,
) -> Result<PassportIssuanceOfferRegistry, CliError> {
    if path.exists() {
        PassportIssuanceOfferRegistry::load(path)
    } else {
        Ok(PassportIssuanceOfferRegistry::default())
    }
}

fn load_signed_passport_verifier_policy(
    path: &Path,
) -> Result<SignedPassportVerifierPolicy, CliError> {
    Ok(serde_json::from_slice(&fs::read(path)?)?)
}

fn load_oid4vci_offer(path: &Path) -> Result<Oid4vciCredentialOffer, CliError> {
    Ok(serde_json::from_slice(&fs::read(path)?)?)
}

fn load_oid4vci_token(path: &Path) -> Result<Oid4vciTokenResponse, CliError> {
    Ok(serde_json::from_slice(&fs::read(path)?)?)
}

fn fetch_json_url<T: for<'de> serde::Deserialize<'de>>(url: &str) -> Result<T, CliError> {
    match ureq::get(url).call() {
        Ok(response) => Ok(serde_json::from_reader(response.into_reader())?),
        Err(ureq::Error::Status(status, response)) => {
            let message = response
                .into_string()
                .ok()
                .filter(|body| !body.trim().is_empty())
                .unwrap_or_else(|| format!("request failed with status {status}"));
            Err(CliError::Other(message))
        }
        Err(ureq::Error::Transport(error)) => Err(CliError::Other(format!(
            "transport request failed: {error}"
        ))),
    }
}

fn fetch_text_url(url: &str) -> Result<String, CliError> {
    match ureq::get(url).call() {
        Ok(response) => response
            .into_string()
            .map_err(|error| CliError::Other(format!("failed to read response body: {error}"))),
        Err(ureq::Error::Status(status, response)) => {
            let message = response
                .into_string()
                .ok()
                .filter(|body| !body.trim().is_empty())
                .unwrap_or_else(|| format!("request failed with status {status}"));
            Err(CliError::Other(message))
        }
        Err(ureq::Error::Transport(error)) => Err(CliError::Other(format!(
            "transport request failed: {error}"
        ))),
    }
}

fn post_json_url<B: serde::Serialize, T: for<'de> serde::Deserialize<'de>>(
    url: &str,
    body: &B,
) -> Result<T, CliError> {
    match ureq::post(url).send_json(serde_json::to_value(body)?) {
        Ok(response) => Ok(serde_json::from_reader(response.into_reader())?),
        Err(ureq::Error::Status(status, response)) => {
            let message = response
                .into_string()
                .ok()
                .filter(|body| !body.trim().is_empty())
                .unwrap_or_else(|| format!("request failed with status {status}"));
            Err(CliError::Other(message))
        }
        Err(ureq::Error::Transport(error)) => Err(CliError::Other(format!(
            "transport request failed: {error}"
        ))),
    }
}

fn post_form_url<T: for<'de> serde::Deserialize<'de>>(
    url: &str,
    fields: &[(&str, &str)],
) -> Result<T, CliError> {
    let body = serde_urlencoded::to_string(fields)
        .map_err(|error| CliError::Other(format!("failed to encode form body: {error}")))?;
    match ureq::post(url)
        .set("Content-Type", "application/x-www-form-urlencoded")
        .send_string(&body)
    {
        Ok(response) => Ok(serde_json::from_reader(response.into_reader())?),
        Err(ureq::Error::Status(status, response)) => {
            let message = response
                .into_string()
                .ok()
                .filter(|value| !value.trim().is_empty())
                .unwrap_or_else(|| format!("request failed with status {status}"));
            Err(CliError::Other(message))
        }
        Err(ureq::Error::Transport(error)) => Err(CliError::Other(format!(
            "transport request failed: {error}"
        ))),
    }
}

fn load_text_file(path: &Path) -> Result<String, CliError> {
    let bytes = fs::read(path)?;
    let value = String::from_utf8(bytes).map_err(|error| {
        CliError::Other(format!("{} is not valid UTF-8: {error}", path.display()))
    })?;
    Ok(value.trim().to_string())
}

fn oid4vp_request_url_from_launch_url(url: &str) -> Result<String, CliError> {
    let parsed = Url::parse(url)
        .map_err(|error| CliError::Other(format!("invalid OID4VP launch URL `{url}`: {error}")))?;
    parsed
        .query_pairs()
        .find_map(|(key, value)| (key == "request_uri").then(|| value.into_owned()))
        .filter(|value| !value.trim().is_empty())
        .ok_or_else(|| {
            CliError::Other(
                "OID4VP launch URL must include a non-empty request_uri query parameter"
                    .to_string(),
            )
        })
}

fn oid4vp_verifier_metadata_url(request_url: &str) -> Result<String, CliError> {
    let parsed = Url::parse(request_url).map_err(|error| {
        CliError::Other(format!(
            "invalid OID4VP request URL `{request_url}`: {error}"
        ))
    })?;
    let scheme = parsed.scheme();
    if scheme != "http" && scheme != "https" {
        return Err(CliError::Other(
            "OID4VP request URL must use http or https".to_string(),
        ));
    }
    let host = parsed
        .host_str()
        .ok_or_else(|| CliError::Other("OID4VP request URL must include a host".to_string()))?;
    let mut metadata = Url::parse(&format!("{scheme}://{host}")).map_err(|error| {
        CliError::Other(format!(
            "failed to construct verifier metadata URL: {error}"
        ))
    })?;
    if let Some(port) = parsed.port() {
        metadata
            .set_port(Some(port))
            .map_err(|_| CliError::Other("failed to preserve verifier port".to_string()))?;
    }
    metadata.set_path(OID4VP_VERIFIER_METADATA_PATH);
    metadata.set_query(None);
    metadata.set_fragment(None);
    Ok(metadata.to_string())
}

fn verifier_public_keys_from_jwks(jwks: &PortableJwkSet) -> Result<Vec<PublicKey>, CliError> {
    let mut keys = Vec::new();
    for entry in &jwks.keys {
        keys.push(entry.jwk.to_public_key().map_err(|error| {
            CliError::Other(format!("portable verifier JWKS entry is invalid: {error}"))
        })?);
    }
    if keys.is_empty() {
        return Err(CliError::Other(
            "portable verifier JWKS did not publish any signing keys".to_string(),
        ));
    }
    Ok(keys)
}

fn load_existing_keypair(path: &Path) -> Result<Keypair, CliError> {
    let seed_hex = fs::read_to_string(path).map_err(|error| {
        if error.kind() == std::io::ErrorKind::NotFound {
            CliError::Other(format!("required seed file not found: {}", path.display()))
        } else {
            CliError::Io(error)
        }
    })?;
    Keypair::from_seed_hex(seed_hex.trim()).map_err(CliError::from)
}

pub(crate) struct PassportPolicyCreateArgs<'a> {
    pub output: &'a Path,
    pub policy_id: &'a str,
    pub verifier: &'a str,
    pub signing_seed_file: &'a Path,
    pub policy_path: &'a Path,
    pub expires_at: u64,
    pub verifier_policies_file: Option<&'a Path>,
    pub json_output: bool,
    pub control_url: Option<&'a str>,
    pub control_token: Option<&'a str>,
}

pub(crate) struct PassportChallengeCreateArgs<'a> {
    pub output: &'a Path,
    pub verifier: &'a str,
    pub ttl_secs: u64,
    pub issuers: &'a [String],
    pub max_credentials: Option<usize>,
    pub policy_path: Option<&'a Path>,
    pub policy_id: Option<&'a str>,
    pub verifier_policies_file: Option<&'a Path>,
    pub verifier_challenge_db: Option<&'a Path>,
    pub json_output: bool,
    pub control_url: Option<&'a str>,
    pub control_token: Option<&'a str>,
}

pub(crate) struct PassportOid4vpRequestCreateArgs<'a> {
    pub output: Option<&'a Path>,
    pub disclosure_claims: &'a [String],
    pub issuer_allowlist: &'a [String],
    pub ttl_secs: Option<u64>,
    pub identity_subject: Option<&'a str>,
    pub identity_continuity_id: Option<&'a str>,
    pub identity_provider: Option<&'a str>,
    pub identity_session_hint: Option<&'a str>,
    pub identity_ttl_secs: Option<u64>,
    pub json_output: bool,
    pub control_url: Option<&'a str>,
    pub control_token: Option<&'a str>,
}

pub(crate) struct PassportOid4vpRespondArgs<'a> {
    pub input: &'a Path,
    pub request_url: Option<&'a str>,
    pub same_device_url: Option<&'a str>,
    pub cross_device_url: Option<&'a str>,
    pub holder_seed_file: &'a Path,
    pub output: Option<&'a Path>,
    pub submit: bool,
    pub submit_url: Option<&'a str>,
    pub at: Option<u64>,
    pub json_output: bool,
}

fn validity_seconds(validity_days: u32) -> u64 {
    u64::from(validity_days) * 86_400
}

fn load_enterprise_identity_provenance(
    path: Option<&Path>,
) -> Result<Option<EnterpriseIdentityProvenance>, CliError> {
    path.map(|path| {
        let context: chio_core::EnterpriseIdentityContext =
            serde_json::from_slice(&fs::read(path)?)?;
        Ok(EnterpriseIdentityProvenance::from(&context))
    })
    .transpose()
}

fn summarize_enterprise_provenance(
    provenance: &[EnterpriseIdentityProvenance],
) -> (usize, Vec<String>) {
    (
        provenance.len(),
        provenance
            .iter()
            .map(|entry| entry.provider_id.clone())
            .collect(),
    )
}

fn passport_status_distribution(
    resolve_urls: &[String],
    cache_ttl_secs: Option<u64>,
) -> PassportStatusDistribution {
    PassportStatusDistribution {
        resolve_urls: resolve_urls.to_vec(),
        cache_ttl_secs,
    }
}

fn local_passport_issuer_metadata(
    issuer_url: &str,
    signing_seed_file: Option<&Path>,
    passport_status_url: Option<&str>,
    passport_status_cache_ttl_secs: Option<u64>,
) -> Result<chio_credentials::Oid4vciCredentialIssuerMetadata, CliError> {
    let distribution = passport_status_url
        .map(|resolve_url| {
            passport_status_distribution(&[resolve_url.to_string()], passport_status_cache_ttl_secs)
        })
        .unwrap_or_default();
    let portable_signing_public_key = signing_seed_file
        .map(load_or_create_authority_keypair)
        .transpose()?
        .map(|keypair| keypair.public_key());
    default_oid4vci_passport_issuer_metadata_with_signing_key(
        issuer_url,
        distribution,
        portable_signing_public_key.as_ref(),
    )
    .map_err(CliError::from)
}

fn passport_lifecycle_reason(lifecycle: &PassportLifecycleResolution) -> String {
    match lifecycle.state {
        PassportLifecycleState::Active => "passport lifecycle state is active".to_string(),
        PassportLifecycleState::Stale => lifecycle
            .updated_at
            .map(|updated_at| {
                format!("passport lifecycle state is stale: last updated at {updated_at}")
            })
            .unwrap_or_else(|| "passport lifecycle state is stale".to_string()),
        PassportLifecycleState::Superseded => lifecycle
            .superseded_by
            .as_deref()
            .map(|passport_id| format!("passport lifecycle state is superseded by {passport_id}"))
            .unwrap_or_else(|| "passport lifecycle state is superseded".to_string()),
        PassportLifecycleState::Revoked => lifecycle
            .revoked_reason
            .as_deref()
            .map(|reason| format!("passport lifecycle state is revoked: {reason}"))
            .unwrap_or_else(|| "passport lifecycle state is revoked".to_string()),
        PassportLifecycleState::NotFound => "passport lifecycle record was not found".to_string(),
    }
}

fn resolve_passport_lifecycle(
    passport: &AgentPassport,
    at: u64,
    passport_statuses_file: Option<&Path>,
    control_url: Option<&str>,
    control_token: Option<&str>,
    required: bool,
) -> Result<Option<PassportLifecycleResolution>, CliError> {
    if let Some(url) = control_url {
        let client = crate::trust_control::build_client(url, control_token.unwrap_or_default())?;
        let passport_id = passport_artifact_id(passport).map_err(|error| {
            CliError::Other(format!("failed to derive passport artifact id: {error}"))
        })?;
        let mut lifecycle = match client.public_resolve_passport_status(&passport_id) {
            Ok(lifecycle) => lifecycle,
            Err(error)
                if !required
                    && error
                        .to_string()
                        .contains("passport lifecycle administration requires") =>
            {
                return Ok(None)
            }
            Err(error) => return Err(error),
        };
        lifecycle
            .validate()
            .map_err(|error| CliError::Other(error.to_string()))?;
        lifecycle
            .source
            .get_or_insert_with(|| format!("remote:{url}"));
        return Ok(Some(lifecycle));
    }

    let Some(path) = passport_statuses_file else {
        return Ok(None);
    };
    let registry = load_passport_status_registry_for_admin(path)?;
    let mut lifecycle = registry.resolve_for_passport(passport, at)?;
    lifecycle.source = Some(format!("registry:{}", path.display()));
    Ok(Some(lifecycle))
}

fn portable_passport_status_reference(
    passport: &AgentPassport,
    at: u64,
    passport_statuses_file: Option<&Path>,
) -> Result<Option<chio_credentials::Oid4vciChioPassportStatusReference>, CliError> {
    let Some(path) = passport_statuses_file else {
        return Ok(None);
    };
    let registry = load_passport_status_registry_for_admin(path)?;
    registry
        .portable_status_reference_for_passport(passport, at)
        .map(Some)
}

fn require_passport_lifecycle_source(
    require_active_lifecycle: bool,
    passport_statuses_file: Option<&Path>,
    control_url: Option<&str>,
) -> Result<(), CliError> {
    if require_active_lifecycle && passport_statuses_file.is_none() && control_url.is_none() {
        return Err(CliError::Other(
            "passport verifier policy requires active lifecycle enforcement, but no --passport-statuses-file or --control-url was provided"
                .to_string(),
        ));
    }
    Ok(())
}

fn apply_passport_lifecycle_to_evaluation(
    evaluation: &mut chio_credentials::PassportPolicyEvaluation,
    lifecycle: Option<PassportLifecycleResolution>,
) {
    evaluation.verification.passport_lifecycle = lifecycle.clone();
    if !evaluation.policy.require_active_lifecycle {
        return;
    }
    let Some(lifecycle) = lifecycle else {
        return;
    };
    if lifecycle.state == PassportLifecycleState::Active {
        return;
    }

    let reason = passport_lifecycle_reason(&lifecycle);
    evaluation.accepted = false;
    evaluation.matched_credential_indexes.clear();
    evaluation.matched_issuers.clear();
    if !evaluation
        .passport_reasons
        .iter()
        .any(|existing| existing == &reason)
    {
        evaluation.passport_reasons.push(reason);
    }
}

fn apply_passport_lifecycle_to_presentation(
    verification: &mut chio_credentials::PassportPresentationVerification,
    lifecycle: Option<PassportLifecycleResolution>,
) {
    verification.passport_lifecycle = lifecycle.clone();
    let Some(policy_evaluation) = verification.policy_evaluation.as_mut() else {
        return;
    };
    if !policy_evaluation.policy.require_active_lifecycle {
        return;
    }
    let Some(lifecycle) = lifecycle else {
        return;
    };
    if lifecycle.state == PassportLifecycleState::Active {
        return;
    }

    let reason = passport_lifecycle_reason(&lifecycle);
    policy_evaluation.accepted = false;
    policy_evaluation.matched_credential_indexes.clear();
    policy_evaluation.matched_issuers.clear();
    if !policy_evaluation
        .passport_reasons
        .iter()
        .any(|existing| existing == &reason)
    {
        policy_evaluation.passport_reasons.push(reason);
    }
    verification.accepted = false;
}

fn require_receipt_db(receipt_db_path: Option<&Path>) -> Result<&Path, CliError> {
    receipt_db_path.ok_or_else(|| {
        CliError::Other(
            "passport creation requires --receipt-db so the local attestation corpus can be assembled"
                .to_string(),
        )
    })
}

fn build_attestation_evidence(
    store: &SqliteReceiptStore,
    subject_key: &str,
    since: Option<u64>,
    until: Option<u64>,
    receipt_log_urls: &[String],
    require_checkpoints: bool,
) -> Result<ChioCredentialEvidence, CliError> {
    let bundle = store.build_evidence_export_bundle(&EvidenceExportQuery {
        capability_id: None,
        agent_subject: Some(subject_key.to_string()),
        since,
        until,
        tenant: None,
    })?;

    if bundle.tool_receipts.is_empty() {
        return Err(CliError::Other(format!(
            "no receipts found for subject {subject_key} in the selected window"
        )));
    }
    if require_checkpoints && !bundle.uncheckpointed_receipts.is_empty() {
        return Err(CliError::Other(format!(
            "passport creation requires checkpoint coverage, but {} selected receipt(s) are uncheckpointed",
            bundle.uncheckpointed_receipts.len()
        )));
    }

    Ok(ChioCredentialEvidence {
        query: AttestationWindow {
            since,
            until: until.unwrap_or_else(unix_now),
        },
        receipt_count: bundle.tool_receipts.len(),
        receipt_ids: bundle
            .tool_receipts
            .into_iter()
            .map(|record| record.receipt.id)
            .collect(),
        checkpoint_roots: bundle
            .checkpoints
            .into_iter()
            .map(|checkpoint| checkpoint.body.merkle_root.to_string())
            .collect(),
        receipt_log_urls: receipt_log_urls.to_vec(),
        lineage_records: bundle.capability_lineage.len(),
        uncheckpointed_receipts: bundle.uncheckpointed_receipts.len(),
        runtime_attestation: None,
    })
}

/// Build a deterministic snapshot of the inputs the kernel's
/// `compliance_score` function expects. When a caller provides a
/// `compliance_score_override`, we skip the full factor math and echo
/// the override verbatim so CI and ops harnesses can pin a specific
/// tier. When no override is supplied we materialize a clean-agent
/// report and feed it through `compliance_score` so the emitted score
/// is the kernel's own output, not a shortcut.
fn compute_generate_trust_tier(
    agent: &str,
    compliance_score_override: Option<u32>,
    behavioral_anomaly: bool,
    now: u64,
) -> (u32, bool, TrustTier) {
    let effective_score = if let Some(score) = compliance_score_override {
        score.min(chio_kernel::COMPLIANCE_SCORE_MAX)
    } else {
        // Clean-agent baseline: no denies, no revocations, no velocity
        // anomalies, no stale attestation. The kernel's scoring math
        // returns 1000 for this shape, which matches the default
        // "Premier" tier for a freshly provisioned agent.
        let report = ComplianceReport {
            matching_receipts: 0,
            evidence_ready_receipts: 0,
            uncheckpointed_receipts: 0,
            checkpoint_coverage_rate: None,
            lineage_covered_receipts: 0,
            lineage_gap_receipts: 0,
            lineage_coverage_rate: None,
            pending_settlement_receipts: 0,
            failed_settlement_receipts: 0,
            direct_evidence_export_supported: true,
            child_receipt_scope: EvidenceChildReceiptScope::FullQueryWindow,
            proofs_complete: true,
            export_query: EvidenceExportQuery::default(),
            export_scope_note: None,
        };
        let inputs = ComplianceScoreInputs::new(0, 0, 0, 0, 0, 0, Some(0));
        let config = ComplianceScoreConfig::default();
        compliance_score(&report, &inputs, &config, agent, now).score
    };

    // Drive the kernel's behavioral anomaly scorer so the boolean tier
    // input is not fabricated locally. An all-zero baseline with
    // sample_count < 2 yields `z_score == None`, so we seed a tiny
    // two-sample baseline and feed either the mean (anomaly=false) or
    // a far-tail sample (anomaly=true) to drive the desired output.
    let baseline = EmaBaselineState {
        sample_count: 2,
        ema_mean: 1.0,
        ema_variance: 1.0,
        last_update: now,
    };
    let sample = if behavioral_anomaly { 100.0 } else { 1.0 };
    let anomaly = behavioral_anomaly_score(agent, &baseline, sample, 3.0, now).anomaly;

    let tier = synthesize_trust_tier(effective_score, anomaly);
    (effective_score, anomaly, tier)
}

pub(crate) fn cmd_passport_generate(
    agent: &str,
    output: Option<&Path>,
    compliance_score_override: Option<u32>,
    behavioral_anomaly: bool,
    validity_days: u32,
    json_output: bool,
) -> Result<(), CliError> {
    if agent.trim().is_empty() {
        return Err(CliError::Other(
            "`chio passport generate` requires a non-empty --agent".to_string(),
        ));
    }
    let now = unix_now();
    let valid_until = now.saturating_add(validity_seconds(validity_days));

    let (score, anomaly, tier) =
        compute_generate_trust_tier(agent, compliance_score_override, behavioral_anomaly, now);

    // Keep the emitted document structurally compatible with the
    // extended `AgentPassport` wire form (camelCase, `trustTier` as an
    // optional string field) while remaining lightweight: the real
    // passport builder needs receipts and a signing authority, which
    // the `generate` command deliberately does not require.
    let passport_json = serde_json::json!({
        "schema": "chio.agent-passport.v1",
        "subject": agent,
        "credentials": [],
        "merkleRoots": [],
        "issuedAt": now,
        "validUntil": valid_until,
        "trustTier": tier,
    });

    if let Some(path) = output {
        ensure_parent_dir(path)?;
        fs::write(path, serde_json::to_vec_pretty(&passport_json)?)?;
    }

    if json_output {
        println!(
            "{}",
            serde_json::to_string_pretty(&serde_json::json!({
                "output": output.map(|path| path.display().to_string()),
                "subject": agent,
                "complianceScore": score,
                "behavioralAnomaly": anomaly,
                "trustTier": tier,
                "validUntil": valid_until,
                "passport": passport_json,
            }))?
        );
    } else {
        println!("passport generated");
        println!("subject:          {agent}");
        println!("compliance_score: {score}");
        println!("behavioral_anomaly: {anomaly}");
        println!("trust_tier:       {}", tier.label());
        println!("valid_until:      {valid_until}");
        if let Some(path) = output {
            println!("output:           {}", path.display());
        } else {
            println!("{}", serde_json::to_string_pretty(&passport_json)?);
        }
    }
    Ok(())
}

pub(crate) fn cmd_passport_create(
    subject_public_key: &str,
    output: &Path,
    signing_seed_file: &Path,
    validity_days: u32,
    since: Option<u64>,
    until: Option<u64>,
    receipt_log_urls: &[String],
    require_checkpoints: bool,
    enterprise_identity_path: Option<&Path>,
    receipt_db_path: Option<&Path>,
    budget_db_path: Option<&Path>,
    json_output: bool,
) -> Result<(), CliError> {
    let subject_public_key = PublicKey::from_hex(subject_public_key)?;
    let subject_key = subject_public_key.to_hex();
    let now = unix_now();
    let attestation_until = until.unwrap_or(now);
    let corpus = build_local_reputation_corpus(
        &subject_key,
        receipt_db_path,
        budget_db_path,
        since,
        Some(attestation_until),
    )?;
    if corpus.receipts.is_empty() {
        return Err(CliError::Other(format!(
            "no receipts found for subject {subject_key} in the selected window"
        )));
    }

    let scorecard = compute_local_scorecard(
        &subject_key,
        attestation_until,
        &corpus,
        &ReputationConfig::default(),
    );
    let store = SqliteReceiptStore::open(require_receipt_db(receipt_db_path)?)?;
    let evidence = build_attestation_evidence(
        &store,
        &subject_key,
        since,
        Some(attestation_until),
        receipt_log_urls,
        require_checkpoints,
    )?;
    let signing_key = load_or_create_authority_keypair(signing_seed_file)?;
    let credential = issue_reputation_credential_with_enterprise_identity(
        &signing_key,
        scorecard,
        evidence,
        load_enterprise_identity_provenance(enterprise_identity_path)?,
        now,
        now + validity_seconds(validity_days),
    )?;
    let subject_did = DidChio::from_public_key(subject_public_key)
        .map_err(|error| CliError::Other(error.to_string()))?;
    let passport = build_agent_passport(&subject_did.to_string(), vec![credential])?;
    let (enterprise_provenance_count, enterprise_provider_ids) =
        summarize_enterprise_provenance(&passport.enterprise_identity_provenance);

    ensure_parent_dir(output)?;
    fs::write(output, serde_json::to_vec_pretty(&passport)?)?;

    if json_output {
        println!(
            "{}",
            serde_json::to_string_pretty(&serde_json::json!({
                "output": output.display().to_string(),
                "subject": passport.subject,
                "credentialCount": passport.credentials.len(),
                "merkleRootCount": passport.merkle_roots.len(),
                "enterpriseIdentityProvenanceCount": enterprise_provenance_count,
                "enterpriseProviderIds": enterprise_provider_ids,
                "validUntil": passport.valid_until,
            }))?
        );
    } else {
        println!("wrote passport to {}", output.display());
        println!("subject:          {}", passport.subject);
        println!("credential_count: {}", passport.credentials.len());
        println!("merkle_roots:     {}", passport.merkle_roots.len());
        println!("enterprise_provenance: {}", enterprise_provenance_count);
        if !enterprise_provider_ids.is_empty() {
            println!(
                "enterprise_providers: {}",
                enterprise_provider_ids.join(", ")
            );
        }
        println!("valid_until:      {}", passport.valid_until);
    }
    Ok(())
}

pub(crate) fn cmd_passport_verify(
    input: &Path,
    at: Option<u64>,
    passport_statuses_file: Option<&Path>,
    json_output: bool,
    control_url: Option<&str>,
    control_token: Option<&str>,
) -> Result<(), CliError> {
    let passport: AgentPassport = serde_json::from_slice(&fs::read(input)?)?;
    let now = at.unwrap_or_else(unix_now);
    let mut verification = verify_agent_passport(&passport, now)
        .map_err(|error| CliError::Other(error.to_string()))?;
    verification.passport_lifecycle = resolve_passport_lifecycle(
        &passport,
        now,
        passport_statuses_file,
        control_url,
        control_token,
        false,
    )?;
    let (enterprise_provenance_count, enterprise_provider_ids) =
        summarize_enterprise_provenance(&verification.enterprise_identity_provenance);

    if json_output {
        println!("{}", serde_json::to_string_pretty(&verification)?);
    } else {
        println!("passport verified");
        println!("subject:          {}", verification.subject);
        println!("passport_id:      {}", verification.passport_id);
        if let Some(issuer) = verification.issuer.as_deref() {
            println!("issuer:           {issuer}");
        } else {
            println!("issuers:          {}", verification.issuers.join(", "));
        }
        println!("issuer_count:     {}", verification.issuer_count);
        println!("credential_count: {}", verification.credential_count);
        println!("merkle_roots:     {}", verification.merkle_root_count);
        println!("enterprise_provenance: {}", enterprise_provenance_count);
        if !enterprise_provider_ids.is_empty() {
            println!(
                "enterprise_providers: {}",
                enterprise_provider_ids.join(", ")
            );
        }
        if let Some(lifecycle) = verification.passport_lifecycle.as_ref() {
            println!("lifecycle_state:  {}", lifecycle.state.label());
            if let Some(source) = lifecycle.source.as_deref() {
                println!("lifecycle_source: {source}");
            }
            if let Some(superseded_by) = lifecycle.superseded_by.as_deref() {
                println!("superseded_by:    {superseded_by}");
            }
            if let Some(revoked_at) = lifecycle.revoked_at {
                println!("revoked_at:       {revoked_at}");
            }
            if let Some(reason) = lifecycle.revoked_reason.as_deref() {
                println!("revoked_reason:   {reason}");
            }
        }
        println!("valid_until:      {}", verification.valid_until);
    }
    Ok(())
}

pub(crate) fn cmd_passport_evaluate(
    input: &Path,
    policy_path: &Path,
    at: Option<u64>,
    passport_statuses_file: Option<&Path>,
    json_output: bool,
    control_url: Option<&str>,
    control_token: Option<&str>,
) -> Result<(), CliError> {
    let passport: AgentPassport = serde_json::from_slice(&fs::read(input)?)?;
    let policy = load_passport_verifier_policy(policy_path)?;
    let now = at.unwrap_or_else(unix_now);
    require_passport_lifecycle_source(
        policy.require_active_lifecycle,
        passport_statuses_file,
        control_url,
    )?;
    let mut evaluation = evaluate_agent_passport(&passport, now, &policy)
        .map_err(|error| CliError::Other(error.to_string()))?;
    let lifecycle = resolve_passport_lifecycle(
        &passport,
        now,
        passport_statuses_file,
        control_url,
        control_token,
        policy.require_active_lifecycle,
    )?;
    apply_passport_lifecycle_to_evaluation(&mut evaluation, lifecycle);

    if json_output {
        println!("{}", serde_json::to_string_pretty(&evaluation)?);
    } else {
        let (enterprise_provenance_count, enterprise_provider_ids) =
            summarize_enterprise_provenance(
                &evaluation.verification.enterprise_identity_provenance,
            );
        println!("passport evaluated");
        println!("subject:             {}", evaluation.verification.subject);
        println!(
            "passport_id:         {}",
            evaluation.verification.passport_id
        );
        if let Some(issuer) = evaluation.verification.issuer.as_deref() {
            println!("issuer:              {issuer}");
        } else {
            println!(
                "issuers:             {}",
                evaluation.verification.issuers.join(", ")
            );
        }
        println!(
            "issuer_count:        {}",
            evaluation.verification.issuer_count
        );
        println!("accepted:            {}", evaluation.accepted);
        println!(
            "matched_credentials: {}",
            evaluation.matched_credential_indexes.len()
        );
        if !evaluation.matched_issuers.is_empty() {
            println!(
                "matched_issuers:     {}",
                evaluation.matched_issuers.join(", ")
            );
        }
        println!(
            "credential_count:    {}",
            evaluation.verification.credential_count
        );
        println!("enterprise_provenance: {}", enterprise_provenance_count);
        if !enterprise_provider_ids.is_empty() {
            println!(
                "enterprise_providers: {}",
                enterprise_provider_ids.join(", ")
            );
        }
        if let Some(lifecycle) = evaluation.verification.passport_lifecycle.as_ref() {
            println!("lifecycle_state:     {}", lifecycle.state.label());
            if let Some(source) = lifecycle.source.as_deref() {
                println!("lifecycle_source:    {source}");
            }
        }
        println!(
            "valid_until:         {}",
            evaluation.verification.valid_until
        );
        if !evaluation.passport_reasons.is_empty() {
            println!("passport_reasons:");
            for reason in &evaluation.passport_reasons {
                println!("  - {reason}");
            }
        }
        if !evaluation.accepted {
            println!("rejections:");
            for result in &evaluation.credential_results {
                if result.accepted {
                    continue;
                }
                println!("  credential {} ({}):", result.index, result.issuer);
                for reason in &result.reasons {
                    println!("    - {}", reason);
                }
            }
        }
    }
    Ok(())
}

pub(crate) fn cmd_passport_present(
    input: &Path,
    output: &Path,
    issuers: &[String],
    max_credentials: Option<usize>,
    json_output: bool,
) -> Result<(), CliError> {
    let passport: AgentPassport = serde_json::from_slice(&fs::read(input)?)?;
    verify_agent_passport(&passport, unix_now())?;

    let presented = present_agent_passport(
        &passport,
        &PassportPresentationOptions {
            issuer_allowlist: issuers.iter().cloned().collect::<BTreeSet<_>>(),
            max_credentials,
        },
    )?;

    ensure_parent_dir(output)?;
    fs::write(output, serde_json::to_vec_pretty(&presented)?)?;

    if json_output {
        println!(
            "{}",
            serde_json::to_string_pretty(&serde_json::json!({
                "output": output.display().to_string(),
                "subject": presented.subject,
                "credentialCount": presented.credentials.len(),
                "merkleRootCount": presented.merkle_roots.len(),
            }))?
        );
    } else {
        println!("wrote presented passport to {}", output.display());
        println!("subject:          {}", presented.subject);
        println!("credential_count: {}", presented.credentials.len());
        println!("merkle_roots:     {}", presented.merkle_roots.len());
    }
    Ok(())
}

pub(crate) fn cmd_passport_issuance_metadata(
    issuer_url: Option<&str>,
    signing_seed_file: Option<&Path>,
    passport_status_url: Option<&str>,
    passport_status_cache_ttl_secs: Option<u64>,
    json_output: bool,
    control_url: Option<&str>,
    control_token: Option<&str>,
) -> Result<(), CliError> {
    let metadata = if let Some(url) = control_url {
        crate::trust_control::build_client(url, control_token.unwrap_or_default())?
            .passport_issuer_metadata()?
    } else {
        local_passport_issuer_metadata(
            require_credential_issuer_url(issuer_url)?,
            signing_seed_file,
            passport_status_url,
            passport_status_cache_ttl_secs,
        )?
    };

    if json_output {
        println!("{}", serde_json::to_string_pretty(&metadata)?);
    } else {
        println!("credential_issuer: {}", metadata.credential_issuer);
        println!("token_endpoint:    {}", metadata.token_endpoint);
        println!("credential_endpoint: {}", metadata.credential_endpoint);
        println!(
            "configurations:    {}",
            metadata
                .credential_configurations_supported
                .keys()
                .cloned()
                .collect::<Vec<_>>()
                .join(", ")
        );
    }
    Ok(())
}

pub(crate) fn cmd_passport_issuance_offer_create(
    input: &Path,
    output: Option<&Path>,
    issuer_url: Option<&str>,
    passport_issuance_offers_file: Option<&Path>,
    passport_statuses_file: Option<&Path>,
    signing_seed_file: Option<&Path>,
    credential_configuration_id: Option<&str>,
    ttl_secs: u64,
    json_output: bool,
    control_url: Option<&str>,
    control_token: Option<&str>,
) -> Result<(), CliError> {
    let passport: AgentPassport = serde_json::from_slice(&fs::read(input)?)?;
    let record = if let Some(url) = control_url {
        let token = crate::require_control_token(control_token)?;
        crate::trust_control::build_client(url, token)?.create_passport_issuance_offer(
            &crate::trust_control::CreatePassportIssuanceOfferRequest {
                passport,
                ttl_seconds: ttl_secs,
                credential_configuration_id: credential_configuration_id.map(str::to_string),
            },
        )?
    } else {
        let path = require_passport_issuance_registry_path(passport_issuance_offers_file)?;
        let mut registry = load_passport_issuance_registry_for_admin(path)?;
        let metadata = local_passport_issuer_metadata(
            require_credential_issuer_url(issuer_url)?,
            signing_seed_file,
            None,
            None,
        )?;
        if passport_statuses_file.is_some() {
            portable_passport_status_reference(&passport, unix_now(), passport_statuses_file)?;
        }
        let record = registry.issue_offer(
            &metadata,
            passport,
            credential_configuration_id,
            ttl_secs,
            unix_now(),
        )?;
        registry.save(path)?;
        record
    };

    if let Some(output) = output {
        ensure_parent_dir(output)?;
        fs::write(output, serde_json::to_vec_pretty(&record.offer)?)?;
    }

    if json_output {
        println!("{}", serde_json::to_string_pretty(&record)?);
    } else {
        println!("passport issuance offer created");
        println!("offer_id:          {}", record.offer_id);
        println!("credential_issuer: {}", record.offer.credential_issuer);
        println!(
            "configuration_id:  {}",
            record.offer.primary_configuration_id()?
        );
        println!("state:             {:?}", record.state);
        println!("expires_at:        {}", record.expires_at);
        if let Some(output) = output {
            println!("offer_output:      {}", output.display());
        }
    }
    Ok(())
}

pub(crate) fn cmd_passport_issuance_token_redeem(
    offer_path: &Path,
    output: Option<&Path>,
    passport_issuance_offers_file: Option<&Path>,
    json_output: bool,
    control_url: Option<&str>,
    control_token: Option<&str>,
) -> Result<(), CliError> {
    let offer = load_oid4vci_offer(offer_path)?;
    let request = Oid4vciTokenRequest {
        grant_type: chio_credentials::OID4VCI_PRE_AUTHORIZED_GRANT_TYPE.to_string(),
        pre_authorized_code: offer.pre_authorized_code()?.to_string(),
    };
    let token = if let Some(url) = control_url {
        crate::trust_control::build_client(url, control_token.unwrap_or_default())?
            .redeem_passport_issuance_token(&request)?
    } else {
        let metadata = default_oid4vci_passport_issuer_metadata_with_status_distribution(
            &offer.credential_issuer,
            PassportStatusDistribution::default(),
        )?;
        let path = require_passport_issuance_registry_path(passport_issuance_offers_file)?;
        let mut registry = load_passport_issuance_registry_for_admin(path)?;
        let token = registry.redeem_pre_authorized_code(&metadata, &request, unix_now(), 300)?;
        registry.save(path)?;
        token
    };

    if let Some(output) = output {
        ensure_parent_dir(output)?;
        fs::write(output, serde_json::to_vec_pretty(&token)?)?;
    }

    if json_output {
        println!("{}", serde_json::to_string_pretty(&token)?);
    } else {
        println!("passport issuance token redeemed");
        println!("token_type: {}", token.token_type);
        println!("expires_in: {}", token.expires_in);
        if let Some(output) = output {
            println!("token_output: {}", output.display());
        }
    }
    Ok(())
}

pub(crate) fn cmd_passport_issuance_credential_redeem(
    offer_path: &Path,
    token_path: &Path,
    output: Option<&Path>,
    passport_issuance_offers_file: Option<&Path>,
    passport_statuses_file: Option<&Path>,
    signing_seed_file: Option<&Path>,
    credential_configuration_id: Option<&str>,
    format: Option<&str>,
    json_output: bool,
    control_url: Option<&str>,
    control_token: Option<&str>,
) -> Result<(), CliError> {
    let offer = load_oid4vci_offer(offer_path)?;
    let token = load_oid4vci_token(token_path)?;
    let metadata = if let Some(url) = control_url {
        crate::trust_control::build_client(url, control_token.unwrap_or_default())?
            .passport_issuer_metadata()?
    } else {
        local_passport_issuer_metadata(&offer.credential_issuer, signing_seed_file, None, None)?
    };
    let resolved_credential_configuration_id = credential_configuration_id
        .map(str::to_string)
        .or_else(|| offer.primary_configuration_id().ok().map(str::to_string));
    let resolved_format = format.map(str::to_string).or_else(|| {
        resolved_credential_configuration_id
            .as_deref()
            .and_then(|configuration_id| {
                metadata
                    .credential_configuration(configuration_id)
                    .ok()
                    .map(|configuration| configuration.format.clone())
            })
    });
    let request = Oid4vciCredentialRequest {
        credential_configuration_id: resolved_credential_configuration_id,
        format: resolved_format,
        subject: offer
            .chio_offer_context
            .as_ref()
            .map(|context| context.subject.clone())
            .unwrap_or_default(),
    };
    let response = if let Some(url) = control_url {
        crate::trust_control::build_client(url, control_token.unwrap_or_default())?
            .redeem_passport_issuance_credential(&token.access_token, &request)?
    } else {
        let path = require_passport_issuance_registry_path(passport_issuance_offers_file)?;
        let mut registry = load_passport_issuance_registry_for_admin(path)?;
        let portable_signing_keypair = signing_seed_file
            .map(load_or_create_authority_keypair)
            .transpose()?;
        let portable_status_registry = passport_statuses_file
            .map(load_passport_status_registry_for_admin)
            .transpose()?;
        let response = registry.redeem_credential(
            &metadata,
            &token.access_token,
            &request,
            unix_now(),
            portable_signing_keypair.as_ref(),
            portable_status_registry.as_ref(),
        )?;
        registry.save(path)?;
        response
    };

    if let Some(output) = output {
        ensure_parent_dir(output)?;
        fs::write(
            output,
            response
                .credential
                .write_output_bytes()
                .map_err(|error| CliError::Other(error.to_string()))?,
        )?;
    }

    if json_output {
        println!("{}", serde_json::to_string_pretty(&response)?);
    } else {
        println!("passport credential redeemed");
        if let Some(subject) = response.subject_hint() {
            println!("subject: {}", subject);
        }
        println!("format:  {}", response.format);
        if let Some(passport_id) = response.passport_id_hint() {
            println!("passport_id: {}", passport_id);
        }
        if let Some(output) = output {
            println!("credential_output: {}", output.display());
        }
    }
    Ok(())
}

pub(crate) fn cmd_passport_policy_create(
    args: PassportPolicyCreateArgs<'_>,
) -> Result<(), CliError> {
    let PassportPolicyCreateArgs {
        output,
        policy_id,
        verifier,
        signing_seed_file,
        policy_path,
        expires_at,
        verifier_policies_file,
        json_output,
        control_url,
        control_token,
    } = args;
    let now = unix_now();
    let keypair = load_or_create_authority_keypair(signing_seed_file)?;
    let policy = load_passport_verifier_policy(policy_path)?;
    let document = create_signed_passport_verifier_policy(
        &keypair, policy_id, verifier, now, expires_at, policy,
    )?;

    ensure_parent_dir(output)?;
    fs::write(output, serde_json::to_vec_pretty(&document)?)?;

    let registration = if let Some(url) = control_url {
        let token = crate::require_control_token(control_token)?;
        crate::trust_control::build_client(url, token)?
            .upsert_verifier_policy(policy_id, &document)?;
        Some(url.to_string())
    } else if let Some(path) = verifier_policies_file {
        let mut registry = load_verifier_policy_registry_for_admin(path)?;
        registry.upsert(document.clone())?;
        registry.save(path)?;
        Some(path.display().to_string())
    } else {
        None
    };

    if json_output {
        println!("{}", serde_json::to_string_pretty(&document)?);
    } else {
        println!("verifier policy created");
        println!("output:            {}", output.display());
        println!("policy_id:         {}", document.body.policy_id);
        println!("verifier:          {}", document.body.verifier);
        println!(
            "signer_public_key: {}",
            document.body.signer_public_key.to_hex()
        );
        println!("created_at:        {}", document.body.created_at);
        println!("expires_at:        {}", document.body.expires_at);
        if let Some(registration) = registration.as_deref() {
            println!("registered_in:     {registration}");
        }
    }
    Ok(())
}

pub(crate) fn cmd_passport_policy_verify(
    input: &Path,
    at: Option<u64>,
    json_output: bool,
) -> Result<(), CliError> {
    let document = load_signed_passport_verifier_policy(input)?;
    verify_signed_passport_verifier_policy(&document)
        .map_err(|error| CliError::Other(error.to_string()))?;
    ensure_signed_passport_verifier_policy_active(&document, at.unwrap_or_else(unix_now))
        .map_err(|error| CliError::Other(error.to_string()))?;

    if json_output {
        println!("{}", serde_json::to_string_pretty(&document)?);
    } else {
        println!("verifier policy verified");
        println!("policy_id:         {}", document.body.policy_id);
        println!("verifier:          {}", document.body.verifier);
        println!(
            "signer_public_key: {}",
            document.body.signer_public_key.to_hex()
        );
        println!("created_at:        {}", document.body.created_at);
        println!("expires_at:        {}", document.body.expires_at);
    }
    Ok(())
}

pub(crate) fn cmd_passport_policy_list(
    json_output: bool,
    verifier_policies_file: Option<&Path>,
    control_url: Option<&str>,
    control_token: Option<&str>,
) -> Result<(), CliError> {
    let response = if let Some(url) = control_url {
        let token = crate::require_control_token(control_token)?;
        crate::trust_control::build_client(url, token)?.list_verifier_policies()?
    } else {
        let path = require_verifier_policy_registry_path(verifier_policies_file)?;
        let registry = load_verifier_policy_registry_for_admin(path)?;
        VerifierPolicyListResponse {
            configured: true,
            count: registry.policies.len(),
            policies: registry.policies.into_values().collect(),
        }
    };

    if json_output {
        println!("{}", serde_json::to_string_pretty(&response)?);
    } else {
        println!("configured: {}", response.configured);
        println!("count:      {}", response.count);
        for document in response.policies {
            println!(
                "- {} ({}) expires_at={}",
                document.body.policy_id, document.body.verifier, document.body.expires_at
            );
        }
    }
    Ok(())
}

pub(crate) fn cmd_passport_policy_get(
    policy_id: &str,
    json_output: bool,
    verifier_policies_file: Option<&Path>,
    control_url: Option<&str>,
    control_token: Option<&str>,
) -> Result<(), CliError> {
    let document = if let Some(url) = control_url {
        let token = crate::require_control_token(control_token)?;
        crate::trust_control::build_client(url, token)?.get_verifier_policy(policy_id)?
    } else {
        let path = require_verifier_policy_registry_path(verifier_policies_file)?;
        let registry = load_verifier_policy_registry_for_admin(path)?;
        registry.get(policy_id).cloned().ok_or_else(|| {
            CliError::Other(format!("verifier policy `{policy_id}` was not found"))
        })?
    };

    if json_output {
        println!("{}", serde_json::to_string_pretty(&document)?);
    } else {
        println!("policy_id:         {}", document.body.policy_id);
        println!("verifier:          {}", document.body.verifier);
        println!(
            "signer_public_key: {}",
            document.body.signer_public_key.to_hex()
        );
        println!("created_at:        {}", document.body.created_at);
        println!("expires_at:        {}", document.body.expires_at);
    }
    Ok(())
}

pub(crate) fn cmd_passport_policy_upsert(
    input: &Path,
    json_output: bool,
    verifier_policies_file: Option<&Path>,
    control_url: Option<&str>,
    control_token: Option<&str>,
) -> Result<(), CliError> {
    let document = load_signed_passport_verifier_policy(input)?;
    verify_signed_passport_verifier_policy(&document)
        .map_err(|error| CliError::Other(error.to_string()))?;
    let saved = if let Some(url) = control_url {
        let token = crate::require_control_token(control_token)?;
        crate::trust_control::build_client(url, token)?
            .upsert_verifier_policy(&document.body.policy_id, &document)?
    } else {
        let path = require_verifier_policy_registry_path(verifier_policies_file)?;
        let mut registry = load_verifier_policy_registry_for_admin(path)?;
        registry.upsert(document.clone())?;
        registry.save(path)?;
        document
    };

    if json_output {
        println!("{}", serde_json::to_string_pretty(&saved)?);
    } else {
        println!("verifier policy upserted");
        println!("policy_id:  {}", saved.body.policy_id);
        println!("verifier:   {}", saved.body.verifier);
        println!("expires_at: {}", saved.body.expires_at);
    }
    Ok(())
}

pub(crate) fn cmd_passport_policy_delete(
    policy_id: &str,
    json_output: bool,
    verifier_policies_file: Option<&Path>,
    control_url: Option<&str>,
    control_token: Option<&str>,
) -> Result<(), CliError> {
    let (deleted, configured) = if let Some(url) = control_url {
        let token = crate::require_control_token(control_token)?;
        let response =
            crate::trust_control::build_client(url, token)?.delete_verifier_policy(policy_id)?;
        (response.deleted, true)
    } else {
        let path = require_verifier_policy_registry_path(verifier_policies_file)?;
        let mut registry = load_verifier_policy_registry_for_admin(path)?;
        let deleted = registry.remove(policy_id);
        registry.save(path)?;
        (deleted, true)
    };

    if json_output {
        println!(
            "{}",
            serde_json::to_string_pretty(&serde_json::json!({
                "configured": configured,
                "policyId": policy_id,
                "deleted": deleted,
            }))?
        );
    } else {
        println!("configured: {configured}");
        println!("policy_id:  {policy_id}");
        println!("deleted:    {deleted}");
    }
    Ok(())
}

pub(crate) fn cmd_passport_challenge_create(
    args: PassportChallengeCreateArgs<'_>,
) -> Result<(), CliError> {
    let PassportChallengeCreateArgs {
        output,
        verifier,
        ttl_secs,
        issuers,
        max_credentials,
        policy_path,
        policy_id,
        verifier_policies_file,
        verifier_challenge_db,
        json_output,
        control_url,
        control_token,
    } = args;
    let now = unix_now();
    if policy_path.is_some() && policy_id.is_some() {
        return Err(CliError::Other(
            "challenge creation accepts either --policy or --policy-id, not both".to_string(),
        ));
    }
    let challenge_response = if let Some(url) = control_url {
        let token = crate::require_control_token(control_token)?;
        crate::trust_control::build_client(url, token)?.create_passport_challenge(
            &CreatePassportChallengeRequest {
                verifier: verifier.to_string(),
                ttl_seconds: ttl_secs,
                issuers: issuers.to_vec(),
                max_credentials,
                policy_id: policy_id.map(str::to_string),
                policy: policy_path.map(load_passport_verifier_policy).transpose()?,
            },
        )?
    } else {
        let (policy_ref, policy, policy_verifier) = if let Some(policy_id) = policy_id {
            let path = require_verifier_policy_registry_path(verifier_policies_file)?;
            let registry = load_verifier_policy_registry_for_admin(path)?;
            let document = registry.active_policy(policy_id, now)?;
            (
                Some(PassportVerifierPolicyReference {
                    policy_id: document.body.policy_id.clone(),
                }),
                None,
                document.body.verifier.clone(),
            )
        } else {
            (
                None,
                policy_path.map(load_passport_verifier_policy).transpose()?,
                verifier.to_string(),
            )
        };
        if policy_ref.is_some() && policy_verifier != verifier {
            return Err(CliError::Other(
                "stored verifier policy verifier must match --verifier".to_string(),
            ));
        }
        let challenge = create_passport_presentation_challenge_with_reference(
            chio_credentials::PassportPresentationChallengeArgs {
                verifier: verifier.to_string(),
                challenge_id: Some(Keypair::generate().public_key().to_hex()),
                nonce: Keypair::generate().public_key().to_hex(),
                issued_at: now,
                expires_at: now.saturating_add(ttl_secs),
                options: PassportPresentationOptions {
                    issuer_allowlist: issuers.iter().cloned().collect::<BTreeSet<_>>(),
                    max_credentials,
                },
                policy_ref,
                policy,
            },
        )?;
        if let Some(path) = verifier_challenge_db {
            PassportVerifierChallengeStore::open(path)?.register(&challenge)?;
        }
        CreatePassportChallengeResponse {
            challenge,
            transport: None,
        }
    };
    let challenge = &challenge_response.challenge;

    ensure_parent_dir(output)?;
    fs::write(output, serde_json::to_vec_pretty(&challenge)?)?;

    if json_output {
        println!(
            "{}",
            serde_json::to_string_pretty(&serde_json::json!({
                "output": output.display().to_string(),
                "verifier": challenge.verifier,
                "challengeId": challenge.challenge_id,
                "nonce": challenge.nonce,
                "expiresAt": challenge.expires_at,
                "policyId": challenge.policy_ref.as_ref().map(|reference| reference.policy_id.clone()),
                "policyEmbedded": challenge.policy.is_some(),
                "transport": challenge_response.transport,
            }))?
        );
    } else {
        println!("wrote challenge to {}", output.display());
        println!("verifier:        {}", challenge.verifier);
        if let Some(challenge_id) = challenge.challenge_id.as_deref() {
            println!("challenge_id:    {challenge_id}");
        }
        println!("nonce:           {}", challenge.nonce);
        println!("expires_at:      {}", challenge.expires_at);
        if let Some(policy_id) = challenge
            .policy_ref
            .as_ref()
            .map(|reference| reference.policy_id.as_str())
        {
            println!("policy_id:       {policy_id}");
        }
        println!("policy_embedded: {}", challenge.policy.is_some());
        if let Some(transport) = challenge_response.transport.as_ref() {
            println!("challenge_url:   {}", transport.challenge_url);
            println!("submit_url:      {}", transport.submit_url);
        }
    }
    Ok(())
}

pub(crate) fn cmd_passport_challenge_respond(
    input: &Path,
    challenge_path: Option<&Path>,
    challenge_url: Option<&str>,
    holder_seed_file: &Path,
    output: &Path,
    at: Option<u64>,
    json_output: bool,
) -> Result<(), CliError> {
    let passport: AgentPassport = serde_json::from_slice(&fs::read(input)?)?;
    let challenge: PassportPresentationChallenge =
        match (challenge_path, challenge_url) {
            (Some(path), None) => serde_json::from_slice(&fs::read(path)?)?,
            (None, Some(url)) => fetch_json_url(url)?,
            (Some(_), Some(_)) => {
                return Err(CliError::Other(
                    "challenge response accepts either --challenge or --challenge-url, not both"
                        .to_string(),
                ))
            }
            (None, None) => return Err(CliError::Other(
                "challenge response requires either --challenge <path> or --challenge-url <url>"
                    .to_string(),
            )),
        };
    let holder_keypair = load_existing_keypair(holder_seed_file)?;
    let response = respond_to_passport_presentation_challenge(
        &holder_keypair,
        &passport,
        &challenge,
        at.unwrap_or_else(unix_now),
    )?;

    ensure_parent_dir(output)?;
    fs::write(output, serde_json::to_vec_pretty(&response)?)?;

    if json_output {
        println!(
            "{}",
            serde_json::to_string_pretty(&serde_json::json!({
                "output": output.display().to_string(),
                "subject": response.passport.subject,
                "verifier": response.challenge.verifier,
                "nonce": response.challenge.nonce,
                "credentialCount": response.passport.credentials.len(),
            }))?
        );
    } else {
        println!("wrote challenge response to {}", output.display());
        println!("subject:          {}", response.passport.subject);
        println!("verifier:         {}", response.challenge.verifier);
        println!("nonce:            {}", response.challenge.nonce);
        println!("credential_count: {}", response.passport.credentials.len());
    }
    Ok(())
}

pub(crate) fn cmd_passport_challenge_submit(
    input: &Path,
    submit_url: &str,
    json_output: bool,
) -> Result<(), CliError> {
    let presentation: PassportPresentationResponse = serde_json::from_slice(&fs::read(input)?)?;
    let verification: chio_credentials::PassportPresentationVerification = post_json_url(
        submit_url,
        &VerifyPassportChallengeRequest {
            presentation,
            expected_challenge: None,
        },
    )?;

    if json_output {
        println!("{}", serde_json::to_string_pretty(&verification)?);
    } else {
        println!("presentation submitted");
        println!("accepted:         {}", verification.accepted);
        println!("subject:          {}", verification.subject);
        println!("verifier:         {}", verification.verifier);
        if let Some(challenge_id) = verification.challenge_id.as_deref() {
            println!("challenge_id:     {challenge_id}");
        }
        if let Some(replay_state) = verification.replay_state.as_deref() {
            println!("replay_state:     {replay_state}");
        }
    }
    Ok(())
}

pub(crate) fn cmd_passport_challenge_verify(
    input: &Path,
    challenge_path: Option<&Path>,
    verifier_policies_file: Option<&Path>,
    verifier_challenge_db: Option<&Path>,
    passport_statuses_file: Option<&Path>,
    at: Option<u64>,
    json_output: bool,
    control_url: Option<&str>,
    control_token: Option<&str>,
) -> Result<(), CliError> {
    let response: PassportPresentationResponse = serde_json::from_slice(&fs::read(input)?)?;
    let expected_challenge = challenge_path
        .map(|path| -> Result<PassportPresentationChallenge, CliError> {
            Ok(serde_json::from_slice(&fs::read(path)?)?)
        })
        .transpose()?;
    let now = at.unwrap_or_else(unix_now);
    let verification = if let Some(url) = control_url {
        let token = crate::require_control_token(control_token)?;
        crate::trust_control::build_client(url, token)?.verify_passport_challenge(
            &VerifyPassportChallengeRequest {
                presentation: response,
                expected_challenge,
            },
        )?
    } else {
        let challenge = expected_challenge.as_ref().unwrap_or(&response.challenge);
        let (resolved_policy, policy_source) =
            resolve_challenge_policy_local(challenge, verifier_policies_file, now)?;
        require_passport_lifecycle_source(
            resolved_policy
                .as_ref()
                .is_some_and(|policy| policy.require_active_lifecycle),
            passport_statuses_file,
            None,
        )?;
        let mut verification = verify_passport_presentation_response_with_policy(
            &response,
            expected_challenge.as_ref(),
            now,
            resolved_policy.as_ref(),
            policy_source,
        )
        .map_err(|error| CliError::Other(error.to_string()))?;
        let lifecycle = resolve_passport_lifecycle(
            &response.passport,
            now,
            passport_statuses_file,
            None,
            None,
            resolved_policy
                .as_ref()
                .is_some_and(|policy| policy.require_active_lifecycle),
        )?;
        apply_passport_lifecycle_to_presentation(&mut verification, lifecycle);
        if let Some(path) = verifier_challenge_db {
            PassportVerifierChallengeStore::open(path)?.consume(challenge, now)?;
            verification.replay_state = Some("consumed".to_string());
        }
        verification
    };
    let (enterprise_provenance_count, enterprise_provider_ids) =
        summarize_enterprise_provenance(&verification.enterprise_identity_provenance);

    if json_output {
        println!("{}", serde_json::to_string_pretty(&verification)?);
    } else {
        println!("presentation verified");
        println!("subject:              {}", verification.subject);
        println!("passport_id:          {}", verification.passport_id);
        println!("verifier:             {}", verification.verifier);
        if let Some(challenge_id) = verification.challenge_id.as_deref() {
            println!("challenge_id:         {challenge_id}");
        }
        println!("nonce:                {}", verification.nonce);
        println!("accepted:             {}", verification.accepted);
        println!("policy_evaluated:     {}", verification.policy_evaluated);
        if let Some(policy_source) = verification.policy_source.as_deref() {
            println!("policy_source:        {policy_source}");
        }
        if let Some(policy_id) = verification.policy_id.as_deref() {
            println!("policy_id:            {policy_id}");
        }
        println!("credential_count:     {}", verification.credential_count);
        println!("valid_until:          {}", verification.valid_until);
        println!(
            "challenge_expires_at: {}",
            verification.challenge_expires_at
        );
        println!("enterprise_provenance: {}", enterprise_provenance_count);
        if !enterprise_provider_ids.is_empty() {
            println!(
                "enterprise_providers: {}",
                enterprise_provider_ids.join(", ")
            );
        }
        if let Some(lifecycle) = verification.passport_lifecycle.as_ref() {
            println!("lifecycle_state:      {}", lifecycle.state.label());
            if let Some(source) = lifecycle.source.as_deref() {
                println!("lifecycle_source:     {source}");
            }
        }
        if let Some(replay_state) = verification.replay_state.as_deref() {
            println!("replay_state:         {replay_state}");
        }
    }
    Ok(())
}

pub(crate) fn cmd_passport_oid4vp_request_create(
    args: PassportOid4vpRequestCreateArgs<'_>,
) -> Result<(), CliError> {
    let PassportOid4vpRequestCreateArgs {
        output,
        disclosure_claims,
        issuer_allowlist,
        ttl_secs,
        identity_subject,
        identity_continuity_id,
        identity_provider,
        identity_session_hint,
        identity_ttl_secs,
        json_output,
        control_url,
        control_token,
    } = args;
    let identity_assertion = match (identity_subject, identity_continuity_id) {
        (Some(subject), Some(continuity_id)) => Some(CreateIdentityAssertionRequest {
            subject: subject.to_string(),
            continuity_id: continuity_id.to_string(),
            provider: identity_provider.map(str::to_string),
            session_hint: identity_session_hint.map(str::to_string),
            ttl_seconds: identity_ttl_secs,
        }),
        (None, None) => None,
        _ => {
            return Err(CliError::Other(
                "OID4VP identity assertion requires both --identity-subject and --identity-continuity-id"
                    .to_string(),
            ))
        }
    };
    let url = control_url.ok_or_else(|| {
        CliError::Other(
            "OID4VP request creation requires --control-url because it depends on the running trust-control verifier service"
                .to_string(),
        )
    })?;
    let token = crate::require_control_token(control_token)?;
    let response = crate::trust_control::build_client(url, token)?.create_oid4vp_request(
        &CreateOid4vpRequest {
            disclosure_claims: disclosure_claims.to_vec(),
            issuer_allowlist: issuer_allowlist.to_vec(),
            ttl_seconds: ttl_secs,
            identity_assertion,
        },
    )?;

    if let Some(path) = output {
        ensure_parent_dir(path)?;
        fs::write(path, serde_json::to_vec_pretty(&response)?)?;
    }

    if json_output {
        println!("{}", serde_json::to_string_pretty(&response)?);
    } else {
        println!("OID4VP verifier request created");
        println!("request_id:       {}", response.transport.request_id);
        println!("request_uri:      {}", response.transport.request_uri);
        println!("same_device_url:  {}", response.transport.same_device_url);
        println!("cross_device_url: {}", response.transport.cross_device_url);
        println!(
            "descriptor_url:   {}",
            response.wallet_exchange.descriptor.descriptor_url
        );
        println!(
            "exchange_state:   {}",
            response.wallet_exchange.transaction.status.label()
        );
        if let Some(assertion) = response.wallet_exchange.identity_assertion.as_ref() {
            println!("identity_subject: {}", assertion.subject);
            println!("continuity_id:   {}", assertion.continuity_id);
        }
        println!("response_uri:     {}", response.transport.response_uri);
        println!("expires_at:       {}", response.transport.expires_at);
        if let Some(path) = output {
            println!("output:           {}", path.display());
        }
    }
    Ok(())
}

pub(crate) fn cmd_passport_oid4vp_respond(
    args: PassportOid4vpRespondArgs<'_>,
) -> Result<(), CliError> {
    let PassportOid4vpRespondArgs {
        input,
        request_url,
        same_device_url,
        cross_device_url,
        holder_seed_file,
        output,
        submit,
        submit_url,
        at,
        json_output,
    } = args;
    let resolved_request_url = match (request_url, same_device_url, cross_device_url) {
        (Some(url), None, None) => url.to_string(),
        (None, Some(url), None) | (None, None, Some(url)) => oid4vp_request_url_from_launch_url(url)?,
        (None, None, None) => {
            return Err(CliError::Other(
                "OID4VP response requires one of --request-url, --same-device-url, or --cross-device-url"
                    .to_string(),
            ))
        }
        _ => {
            return Err(CliError::Other(
                "OID4VP response accepts exactly one of --request-url, --same-device-url, or --cross-device-url"
                    .to_string(),
            ))
        }
    };
    if output.is_none() && !submit {
        return Err(CliError::Other(
            "OID4VP response requires --output <path> unless --submit is set".to_string(),
        ));
    }

    let request_jwt = fetch_text_url(&resolved_request_url)?;
    let metadata_url = oid4vp_verifier_metadata_url(&resolved_request_url)?;
    let metadata: Oid4vpVerifierMetadata = fetch_json_url(&metadata_url)?;
    metadata
        .validate()
        .map_err(|error| CliError::Other(error.to_string()))?;
    if !resolved_request_url.starts_with(&metadata.request_uri_prefix) {
        return Err(CliError::Other(
            "OID4VP request URL is outside the verifier metadata request_uri_prefix".to_string(),
        ));
    }
    let jwks: PortableJwkSet = fetch_json_url(&metadata.jwks_uri)?;
    let verifier_public_keys = verifier_public_keys_from_jwks(&jwks)?;
    let request: Oid4vpRequestObject = verify_signed_oid4vp_request_object_with_any_key(
        &request_jwt,
        &verifier_public_keys,
        at.unwrap_or_else(unix_now),
    )
    .map_err(|error| CliError::Other(error.to_string()))?;
    if request.request_uri != resolved_request_url {
        return Err(CliError::Other(
            "OID4VP request JWT did not match the fetched request URL".to_string(),
        ));
    }
    if request.client_id != metadata.client_id {
        return Err(CliError::Other(
            "OID4VP verifier metadata client_id did not match the signed request".to_string(),
        ));
    }

    let holder_keypair = load_existing_keypair(holder_seed_file)?;
    let portable_credential = load_text_file(input)?;
    let response_jwt = respond_to_oid4vp_request(
        &holder_keypair,
        &portable_credential,
        &request,
        at.unwrap_or_else(unix_now),
    )
    .map_err(|error| CliError::Other(error.to_string()))?;

    if let Some(path) = output {
        ensure_parent_dir(path)?;
        fs::write(path, response_jwt.as_bytes())?;
    }

    let submit_target = submit
        .then_some(request.response_uri.as_str())
        .or(submit_url);
    let verification = submit_target
        .map(|url| {
            post_form_url::<Oid4vpPresentationVerification>(url, &[("response", &response_jwt)])
        })
        .transpose()?;

    if json_output {
        let payload = if let Some(verification) = verification.as_ref() {
            serde_json::json!({
                "requestId": request.jti,
                "requestUri": request.request_uri,
                "responseUri": request.response_uri,
                "metadataUrl": metadata_url,
                "output": output.map(|path| path.display().to_string()),
                "submitted": true,
                "verification": verification,
            })
        } else {
            serde_json::json!({
                "requestId": request.jti,
                "requestUri": request.request_uri,
                "responseUri": request.response_uri,
                "metadataUrl": metadata_url,
                "output": output.map(|path| path.display().to_string()),
                "submitted": false,
            })
        };
        println!("{}", serde_json::to_string_pretty(&payload)?);
    } else {
        println!("OID4VP response created");
        println!("request_id:       {}", request.jti);
        println!("request_uri:      {}", request.request_uri);
        println!("response_uri:     {}", request.response_uri);
        println!("metadata_url:     {}", metadata_url);
        if let Some(path) = output {
            println!("output:           {}", path.display());
        }
        if let Some(verification) = verification.as_ref() {
            println!("submitted:        true");
            println!("verified_at:      {}", verification.verified_at);
            println!("passport_id:      {}", verification.passport_id);
            println!("subject_did:      {}", verification.subject_did);
            println!("issuer:           {}", verification.issuer);
        } else {
            println!("submitted:        false");
        }
    }
    Ok(())
}

pub(crate) fn cmd_passport_oid4vp_submit(
    input: &Path,
    submit_url: &str,
    json_output: bool,
) -> Result<(), CliError> {
    let response_jwt = load_text_file(input)?;
    let verification: Oid4vpPresentationVerification =
        post_form_url(submit_url, &[("response", response_jwt.as_str())])?;
    if json_output {
        println!("{}", serde_json::to_string_pretty(&verification)?);
    } else {
        println!("OID4VP response submitted");
        println!("request_id:   {}", verification.request_id);
        println!("passport_id:  {}", verification.passport_id);
        println!("subject_did:  {}", verification.subject_did);
        println!("issuer:       {}", verification.issuer);
        println!("verified_at:  {}", verification.verified_at);
        if let Some(transaction) = verification.exchange_transaction.as_ref() {
            println!("exchange_id:  {}", transaction.exchange_id);
            println!("exchange:     {}", transaction.status.label());
        }
        if let Some(assertion) = verification.identity_assertion.as_ref() {
            println!("identity:     {}", assertion.subject);
            println!("continuity:   {}", assertion.continuity_id);
        }
    }
    Ok(())
}

pub(crate) fn cmd_passport_oid4vp_metadata(
    verifier_url: &str,
    json_output: bool,
) -> Result<(), CliError> {
    let url = oid4vp_verifier_metadata_url(&format!(
        "{}/v1/public/passport/oid4vp/requests/example",
        verifier_url.trim_end_matches('/')
    ))?;
    let metadata: Oid4vpVerifierMetadata = fetch_json_url(&url)?;
    metadata
        .validate()
        .map_err(|error| CliError::Other(error.to_string()))?;
    if json_output {
        println!("{}", serde_json::to_string_pretty(&metadata)?);
    } else {
        println!("OID4VP verifier metadata");
        println!("verifier_id:      {}", metadata.verifier_id);
        println!("client_id:        {}", metadata.client_id);
        println!("request_prefix:   {}", metadata.request_uri_prefix);
        println!("response_uri:     {}", metadata.response_uri);
        println!("jwks_uri:         {}", metadata.jwks_uri);
        println!("trusted_key_count: {}", metadata.trusted_key_count);
    }
    Ok(())
}

pub(crate) fn cmd_passport_status_publish(
    input: &Path,
    passport_statuses_file: Option<&Path>,
    resolve_urls: &[String],
    cache_ttl_secs: Option<u64>,
    json_output: bool,
    control_url: Option<&str>,
    control_token: Option<&str>,
) -> Result<(), CliError> {
    let passport: AgentPassport = serde_json::from_slice(&fs::read(input)?)?;
    let distribution = passport_status_distribution(resolve_urls, cache_ttl_secs);
    let record = if let Some(url) = control_url {
        let token = crate::require_control_token(control_token)?;
        crate::trust_control::build_client(url, token)?.publish_passport_status(
            &PublishPassportStatusRequest {
                passport,
                distribution,
            },
        )?
    } else {
        let path = require_passport_status_registry_path(passport_statuses_file)?;
        let mut registry = load_passport_status_registry_for_admin(path)?;
        let record = registry.publish(&passport, unix_now(), distribution)?;
        registry.save(path)?;
        record
    };

    if json_output {
        println!("{}", serde_json::to_string_pretty(&record)?);
    } else {
        println!("passport lifecycle published");
        println!("passport_id:   {}", record.passport_id);
        println!("subject:       {}", record.subject);
        println!("state:         {}", record.status.label());
        println!("issuer_count:  {}", record.issuer_count);
        println!("published_at:  {}", record.published_at);
        println!("updated_at:    {}", record.updated_at);
        if !record.distribution.resolve_urls.is_empty() {
            println!(
                "resolve_urls:  {}",
                record.distribution.resolve_urls.join(", ")
            );
        }
        if let Some(cache_ttl_secs) = record.distribution.cache_ttl_secs {
            println!("cache_ttl:     {cache_ttl_secs}");
        }
    }
    Ok(())
}

pub(crate) fn cmd_passport_status_list(
    passport_statuses_file: Option<&Path>,
    json_output: bool,
    control_url: Option<&str>,
    control_token: Option<&str>,
) -> Result<(), CliError> {
    let response = if let Some(url) = control_url {
        let token = crate::require_control_token(control_token)?;
        crate::trust_control::build_client(url, token)?.list_passport_statuses()?
    } else {
        let path = require_passport_status_registry_path(passport_statuses_file)?;
        let registry = load_passport_status_registry_for_admin(path)?;
        PassportStatusListResponse {
            configured: true,
            count: registry.passports.len(),
            passports: registry.passports.into_values().collect(),
        }
    };

    if json_output {
        println!("{}", serde_json::to_string_pretty(&response)?);
    } else {
        println!("configured: {}", response.configured);
        println!("count:      {}", response.count);
        for record in response.passports {
            println!(
                "- {} {} {}",
                record.passport_id,
                record.subject,
                record.status.label()
            );
        }
    }
    Ok(())
}

pub(crate) fn cmd_passport_status_get(
    passport_id: &str,
    passport_statuses_file: Option<&Path>,
    json_output: bool,
    control_url: Option<&str>,
    control_token: Option<&str>,
) -> Result<(), CliError> {
    let record = if let Some(url) = control_url {
        let token = crate::require_control_token(control_token)?;
        crate::trust_control::build_client(url, token)?.get_passport_status(passport_id)?
    } else {
        let path = require_passport_status_registry_path(passport_statuses_file)?;
        let registry = load_passport_status_registry_for_admin(path)?;
        registry.get(passport_id).cloned().ok_or_else(|| {
            CliError::Other(format!(
                "passport `{passport_id}` was not found in the lifecycle registry"
            ))
        })?
    };

    if json_output {
        println!("{}", serde_json::to_string_pretty(&record)?);
    } else {
        println!("passport_id:   {}", record.passport_id);
        println!("subject:       {}", record.subject);
        println!("state:         {}", record.status.label());
        println!("issuer_count:  {}", record.issuer_count);
        println!("published_at:  {}", record.published_at);
        println!("updated_at:    {}", record.updated_at);
        if let Some(superseded_by) = record.superseded_by.as_deref() {
            println!("superseded_by: {superseded_by}");
        }
        if let Some(revoked_at) = record.revoked_at {
            println!("revoked_at:    {revoked_at}");
        }
        if let Some(reason) = record.revoked_reason.as_deref() {
            println!("revoked_reason:{reason}");
        }
    }
    Ok(())
}

pub(crate) fn cmd_passport_status_resolve(
    passport_id: &str,
    passport_statuses_file: Option<&Path>,
    json_output: bool,
    control_url: Option<&str>,
    control_token: Option<&str>,
) -> Result<(), CliError> {
    let resolution = if let Some(url) = control_url {
        let client = crate::trust_control::build_client(url, control_token.unwrap_or_default())?;
        if control_token.is_some() {
            client.resolve_passport_status(passport_id)?
        } else {
            client.public_resolve_passport_status(passport_id)?
        }
    } else {
        let path = require_passport_status_registry_path(passport_statuses_file)?;
        let registry = load_passport_status_registry_for_admin(path)?;
        let mut resolution = registry.resolve(passport_id);
        resolution.source = Some(format!("registry:{}", path.display()));
        resolution
    };

    if json_output {
        println!("{}", serde_json::to_string_pretty(&resolution)?);
    } else {
        println!("passport_id:   {}", resolution.passport_id);
        println!("state:         {}", resolution.state.label());
        if !resolution.subject.is_empty() {
            println!("subject:       {}", resolution.subject);
        }
        if let Some(updated_at) = resolution.updated_at {
            println!("updated_at:    {updated_at}");
        }
        if let Some(source) = resolution.source.as_deref() {
            println!("source:        {source}");
        }
        if let Some(superseded_by) = resolution.superseded_by.as_deref() {
            println!("superseded_by: {superseded_by}");
        }
        if let Some(revoked_at) = resolution.revoked_at {
            println!("revoked_at:    {revoked_at}");
        }
        if let Some(reason) = resolution.revoked_reason.as_deref() {
            println!("revoked_reason:{reason}");
        }
    }
    Ok(())
}

pub(crate) fn cmd_passport_status_revoke(
    passport_id: &str,
    passport_statuses_file: Option<&Path>,
    reason: Option<&str>,
    revoked_at: Option<u64>,
    json_output: bool,
    control_url: Option<&str>,
    control_token: Option<&str>,
) -> Result<(), CliError> {
    let record = if let Some(url) = control_url {
        let token = crate::require_control_token(control_token)?;
        crate::trust_control::build_client(url, token)?.revoke_passport_status(
            passport_id,
            &PassportStatusRevocationRequest {
                reason: reason.map(str::to_string),
                revoked_at,
            },
        )?
    } else {
        let path = require_passport_status_registry_path(passport_statuses_file)?;
        let mut registry = load_passport_status_registry_for_admin(path)?;
        let record = registry.revoke(passport_id, reason, revoked_at)?;
        registry.save(path)?;
        record
    };

    if json_output {
        println!("{}", serde_json::to_string_pretty(&record)?);
    } else {
        println!("passport revoked");
        println!("passport_id:   {}", record.passport_id);
        println!("state:         {}", record.status.label());
        if let Some(revoked_at) = record.revoked_at {
            println!("revoked_at:    {revoked_at}");
        }
        if let Some(reason) = record.revoked_reason.as_deref() {
            println!("revoked_reason:{reason}");
        }
    }
    Ok(())
}

fn load_passport_verifier_policy(path: &Path) -> Result<PassportVerifierPolicy, CliError> {
    let contents = fs::read_to_string(path)?;
    let policy = if path
        .extension()
        .and_then(|extension| extension.to_str())
        .is_some_and(|extension| matches!(extension, "yaml" | "yml"))
    {
        serde_yml::from_str(&contents)?
    } else if let Ok(document) = serde_json::from_str::<SignedPassportVerifierPolicy>(&contents) {
        verify_signed_passport_verifier_policy(&document)
            .map_err(|error| CliError::Other(error.to_string()))?;
        document.body.policy
    } else {
        serde_json::from_str(&contents).or_else(|_| serde_yml::from_str(&contents))?
    };
    Ok(policy)
}

fn resolve_challenge_policy_local(
    challenge: &PassportPresentationChallenge,
    verifier_policies_file: Option<&Path>,
    now: u64,
) -> Result<(Option<PassportVerifierPolicy>, Option<String>), CliError> {
    if let Some(policy) = challenge.policy.as_ref() {
        return Ok((Some(policy.clone()), Some("embedded".to_string())));
    }
    let Some(reference) = challenge.policy_ref.as_ref() else {
        return Ok((None, None));
    };
    let path = require_verifier_policy_registry_path(verifier_policies_file)?;
    let registry = load_verifier_policy_registry_for_admin(path)?;
    let document = registry.active_policy(&reference.policy_id, now)?;
    if document.body.verifier != challenge.verifier {
        return Err(CliError::Other(format!(
            "verifier policy `{}` is bound to verifier `{}` but challenge expects `{}`",
            document.body.policy_id, document.body.verifier, challenge.verifier
        )));
    }
    Ok((
        Some(document.body.policy.clone()),
        Some(format!("registry:{}", document.body.policy_id)),
    ))
}
