use serde::{Deserialize, Serialize};

// ── Core (DAG)
// ────────────────────────────────────────────────────────────────

/// Node polarity in the ontology DAG.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Pole {
    Yang,
    Yin,
    Spiral,
}

/// Abstraction level of a node.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum AbstractionLevel {
    Axiom,
    Tension,
    Practice,
    Project,
    Moment,
}

/// Edge type between nodes.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum EdgeType {
    Contains,
    TensionWith,
    ManifestsIn,
    ValidatedBy,
    FlowsTo,
    CyclesIn,
    SpiralsWith,
    LimitedBy,
    Complements,
}

/// An ontology node (axiom, tension, practice, etc.).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Node {
    pub id: String,
    pub name: String,
    pub pole: Pole,
    pub level: AbstractionLevel,
    pub description: String,
    #[serde(default)]
    pub invariant: bool,
    #[serde(default)]
    pub artifact_paths: Vec<String>,
    #[serde(default)]
    pub adrs: Vec<String>,
}

/// A directed edge between two nodes.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Edge {
    pub from: String,
    pub to: String,
    pub kind: EdgeType,
    #[serde(default = "Edge::default_weight")]
    pub weight: f64,
    #[serde(default)]
    pub note: String,
}

impl Edge {
    fn default_weight() -> f64 {
        1.0
    }
}

/// Deserialization root for core.ncl exports.
#[derive(Debug, Deserialize)]
pub struct CoreConfig {
    pub nodes: Vec<Node>,
    pub edges: Vec<Edge>,
}

// ── State (FSM)
// ───────────────────────────────────────────────────────────────

/// Tension level of a state.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum TensionLevel {
    High,
    Medium,
    Low,
    Ignored,
}

/// Time horizon for a dimension or transition.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Horizon {
    Weeks,
    Months,
    Years,
    Continuous,
}

/// A discrete state within a dimension.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DimensionState {
    pub id: String,
    pub name: String,
    pub description: String,
    pub tension: TensionLevel,
}

/// A valid transition between two states.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Transition {
    pub from: String,
    pub to: String,
    pub condition: String,
    pub catalyst: String,
    pub blocker: String,
    pub horizon: Horizon,
}

/// Coupling relationship between dimensions.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Coupling {
    pub origin: String,
    pub destination: String,
    pub kind: String,
    pub note: String,
}

/// A tracked dimension of the project's state (FSM).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Dimension {
    pub id: String,
    pub name: String,
    pub description: String,
    pub current_state: String,
    pub desired_state: String,
    pub horizon: Horizon,
    pub states: Vec<DimensionState>,
    pub transitions: Vec<Transition>,
    #[serde(default)]
    pub coupled_with: Vec<String>,
}

/// Deserialization root for state.ncl exports.
#[derive(Debug, Deserialize)]
pub struct StateConfig {
    pub dimensions: Vec<Dimension>,
}

// ── Gate (membranes)
// ──────────────────────────────────────────────────────────

/// Membrane permeability level.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Permeability {
    High,
    Medium,
    Low,
    Closed,
}

/// Signal processing protocol.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Protocol {
    Observe,
    Absorb,
    Challenge,
    Reject,
}

/// Maximum duration a membrane can remain open.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Duration {
    Moment,
    Days,
    Weeks,
    Indefinite,
}

/// Known signal types that membranes can accept.
/// Uses `#[serde(other)]` so project-specific extensions deserialize as
/// `Unknown`.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum SignalType {
    FrameBreakingQuestion,
    UsageFriction,
    ProductiveMisunderstanding,
    HardBug,
    ContextlessInsight,
    SignificantSilence,
    ConnectsToPractice,
    EcosystemRelevance,
    DepthDemonstrated,
    IdentityReinforcement,
    OpportunityAlignment,
    #[serde(other)]
    Unknown,
}

/// Conditions under which a membrane opens.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OpeningCondition {
    pub max_tension_dimensions: u32,
    pub pending_transitions: u32,
    pub core_stable: bool,
    pub description: String,
}

/// A gate membrane — filters incoming signals to protect ontology invariants.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Membrane {
    pub id: String,
    pub name: String,
    pub description: String,
    pub permeability: Permeability,
    pub accepts: Vec<SignalType>,
    pub protects: Vec<String>,
    pub opening_condition: OpeningCondition,
    pub closing_condition: String,
    pub protocol: Protocol,
    pub max_duration: Duration,
    #[serde(default)]
    pub active: bool,
}

/// Deserialization root for gate.ncl exports.
#[derive(Debug, Deserialize)]
pub struct GateConfig {
    pub membranes: Vec<Membrane>,
}
