//! Typed Cortex / pai-axiom boundary envelopes.
//!
//! ADR 0040 requires cross-system exchanges to carry machine-readable
//! constraints and receipt state. These structs are shape-only: they do not
//! grant authority, persist records, or bypass the receiver's own gates.

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::{
    AxiomConstraint, ClaimCeiling, ContextPackId, ProofClosureReport, ProvenanceClass,
    SemanticTrustClass,
};

/// Current boundary envelope schema version.
pub const BOUNDARY_SCHEMA_VERSION: u16 = 1;

/// Stable type string for Cortex -> AXIOM constraint envelopes.
pub const CORTEX_TO_AXIOM_CONSTRAINT_ENVELOPE_V1: &str = "cortex.boundary.constraint_envelope.v1";

/// Stable type string for pai-axiom -> Cortex execution receipts.
pub const PAI_AXIOM_TO_CORTEX_EXECUTION_RECEIPT_V1: &str =
    "pai_axiom.boundary.execution_receipt.v1";

/// Contradiction state crossing the Cortex / pai-axiom boundary.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum BoundaryContradictionState {
    /// Contradictions were scanned and resolved for this use.
    Resolved,
    /// Multiple hypotheses remain intentionally represented.
    MultiHypothesis,
    /// Contradiction state is missing or not scanned.
    Unknown,
    /// An unresolved contradiction blocks authority-bearing use.
    Blocked,
}

/// Quarantine state crossing the Cortex / pai-axiom boundary.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum BoundaryQuarantineState {
    /// No quarantine or contamination is known.
    Clean,
    /// Material may be shown only for diagnostics.
    DiagnosticOnly,
    /// Material is quarantined and must not guide authority-bearing work.
    Quarantined,
    /// Material is contaminated by a failed or unsafe path.
    Contaminated,
}

/// Redaction state crossing the Cortex / pai-axiom boundary.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum BoundaryRedactionState {
    /// Raw operator-mode material is present under explicit opt-in.
    RawOperatorOptIn,
    /// Content has been redacted.
    Redacted,
    /// Content has been abstracted.
    Abstracted,
    /// Content is safe for the declared export posture.
    ExportSafe,
}

/// Claim language permitted to the receiving AXIOM runtime.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum AllowedClaimLanguage {
    /// Candidate claim only.
    CandidateClaim,
    /// Evidence reference only.
    EvidenceReference,
    /// Constraint or limit.
    Constraint,
    /// Residual risk.
    ResidualRisk,
    /// Request verification instead of asserting.
    VerificationRequest,
    /// Refusal or abstention.
    Refusal,
}

/// Authority-bearing use forbidden by a boundary envelope.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ForbiddenBoundaryUse {
    /// Principle or doctrine promotion.
    Promotion,
    /// Trusted run-history claim.
    TrustedHistory,
    /// Compliance evidence.
    ComplianceEvidence,
    /// Export as trusted material.
    Export,
    /// Release gate or release evidence.
    Release,
    /// External reporting.
    ExternalReporting,
    /// Execution authority or tool permission.
    ExecutionAuthority,
}

/// Cortex -> pai-axiom constraint envelope.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct CortexAxiomConstraintEnvelopeV1 {
    /// Schema version.
    pub schema_version: u16,
    /// Stable envelope type string.
    pub envelope_type: String,
    /// Context pack being constrained.
    pub context_pack_id: ContextPackId,
    /// Proof closure state and failing edges for the pack/use.
    pub proof_state: ProofClosureReport,
    /// Maximum claim strength permitted.
    pub truth_ceiling: ClaimCeiling,
    /// Semantic trust class for the selected context.
    pub semantic_trust: SemanticTrustClass,
    /// Weakest provenance class represented in selected context.
    pub provenance_class: ProvenanceClass,
    /// Contradiction posture.
    pub contradiction_state: BoundaryContradictionState,
    /// Quarantine posture.
    pub quarantine_state: BoundaryQuarantineState,
    /// Redaction posture.
    pub redaction_state: BoundaryRedactionState,
    /// Claim language the receiver may use.
    pub allowed_claim_language: Vec<AllowedClaimLanguage>,
    /// Authority-bearing uses forbidden by this envelope.
    pub forbidden_uses: Vec<ForbiddenBoundaryUse>,
    /// Detailed AXIOM constraints already understood by existing exports.
    pub constraints: Vec<AxiomConstraint>,
}

impl CortexAxiomConstraintEnvelopeV1 {
    /// Construct an envelope with the stable v1 type string.
    #[must_use]
    pub fn new(
        context_pack_id: ContextPackId,
        proof_state: ProofClosureReport,
        truth_ceiling: ClaimCeiling,
        semantic_trust: SemanticTrustClass,
        provenance_class: ProvenanceClass,
    ) -> Self {
        Self {
            schema_version: BOUNDARY_SCHEMA_VERSION,
            envelope_type: CORTEX_TO_AXIOM_CONSTRAINT_ENVELOPE_V1.to_string(),
            context_pack_id,
            proof_state,
            truth_ceiling,
            semantic_trust,
            provenance_class,
            contradiction_state: BoundaryContradictionState::Unknown,
            quarantine_state: BoundaryQuarantineState::DiagnosticOnly,
            redaction_state: BoundaryRedactionState::Abstracted,
            allowed_claim_language: default_allowed_claim_language(),
            forbidden_uses: default_forbidden_boundary_uses(),
            constraints: Vec::new(),
        }
    }
}

/// Capability-token decision from pai-axiom.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum CapabilityTokenDecision {
    /// Token and policy allowed the operation.
    Allowed,
    /// Token allowed with warning.
    Warned,
    /// Token or policy rejected the operation.
    Rejected,
    /// Token was expired.
    Expired,
    /// Token was revoked.
    Revoked,
}

/// Capability-token state supplied by pai-axiom.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct CapabilityTokenState {
    /// Final token decision.
    pub decision: CapabilityTokenDecision,
    /// Token structure was valid.
    pub valid_structure: bool,
    /// Token audience matched Cortex.
    pub audience_bound: bool,
    /// Token scope matched the operation.
    pub scope_bound: bool,
    /// Token operation binding matched the receipt.
    pub operation_bound: bool,
    /// Token was not expired at execution time.
    pub not_expired: bool,
    /// Token was not revoked at execution time.
    pub not_revoked: bool,
    /// Policy allowed the operation.
    pub policy_allowed: bool,
    /// Attestation link was present and verified.
    pub attestation_linked: bool,
}

/// Runtime integrity state supplied by pai-axiom.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum RuntimeIntegrityState {
    /// Runtime integrity was not verified.
    Unverified,
    /// Release artifact was verified.
    VerifiedRelease,
    /// Release provenance was verified.
    VerifiedProvenance,
    /// Runtime is known or suspected compromised.
    Compromised,
}

/// Execution trust state supplied by pai-axiom.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ExecutionTrustState {
    /// Runtime integrity state.
    pub runtime_integrity: RuntimeIntegrityState,
    /// Optional release or provenance evidence ref.
    pub evidence_ref: Option<String>,
}

/// Operator approval state supplied by pai-axiom.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum OperatorApprovalState {
    /// Approval was not required.
    NotRequired,
    /// Approval was required but absent.
    RequiredMissing,
    /// Approval was supplied and operation-bound.
    ApprovedBound,
    /// Approval was supplied but not operation-bound.
    ApprovedUnbound,
}

/// Tool invocation outcome in a boundary receipt.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum BoundaryToolOutcome {
    /// Tool invocation succeeded.
    Succeeded,
    /// Tool invocation failed.
    Failed,
    /// Tool invocation was blocked.
    Blocked,
}

/// Tool provenance item in a boundary receipt.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct BoundaryToolInvocation {
    /// Tool name.
    pub tool_name: String,
    /// Invocation identifier.
    pub invocation_id: String,
    /// Stable input anchor.
    pub input_ref: Option<String>,
    /// Stable output anchor.
    pub output_ref: Option<String>,
    /// Invocation outcome.
    pub outcome: BoundaryToolOutcome,
}

/// Source anchor in a boundary receipt.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct BoundarySourceAnchor {
    /// Source reference.
    pub reference: String,
    /// Source kind.
    pub kind: String,
}

/// pai-axiom -> Cortex execution receipt.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct PaiAxiomExecutionReceiptV1 {
    /// Schema version.
    pub schema_version: u16,
    /// Stable receipt type string.
    pub receipt_type: String,
    /// Runtime/workloop identity.
    pub runtime_id: String,
    /// Capability-token state.
    pub capability_token_state: CapabilityTokenState,
    /// Execution trust state.
    pub execution_trust_state: ExecutionTrustState,
    /// Tool invocations and anchors.
    pub tool_provenance: Vec<BoundaryToolInvocation>,
    /// Operator approval state.
    pub operator_approval_state: OperatorApprovalState,
    /// Runtime quarantine state.
    pub quarantine_state: BoundaryQuarantineState,
    /// Source anchors for claims proposed to Cortex.
    pub source_anchors: Vec<BoundarySourceAnchor>,
    /// Explicit residual risk strings.
    pub residual_risk: Vec<String>,
    /// Must remain true for Cortex admission: receipt is not promotion.
    pub explicit_non_promotion: bool,
}

impl PaiAxiomExecutionReceiptV1 {
    /// Construct a v1 execution receipt.
    #[must_use]
    pub fn new(
        runtime_id: impl Into<String>,
        capability_token_state: CapabilityTokenState,
        execution_trust_state: ExecutionTrustState,
        operator_approval_state: OperatorApprovalState,
    ) -> Self {
        Self {
            schema_version: BOUNDARY_SCHEMA_VERSION,
            receipt_type: PAI_AXIOM_TO_CORTEX_EXECUTION_RECEIPT_V1.to_string(),
            runtime_id: runtime_id.into(),
            capability_token_state,
            execution_trust_state,
            tool_provenance: Vec::new(),
            operator_approval_state,
            quarantine_state: BoundaryQuarantineState::DiagnosticOnly,
            source_anchors: Vec::new(),
            residual_risk: Vec::new(),
            explicit_non_promotion: true,
        }
    }
}

/// Default claim language for constrained AXIOM work.
#[must_use]
pub fn default_allowed_claim_language() -> Vec<AllowedClaimLanguage> {
    vec![
        AllowedClaimLanguage::CandidateClaim,
        AllowedClaimLanguage::EvidenceReference,
        AllowedClaimLanguage::Constraint,
        AllowedClaimLanguage::ResidualRisk,
        AllowedClaimLanguage::VerificationRequest,
        AllowedClaimLanguage::Refusal,
    ]
}

/// Default forbidden authority-bearing uses for cross-boundary context.
#[must_use]
pub fn default_forbidden_boundary_uses() -> Vec<ForbiddenBoundaryUse> {
    vec![
        ForbiddenBoundaryUse::Promotion,
        ForbiddenBoundaryUse::TrustedHistory,
        ForbiddenBoundaryUse::ComplianceEvidence,
        ForbiddenBoundaryUse::Export,
        ForbiddenBoundaryUse::Release,
        ForbiddenBoundaryUse::ExternalReporting,
        ForbiddenBoundaryUse::ExecutionAuthority,
    ]
}

#[cfg(test)]
mod tests {
    use serde_json::json;

    use super::*;

    fn token_state(decision: CapabilityTokenDecision) -> CapabilityTokenState {
        CapabilityTokenState {
            decision,
            valid_structure: true,
            audience_bound: true,
            scope_bound: true,
            operation_bound: true,
            not_expired: true,
            not_revoked: true,
            policy_allowed: true,
            attestation_linked: true,
        }
    }

    #[test]
    fn boundary_type_strings_are_stable() {
        assert_eq!(
            CORTEX_TO_AXIOM_CONSTRAINT_ENVELOPE_V1,
            "cortex.boundary.constraint_envelope.v1"
        );
        assert_eq!(
            PAI_AXIOM_TO_CORTEX_EXECUTION_RECEIPT_V1,
            "pai_axiom.boundary.execution_receipt.v1"
        );
    }

    #[test]
    fn boundary_enum_wire_strings_are_stable() {
        assert_eq!(
            serde_json::to_value(BoundaryContradictionState::MultiHypothesis).unwrap(),
            json!("multi_hypothesis")
        );
        assert_eq!(
            serde_json::to_value(BoundaryQuarantineState::DiagnosticOnly).unwrap(),
            json!("diagnostic_only")
        );
        assert_eq!(
            serde_json::to_value(BoundaryRedactionState::RawOperatorOptIn).unwrap(),
            json!("raw_operator_opt_in")
        );
        assert_eq!(
            serde_json::to_value(AllowedClaimLanguage::VerificationRequest).unwrap(),
            json!("verification_request")
        );
        assert_eq!(
            serde_json::to_value(ForbiddenBoundaryUse::TrustedHistory).unwrap(),
            json!("trusted_history")
        );
        assert_eq!(
            serde_json::to_value(CapabilityTokenDecision::Revoked).unwrap(),
            json!("revoked")
        );
        assert_eq!(
            serde_json::to_value(RuntimeIntegrityState::VerifiedProvenance).unwrap(),
            json!("verified_provenance")
        );
        assert_eq!(
            serde_json::to_value(OperatorApprovalState::ApprovedBound).unwrap(),
            json!("approved_bound")
        );
        assert_eq!(
            serde_json::to_value(BoundaryToolOutcome::Blocked).unwrap(),
            json!("blocked")
        );
    }

    #[test]
    fn constraint_envelope_defaults_forbid_authority_bearing_uses() {
        let envelope = CortexAxiomConstraintEnvelopeV1::new(
            ContextPackId::new(),
            ProofClosureReport::full_chain_verified(Vec::new()),
            ClaimCeiling::LocalUnsigned,
            SemanticTrustClass::CandidateOnly,
            ProvenanceClass::RuntimeDerived,
        );

        assert_eq!(envelope.schema_version, BOUNDARY_SCHEMA_VERSION);
        assert_eq!(
            envelope.envelope_type,
            CORTEX_TO_AXIOM_CONSTRAINT_ENVELOPE_V1
        );
        assert!(envelope
            .forbidden_uses
            .contains(&ForbiddenBoundaryUse::ExecutionAuthority));
        assert!(envelope
            .allowed_claim_language
            .contains(&AllowedClaimLanguage::Refusal));
    }

    #[test]
    fn execution_receipt_defaults_to_candidate_only_non_promotion() {
        let receipt = PaiAxiomExecutionReceiptV1::new(
            "axiom-runtime:v3.1",
            token_state(CapabilityTokenDecision::Allowed),
            ExecutionTrustState {
                runtime_integrity: RuntimeIntegrityState::Unverified,
                evidence_ref: None,
            },
            OperatorApprovalState::NotRequired,
        );

        assert_eq!(receipt.schema_version, BOUNDARY_SCHEMA_VERSION);
        assert_eq!(
            receipt.receipt_type,
            PAI_AXIOM_TO_CORTEX_EXECUTION_RECEIPT_V1
        );
        assert!(receipt.explicit_non_promotion);
        assert_eq!(
            receipt.quarantine_state,
            BoundaryQuarantineState::DiagnosticOnly
        );
    }
}
