//! Server configuration

use serde::{Deserialize, Serialize};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};

/// TLS configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TlsConfig {
    /// Path to certificate file (PEM)
    pub cert_file: String,

    /// Path to private key file (PEM)
    pub key_file: String,

    /// Path to CA certificate for client verification (optional, for mTLS)
    pub ca_file: Option<String>,

    /// Require client certificates (mTLS)
    #[serde(default)]
    pub require_client_cert: bool,
}

/// Metadata storage backend
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum MetadataBackend {
    /// FlareDB distributed metadata database
    FlareDb,
    /// PostgreSQL metadata database
    Postgres,
    /// SQLite metadata database (single-node only)
    Sqlite,
}

impl Default for MetadataBackend {
    fn default() -> Self {
        Self::FlareDb
    }
}

/// Server configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
    /// gRPC management API address
    pub grpc_addr: SocketAddr,

    /// ChainFire endpoint used for cluster coordination only
    pub chainfire_endpoint: Option<String>,

    /// FlareDB endpoint used for metadata and tenant data storage
    pub flaredb_endpoint: Option<String>,

    /// Metadata backend selection (flaredb, postgres, sqlite)
    #[serde(default)]
    pub metadata_backend: MetadataBackend,

    /// SQL database URL for metadata when backend is postgres or sqlite
    pub metadata_database_url: Option<String>,

    /// Allow single-node mode (required for SQLite)
    #[serde(default)]
    pub single_node: bool,

    /// Log level
    pub log_level: String,

    /// TLS configuration (optional)
    pub tls: Option<TlsConfig>,

    /// Authentication configuration
    #[serde(default)]
    pub auth: AuthConfig,

    /// Backend health checker configuration
    #[serde(default)]
    pub health: HealthRuntimeConfig,

    /// VIP advertisement reconciliation configuration
    #[serde(default)]
    pub vip_advertisement: VipAdvertisementConfig,

    /// Local VIP ownership configuration.
    #[serde(default)]
    pub vip_ownership: VipOwnershipConfig,

    /// Native BGP speaker configuration
    #[serde(default)]
    pub bgp: BgpConfig,
}

/// Authentication configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthConfig {
    /// IAM server endpoint
    #[serde(default = "default_iam_server_addr")]
    pub iam_server_addr: String,
}

fn default_iam_server_addr() -> String {
    "127.0.0.1:50051".to_string()
}

/// Backend health checker runtime configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthRuntimeConfig {
    /// Interval between backend health check sweeps.
    #[serde(default = "default_health_check_interval_secs")]
    pub interval_secs: u64,

    /// Timeout for individual backend checks.
    #[serde(default = "default_health_check_timeout_secs")]
    pub timeout_secs: u64,
}

fn default_health_check_interval_secs() -> u64 {
    5
}

fn default_health_check_timeout_secs() -> u64 {
    5
}

impl Default for HealthRuntimeConfig {
    fn default() -> Self {
        Self {
            interval_secs: default_health_check_interval_secs(),
            timeout_secs: default_health_check_timeout_secs(),
        }
    }
}

/// VIP advertisement reconciliation runtime configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VipAdvertisementConfig {
    /// Interval between BGP advertisement reconciliation sweeps.
    #[serde(default = "default_vip_check_interval_secs")]
    pub interval_secs: u64,

    /// Presence of this file puts the node into control-plane drain mode.
    #[serde(default = "default_vip_drain_file")]
    pub drain_file: String,

    /// Time to keep a locally owned VIP after withdrawing it for drain.
    #[serde(default = "default_vip_drain_hold_time_secs")]
    pub drain_hold_time_secs: u64,
}

fn default_vip_check_interval_secs() -> u64 {
    3
}

fn default_vip_drain_file() -> String {
    "/var/lib/fiberlb/drain".to_string()
}

fn default_vip_drain_hold_time_secs() -> u64 {
    5
}

impl Default for VipAdvertisementConfig {
    fn default() -> Self {
        Self {
            interval_secs: default_vip_check_interval_secs(),
            drain_file: default_vip_drain_file(),
            drain_hold_time_secs: default_vip_drain_hold_time_secs(),
        }
    }
}

/// Local VIP ownership runtime configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VipOwnershipConfig {
    /// Whether FiberLB should claim VIP /32 addresses on the local node.
    #[serde(default)]
    pub enabled: bool,

    /// Interface used for local VIP ownership.
    #[serde(default = "default_vip_ownership_interface")]
    pub interface: String,

    /// Optional explicit `ip` command path used for local VIP ownership.
    #[serde(default)]
    pub ip_command: Option<String>,
}

fn default_vip_ownership_interface() -> String {
    "lo".to_string()
}

impl Default for VipOwnershipConfig {
    fn default() -> Self {
        Self {
            enabled: false,
            interface: default_vip_ownership_interface(),
            ip_command: None,
        }
    }
}

/// Static BGP peer configuration.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct BgpPeerConfig {
    /// Peer IP address or hostname.
    pub address: String,

    /// Peer TCP port.
    #[serde(default = "default_bgp_peer_port")]
    pub port: u16,

    /// Peer AS number.
    pub asn: u32,

    /// Optional operator-visible description.
    #[serde(default)]
    pub description: String,

    /// Optional export policy applied to announcements sent to this peer.
    #[serde(default)]
    pub export_policy: BgpExportPolicyConfig,

    /// Optional single-hop BFD session parameters for this peer.
    #[serde(default)]
    pub bfd: BfdConfig,
}

fn default_bgp_peer_port() -> u16 {
    179
}

/// Peer-scoped BGP export policy.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
pub struct BgpExportPolicyConfig {
    /// Optional MED attached to announced VIP routes.
    #[serde(default)]
    pub med: Option<u32>,

    /// Optional standard communities attached to announced VIP routes.
    #[serde(default)]
    pub communities: Vec<String>,
}

/// Single-hop BFD configuration for a BGP peer.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct BfdConfig {
    /// Whether BFD should gate route advertisement for this peer.
    #[serde(default)]
    pub enabled: bool,

    /// Desired transmit interval in milliseconds.
    #[serde(default = "default_bfd_desired_min_tx_millis")]
    pub desired_min_tx_millis: u64,

    /// Required receive interval in milliseconds.
    #[serde(default = "default_bfd_required_min_rx_millis")]
    pub required_min_rx_millis: u64,

    /// Detection multiplier.
    #[serde(default = "default_bfd_detect_multiplier")]
    pub detect_multiplier: u8,

    /// Maximum time to wait for the session to reach Up after BGP establishment.
    #[serde(default = "default_bfd_bootstrap_timeout_secs")]
    pub bootstrap_timeout_secs: u64,
}

fn default_bfd_desired_min_tx_millis() -> u64 {
    300
}

fn default_bfd_required_min_rx_millis() -> u64 {
    300
}

fn default_bfd_detect_multiplier() -> u8 {
    3
}

fn default_bfd_bootstrap_timeout_secs() -> u64 {
    10
}

impl Default for BfdConfig {
    fn default() -> Self {
        Self {
            enabled: false,
            desired_min_tx_millis: default_bfd_desired_min_tx_millis(),
            required_min_rx_millis: default_bfd_required_min_rx_millis(),
            detect_multiplier: default_bfd_detect_multiplier(),
            bootstrap_timeout_secs: default_bfd_bootstrap_timeout_secs(),
        }
    }
}

/// Native BGP speaker configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BgpConfig {
    /// Whether FiberLB should originate VIP routes itself.
    #[serde(default)]
    pub enabled: bool,

    /// Local AS number.
    #[serde(default = "default_bgp_local_as")]
    pub local_as: u32,

    /// BGP router ID. Must be IPv4.
    #[serde(default = "default_bgp_router_id")]
    pub router_id: String,

    /// Optional explicit next-hop address. Falls back to router_id.
    #[serde(default)]
    pub next_hop: Option<String>,

    /// Requested hold time in seconds.
    #[serde(default = "default_bgp_hold_time_secs")]
    pub hold_time_secs: u16,

    /// Keepalive interval in seconds.
    #[serde(default = "default_bgp_keepalive_secs")]
    pub keepalive_secs: u16,

    /// Delay before reconnecting to a failed peer.
    #[serde(default = "default_bgp_connect_retry_secs")]
    pub connect_retry_secs: u64,

    /// Static peers for outbound eBGP sessions.
    #[serde(default)]
    pub peers: Vec<BgpPeerConfig>,
}

fn default_bgp_local_as() -> u32 {
    65001
}

fn default_bgp_router_id() -> String {
    Ipv4Addr::new(127, 0, 0, 1).to_string()
}

fn default_bgp_hold_time_secs() -> u16 {
    90
}

fn default_bgp_keepalive_secs() -> u16 {
    30
}

fn default_bgp_connect_retry_secs() -> u64 {
    5
}

impl BgpConfig {
    /// Effective next hop advertised in UPDATE messages.
    pub fn next_hop_addr(&self) -> std::result::Result<IpAddr, std::net::AddrParseError> {
        self.next_hop.as_deref().unwrap_or(&self.router_id).parse()
    }

    /// Parsed router ID as IPv4.
    pub fn router_id_addr(&self) -> std::result::Result<Ipv4Addr, std::net::AddrParseError> {
        self.router_id.parse()
    }
}

impl Default for BgpConfig {
    fn default() -> Self {
        Self {
            enabled: false,
            local_as: default_bgp_local_as(),
            router_id: default_bgp_router_id(),
            next_hop: None,
            hold_time_secs: default_bgp_hold_time_secs(),
            keepalive_secs: default_bgp_keepalive_secs(),
            connect_retry_secs: default_bgp_connect_retry_secs(),
            peers: Vec::new(),
        }
    }
}

impl Default for AuthConfig {
    fn default() -> Self {
        Self {
            iam_server_addr: default_iam_server_addr(),
        }
    }
}

impl Default for ServerConfig {
    fn default() -> Self {
        Self {
            grpc_addr: "0.0.0.0:9080".parse().unwrap(),
            chainfire_endpoint: None,
            flaredb_endpoint: None,
            metadata_backend: MetadataBackend::FlareDb,
            metadata_database_url: None,
            single_node: false,
            log_level: "info".to_string(),
            tls: None,
            auth: AuthConfig::default(),
            health: HealthRuntimeConfig::default(),
            vip_advertisement: VipAdvertisementConfig::default(),
            vip_ownership: VipOwnershipConfig::default(),
            bgp: BgpConfig::default(),
        }
    }
}
