use serde::{Deserialize, Serialize};
use std::str::FromStr;

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "snake_case")]
pub enum QueueEventStatus {
    New,
    Processing,
    Done,
    DeadLetter,
}

impl QueueEventStatus {
    pub const fn as_str(self) -> &'static str {
        match self {
            Self::New => "new",
            Self::Processing => "processing",
            Self::Done => "done",
            Self::DeadLetter => "dead_letter",
        }
    }
}

impl std::fmt::Display for QueueEventStatus {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(self.as_str())
    }
}

impl FromStr for QueueEventStatus {
    type Err = String;

    fn from_str(raw: &str) -> Result<Self, Self::Err> {
        match raw {
            "new" => Ok(Self::New),
            "processing" => Ok(Self::Processing),
            "done" => Ok(Self::Done),
            "dead_letter" => Ok(Self::DeadLetter),
            other => Err(format!("unknown queue event status: {other}")),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct QueueLaneStatus {
    pub new_total: u64,
    pub new_due: u64,
    pub processing: u64,
    pub processed: u64,
    pub error_count: u64,
    pub errors: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct QueueStatus {
    pub semantic: QueueLaneStatus,
    pub embedding: QueueLaneStatus,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct QueueCounts {
    pub new_total: u64,
    pub new_due: u64,
    pub processing: u64,
    pub done: u64,
    pub dead_letter: u64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub earliest_next_attempt_at: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueueCheckpoint {
    pub worker_name: String,
    pub last_event_id: i64,
    pub updated_at: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct QueueDiagnostics {
    pub counts: QueueCounts,
    pub checkpoints: Vec<QueueCheckpoint>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub queue_dead_letter_rate: Vec<QueueDeadLetterRate>,
    #[serde(default)]
    pub om_status: OmQueueStatus,
    #[serde(default)]
    pub om_reflection_apply_metrics: OmReflectionApplyMetrics,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct QueueDeadLetterRate {
    pub event_type: String,
    pub total: u64,
    pub dead_letter: u64,
    pub dead_letter_rate: f64,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct OmQueueStatus {
    pub records_total: u64,
    pub observing_count: u64,
    pub reflecting_count: u64,
    pub buffering_observation_count: u64,
    pub buffering_reflection_count: u64,
    pub observation_tokens_active: u64,
    pub pending_message_tokens: u64,
    pub observer_trigger_count_total: u64,
    pub reflector_trigger_count_total: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct OmReflectionApplyMetrics {
    pub attempts_total: u64,
    pub applied_total: u64,
    pub stale_generation_total: u64,
    pub idempotent_total: u64,
    pub stale_generation_ratio: f64,
    pub avg_latency_ms: f64,
    pub max_latency_ms: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct QueueOverview {
    pub counts: QueueCounts,
    pub checkpoints: Vec<QueueCheckpoint>,
    pub lanes: QueueStatus,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub queue_dead_letter_rate: Vec<QueueDeadLetterRate>,
    #[serde(default)]
    pub om_status: OmQueueStatus,
    #[serde(default)]
    pub om_reflection_apply_metrics: OmReflectionApplyMetrics,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OutboxEvent {
    pub id: i64,
    pub event_type: String,
    pub uri: String,
    pub payload_json: serde_json::Value,
    pub status: QueueEventStatus,
    pub attempt_count: u32,
    pub next_attempt_at: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ReplayReport {
    pub fetched: usize,
    pub processed: usize,
    pub done: usize,
    pub dead_letter: usize,
    pub requeued: usize,
    pub skipped: usize,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct OmV2MigrationReport {
    pub dry_run: bool,
    pub already_applied: bool,
    pub records_scanned: usize,
    pub entries_planned: usize,
    pub continuation_planned: usize,
    pub entries_upserted: usize,
    pub continuation_upserted: usize,
    pub protocol_meta_updated: bool,
    pub integrity_ok: bool,
    pub protocol_version: String,
    pub episodic_rev: String,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub issues: Vec<String>,
}
