use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::fs;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
    /// Bind address for client-facing sockets
    #[serde(default = "default_bind_address")]
    pub bind_address: String,

    /// Client request port (ROUTER - receives client requests)
    #[serde(default = "default_client_request_port")]
    pub client_request_port: u16,

    /// Market data publication port (XPUB - clients subscribe here)
    #[serde(default = "default_market_data_pub_port")]
    pub market_data_pub_port: u16,

    /// Ingestor work queue port (PUB - publish work with exchange prefix)
    #[serde(default = "default_ingestor_work_port")]
    pub ingestor_work_port: u16,

    /// Ingestor response port (ROUTER - receives responses from ingestors)
    #[serde(default = "default_ingestor_response_port")]
    pub ingestor_response_port: u16,

    /// Flink market data endpoint (XSUB - relay subscribes to Flink)
    #[serde(default = "default_flink_market_data_endpoint")]
    pub flink_market_data_endpoint: String,

    /// Request timeout in seconds
    #[serde(default = "default_request_timeout_secs")]
    pub request_timeout_secs: u64,

    /// High water mark for sockets
    #[serde(default = "default_hwm")]
    pub high_water_mark: i32,
}

fn default_bind_address() -> String {
    "tcp://*".to_string()
}

fn default_client_request_port() -> u16 {
    5559
}

fn default_market_data_pub_port() -> u16 {
    5558
}

fn default_ingestor_work_port() -> u16 {
    5555
}

fn default_ingestor_response_port() -> u16 {
    5556
}

fn default_flink_market_data_endpoint() -> String {
    "tcp://flink-jobmanager:5557".to_string()
}

fn default_request_timeout_secs() -> u64 {
    30
}

fn default_hwm() -> i32 {
    10000
}

impl Default for Config {
    fn default() -> Self {
        Self {
            bind_address: default_bind_address(),
            client_request_port: default_client_request_port(),
            market_data_pub_port: default_market_data_pub_port(),
            ingestor_work_port: default_ingestor_work_port(),
            ingestor_response_port: default_ingestor_response_port(),
            flink_market_data_endpoint: default_flink_market_data_endpoint(),
            request_timeout_secs: default_request_timeout_secs(),
            high_water_mark: default_hwm(),
        }
    }
}

impl Config {
    pub fn from_file(path: &str) -> Result<Self> {
        let contents = fs::read_to_string(path)?;
        let config: Config = serde_yaml::from_str(&contents)?;
        Ok(config)
    }

    pub fn from_env() -> Result<Self> {
        let config_path = std::env::var("CONFIG_PATH")
            .unwrap_or_else(|_| "/config/config.yaml".to_string());

        if std::path::Path::new(&config_path).exists() {
            Self::from_file(&config_path)
        } else {
            Ok(Self::default())
        }
    }
}
