//! Authority algebra: terms, capabilities, verbs, attenuation, and effect bounds.
use serde::{Deserialize, Serialize};

use crate::schema::{IsoDateTime, NonEmptyString, RunxSchema};
use crate::{JsonNumber, JsonObject, ProofKind, Reference};

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
#[serde(rename_all = "snake_case")]
pub enum AuthorityResourceFamily {
    GithubRepo,
    Workspace,
    Filesystem,
    Network,
    Deployment,
    Credential,
    Effect,
    Artifact,
    Harness,
    Publication,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
#[serde(rename_all = "snake_case")]
pub enum AuthorityVerb {
    Read,
    Write,
    Comment,
    Review,
    Approve,
    Merge,
    Create,
    Update,
    Delete,
    Execute,
    Verify,
    Estimate,
    Prepare,
    Commit,
    Reverse,
    Publish,
    SpawnChild,
    Revoke,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
#[serde(rename_all = "snake_case")]
pub enum AuthorityCapability {
    FilesystemRead,
    FilesystemWrite,
    NetworkEgress,
    SecretRead,
    ProcessSpawn,
    ProviderMutation,
    PublicPublication,
    ChildHarnessSpawn,
    EffectSingleUseCapability,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
#[serde(rename_all = "snake_case")]
pub enum AuthorityEffectCredentialForm {
    SingleUseCapability,
    ExternalSigner,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
#[serde(deny_unknown_fields)]
pub struct AuthorityEffectLimit {
    pub family: NonEmptyString,
    pub unit: NonEmptyString,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_per_call_units: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_per_run_units: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_per_period_units: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub period: Option<NonEmptyString>,
    pub channels: Vec<NonEmptyString>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub realm: Option<NonEmptyString>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub peer: Option<NonEmptyString>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub operation: Option<NonEmptyString>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub preflight_ttl_ms: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub approval_threshold_units: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub authorization_form: Option<AuthorityEffectCredentialForm>,
    #[serde(default, skip_serializing_if = "is_false")]
    pub preflight_required: bool,
    #[serde(default, skip_serializing_if = "is_false")]
    pub commitment_required: bool,
    #[serde(default, skip_serializing_if = "is_false")]
    pub idempotency_required: bool,
    #[serde(default, skip_serializing_if = "is_false")]
    pub recovery_required: bool,
    #[serde(default, skip_serializing_if = "is_false")]
    pub receipt_before_success: bool,
    #[serde(default, skip_serializing_if = "is_false")]
    pub single_use_capability: bool,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
#[serde(rename_all = "snake_case")]
pub enum AuthorityEffectGuardKind {
    ReceiptBeforeSuccess,
    NonReplay,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
#[serde(deny_unknown_fields)]
pub struct AuthorityEffectGuard {
    pub family: NonEmptyString,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub guard_kinds: Vec<AuthorityEffectGuardKind>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub proof_kinds: Vec<ProofKind>,
}

#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, RunxSchema)]
#[serde(deny_unknown_fields)]
pub struct AuthorityBounds {
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub repo_path_globs: Vec<NonEmptyString>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub branch_patterns: Vec<NonEmptyString>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub filesystem_roots: Vec<NonEmptyString>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub network_destinations: Vec<NonEmptyString>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub deployment_environments: Vec<NonEmptyString>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub token_audiences: Vec<NonEmptyString>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_cost_units: Option<JsonNumber>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub effect_limits: Vec<AuthorityEffectLimit>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub effects: Vec<AuthorityEffectGuard>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_runtime_ms: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_fanout: Option<u32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_child_depth: Option<u32>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
#[serde(rename_all = "snake_case")]
pub enum AuthorityConditionPredicate {
    SignalVerified,
    DecisionSelected,
    HostPostureValid,
    ApprovalPresent,
    WithinTimeWindow,
    WithinBudget,
    SandboxEnforced,
    EffectProofPresent,
    EffectRecoveryAvailable,
}

fn is_false(value: &bool) -> bool {
    !*value
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, RunxSchema)]
#[serde(deny_unknown_fields)]
pub struct AuthorityCondition {
    pub condition_id: NonEmptyString,
    pub predicate: AuthorityConditionPredicate,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub refs: Vec<Reference>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parameters: Option<JsonObject>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
#[serde(deny_unknown_fields)]
pub struct AuthorityApproval {
    pub approval_ref: Reference,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub approved_by_ref: Option<Reference>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub approved_at: Option<IsoDateTime>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub criterion_ids: Vec<NonEmptyString>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, RunxSchema)]
#[serde(deny_unknown_fields)]
pub struct AuthorityTerm {
    pub term_id: NonEmptyString,
    pub principal_ref: Reference,
    pub resource_ref: Reference,
    pub resource_family: AuthorityResourceFamily,
    pub verbs: Vec<AuthorityVerb>,
    pub bounds: AuthorityBounds,
    #[serde(default)]
    pub conditions: Vec<AuthorityCondition>,
    #[serde(default)]
    pub approvals: Vec<AuthorityApproval>,
    #[serde(default)]
    pub capabilities: Vec<AuthorityCapability>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub expires_at: Option<IsoDateTime>,
    pub issued_by_ref: Reference,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub credential_ref: Option<Reference>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
#[serde(rename_all = "snake_case")]
pub enum AuthoritySubsetRelation {
    Equal,
    Subset,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
#[serde(deny_unknown_fields)]
pub struct AuthoritySubsetComparison {
    pub child_term_id: NonEmptyString,
    pub parent_term_id: NonEmptyString,
    pub relation: AuthoritySubsetRelation,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
pub enum AuthoritySubsetResult {
    #[serde(rename = "subset")]
    Subset,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
#[serde(deny_unknown_fields)]
#[runx_schema(
    id = "runx.authority_subset_proof.v1",
    url = "https://schemas.runx.dev/runx/authority/subset-proof/v1.json"
)]
pub struct AuthoritySubsetProof {
    pub parent_authority_ref: Reference,
    pub comparison_algorithm: NonEmptyString,
    pub result: AuthoritySubsetResult,
    pub compared_terms: Vec<AuthoritySubsetComparison>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub proof_ref: Option<Reference>,
    pub checked_at: IsoDateTime,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
#[serde(deny_unknown_fields)]
pub struct AuthorityAttenuation {
    pub parent_authority_ref: Option<Reference>,
    pub subset_proof: Option<AuthoritySubsetProof>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
pub enum AuthoritySchema {
    #[serde(rename = "runx.authority.v1")]
    V1,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, RunxSchema)]
#[serde(deny_unknown_fields)]
#[runx_schema(id = "runx.authority.v1")]
pub struct Authority {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub schema: Option<AuthoritySchema>,
    pub actor_ref: Reference,
    #[serde(default)]
    pub authority_proof_refs: Vec<Reference>,
    #[serde(default)]
    pub grant_refs: Vec<Reference>,
    #[serde(default)]
    pub scope_refs: Vec<Reference>,
    #[serde(default)]
    pub policy_refs: Vec<Reference>,
    #[serde(default)]
    pub terms: Vec<AuthorityTerm>,
    pub attenuation: AuthorityAttenuation,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub mandate_ref: Option<Reference>,
}
