use chrono::{DateTime, Utc};

use crate::read_model::SubjectChainSourceMode;

/// Processed event ready for the inspector.
/// Use `EventDisplay` to control `name` and `summary`.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct InspectorEvent {
    pub seq: i64,
    pub ts: DateTime<Utc>,
    /// The event_type column (codec name, e.g. "OrderEvent").
    #[cfg_attr(feature = "graphql", graphql(name = "type"))]
    pub event_type: String,
    /// Human-readable display name from `EventDisplay::display_name`.
    pub name: String,
    /// Causal event UUID.
    pub id: Option<String>,
    /// Parent event UUID (causal link).
    pub causation_id: Option<String>,
    /// Workflow ID linking the full causal chain.
    pub workflow_id: Option<String>,
    pub reactor_id: Option<String>,
    /// Aggregate type (e.g. "Order"), if this event matched an aggregator.
    pub aggregate_type: Option<String>,
    /// Aggregate instance ID, if this event matched an aggregator.
    pub aggregate_id: Option<String>,
    /// Per-stream version, if this event matched an aggregator.
    pub stream_revision: Option<i64>,
    /// Optional one-line summary from `EventDisplay::summary`.
    pub summary: Option<String>,
    /// JSON payload as string.
    pub payload: String,
}

impl InspectorEvent {
    /// Composite aggregate key (e.g. "Order:00000000-…"), or `None` if no aggregate identity.
    pub fn aggregate_key(&self) -> Option<String> {
        match (&self.aggregate_type, &self.aggregate_id) {
            (Some(t), Some(id)) => Some(format!("{t}:{id}")),
            _ => None,
        }
    }
}

#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct InspectorEventsPage {
    pub events: Vec<InspectorEvent>,
    pub next_cursor: Option<i64>,
}

#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct InspectorCausalTree {
    pub events: Vec<InspectorEvent>,
    pub root_seq: i64,
}

#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct InspectorCausalFlow {
    pub events: Vec<InspectorEvent>,
}

#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct ReactorLog {
    pub event_id: String,
    pub reactor_id: String,
    pub level: String,
    pub message: String,
    pub data: Option<serde_json::Value>,
    pub logged_at: DateTime<Utc>,
}

#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct ReactorDescription {
    pub reactor_id: String,
    pub blocks: serde_json::Value,
}

#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct ReactorDescriptionSnapshot {
    pub seq: i64,
    pub event_id: String,
    pub reactor_id: String,
    pub blocks: serde_json::Value,
}

/// A single aggregate's state at a point in the workflow timeline.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct AggregateStateEntry {
    /// Aggregate type + ID key (e.g. "pipeline_state:00000000-…").
    pub key: String,
    /// Serialized aggregate state JSON.
    pub state: serde_json::Value,
}

/// Aggregate state snapshot at a specific event in a workflow.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct AggregateTimelineEntry {
    pub seq: i64,
    pub event_id: String,
    pub event_type: String,
    /// All aggregate states after this event was applied.
    pub aggregates: Vec<AggregateStateEntry>,
}

/// A reactor's input/output dependency edges for the dependency map.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct ReactorDependency {
    pub reactor_id: String,
    pub input_event_types: Vec<String>,
    pub output_event_types: Vec<String>,
}

/// An aggregate lifecycle entry — state snapshot across all workflows.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct AggregateLifecycleEntry {
    pub seq: i64,
    pub event_id: String,
    pub event_type: String,
    pub ts: DateTime<Utc>,
    pub workflow_id: String,
    pub aggregate_key: String,
    pub state: serde_json::Value,
}

/// Summary of a workflow chain for the explorer pane.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct CorrelationSummary {
    pub workflow_id: String,
    pub event_count: i64,
    pub first_ts: DateTime<Utc>,
    pub last_ts: DateTime<Utc>,
    pub root_event_type: String,
    pub has_errors: bool,
}

/// Paginated workflow summary response.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct CorrelationSummaryPage {
    pub workflows: Vec<CorrelationSummary>,
    pub next_cursor: Option<String>,
}

#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct ReactorOutcome {
    pub reactor_id: String,
    pub status: String,
    pub error: Option<String>,
    pub attempts: i64,
    pub started_at: Option<DateTime<Utc>>,
    pub completed_at: Option<DateTime<Utc>>,
    pub triggering_event_ids: Vec<String>,
    /// True when this reactor accepted a divergent redelivery (a
    /// nondeterministic output whose persisted row was kept). Distinct from
    /// `status`: the reaction completed, but its re-emission diverged — a
    /// determinism warning, not a failure.
    pub diverged: bool,
}

/// Per-attempt execution record for the waterfall / debugging view.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct ReactorAttempt {
    pub event_id: String,
    pub reactor_id: String,
    pub workflow_id: String,
    pub attempt: i32,
    pub status: String,
    pub error: Option<String>,
    pub started_at: DateTime<Utc>,
    pub completed_at: DateTime<Utc>,
}

// ── Entity-scoped inspection types ───────────────────────────────────────────

/// One `ctx.effect()` result for the effects inspector panel.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct InspectorEffect {
    pub consumer:   String,
    pub label:      String,
    pub value:      serde_json::Value,
    pub created_at: DateTime<Utc>,
}

/// A single event in a subject chain response, enriched with display names.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct InspectorSubjectChainEvent {
    pub seq:             i64,
    pub ts:              DateTime<Utc>,
    #[cfg_attr(feature = "graphql", graphql(name = "type"))]
    pub event_type:      String,
    pub name:            String,
    pub id:              Option<String>,
    pub causation_id:    Option<String>,
    pub workflow_id:     Option<String>,
    pub reactor_id:      Option<String>,
    pub aggregate_type:  Option<String>,
    pub aggregate_id:    Option<String>,
    pub stream_revision: Option<i64>,
    pub summary:         Option<String>,
    pub payload:         String,
    /// Whether this event came from the entity's own stream or from a descendant chain.
    pub source_mode:     SubjectChainSourceMode,
}

/// Paginated subject chain response.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct InspectorSubjectChainPage {
    pub events:            Vec<InspectorSubjectChainEvent>,
    pub next_cursor:       Option<i64>,
    pub depth_cap_reached: bool,
}

/// One entity in the aggregate keys listing.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct InspectorAggregateKeyEntry {
    pub aggregate_id:  String,
    pub display_label: Option<String>,
}

/// Paginated entity listing response.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct InspectorAggregateKeysPage {
    pub entries:     Vec<InspectorAggregateKeyEntry>,
    pub next_cursor: Option<String>,
}
