//! Per-backend configuration value types used by `ArchiveRegistration`.

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum FailureMode {
    Required,
    BestEffort,
}

impl Default for FailureMode {
    fn default() -> Self {
        FailureMode::Required
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum WritePolicy {
    Inline,
    Queued {
        batch_window_ms: u64,
        capacity: usize,
        overflow: OverflowPolicy,
    },
}

impl Default for WritePolicy {
    fn default() -> Self {
        WritePolicy::Inline
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum OverflowPolicy {
    Block,
    DropOldest,
    Error,
}

impl Default for OverflowPolicy {
    fn default() -> Self {
        OverflowPolicy::Block
    }
}

use std::sync::Arc;

use chrono::{DateTime, Utc};
use tokio::sync::RwLock;

use crate::backend::{ArchiveBackend, CapabilitySet, HealthStatus};
use crate::registry::filter::ArchiveFilter;

use super::writer::WriterHandle;

pub struct ArchiveRegistration {
    pub name: String,
    pub type_name: &'static str,
    pub backend: Arc<dyn ArchiveBackend>,
    pub write_active: bool,
    pub failure_mode: FailureMode,
    pub read_priority: u32,
    pub enabled: bool,
    pub write_policy: WritePolicy,
    /// Per-archive include/exclude filter consulted during non-primary
    /// write fanout. Default (unrestricted) accepts every session; the
    /// primary target always writes regardless of its filter.
    pub filter: ArchiveFilter,
    pub last_health: Arc<RwLock<HealthStatus>>,
    pub last_error: Arc<RwLock<Option<(DateTime<Utc>, String)>>>,
    pub consecutive_failures: Arc<RwLock<u32>>,
    pub writer: Option<WriterHandle>,
}

impl ArchiveRegistration {
    /// Convenience constructor: builds new `Arc<RwLock<_>>` instances for the
    /// drift trio. Use this for single-process, single-owner registrations
    /// (tests and the simple single-archive constructors).
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        name: String,
        type_name: &'static str,
        backend: Arc<dyn ArchiveBackend>,
        write_active: bool,
        failure_mode: FailureMode,
        read_priority: u32,
        enabled: bool,
        write_policy: WritePolicy,
        writer: Option<WriterHandle>,
        initial_health: HealthStatus,
    ) -> Self {
        Self::new_with_shared_state(
            name,
            type_name,
            backend,
            write_active,
            failure_mode,
            read_priority,
            enabled,
            write_policy,
            writer,
            Arc::new(RwLock::new(initial_health)),
            Arc::new(RwLock::new(None)),
            Arc::new(RwLock::new(0)),
        )
    }

    /// Constructor used by `from_config` so the writer task and the
    /// registration share the same drift state (both mutate it).
    #[allow(clippy::too_many_arguments)]
    pub fn new_with_shared_state(
        name: String,
        type_name: &'static str,
        backend: Arc<dyn ArchiveBackend>,
        write_active: bool,
        failure_mode: FailureMode,
        read_priority: u32,
        enabled: bool,
        write_policy: WritePolicy,
        writer: Option<WriterHandle>,
        last_health: Arc<RwLock<HealthStatus>>,
        last_error: Arc<RwLock<Option<(DateTime<Utc>, String)>>>,
        consecutive_failures: Arc<RwLock<u32>>,
    ) -> Self {
        Self {
            name,
            type_name,
            backend,
            write_active,
            failure_mode,
            read_priority,
            enabled,
            write_policy,
            filter: ArchiveFilter::default(),
            last_health,
            last_error,
            consecutive_failures,
            writer,
        }
    }

    /// Override the registration's filter. Intended for boot-time wiring
    /// (`from_config`) and tests; the field itself is public for other
    /// direct consumers.
    pub fn with_filter(mut self, filter: ArchiveFilter) -> Self {
        self.filter = filter;
        self
    }

    pub fn capabilities(&self) -> &CapabilitySet {
        self.backend.capabilities()
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ArchiveStatus {
    pub name: String,
    pub type_name: String,
    pub enabled: bool,
    pub write_active: bool,
    pub failure_mode: FailureMode,
    pub read_priority: u32,
    pub capabilities: CapabilitySet,
    pub health: HealthStatus,
    pub last_error: Option<(DateTime<Utc>, String)>,
    pub queue_depth: Option<usize>,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn defaults_are_safe() {
        assert_eq!(FailureMode::default(), FailureMode::Required);
        assert_eq!(WritePolicy::default(), WritePolicy::Inline);
        assert_eq!(OverflowPolicy::default(), OverflowPolicy::Block);
    }
}
