use crate::router::Router;
use crate::server::{UpstreamConfig, VarDiffConfig};
use crate::types::ReadyIndicator;
use crate::BanManager;
use crate::MinerList;
use crate::StratumServer;
use async_std::sync::{Arc, Mutex};
use stop_token::StopSource;

#[derive(Default)]
pub struct StratumServerBuilder<State, CState> {
    pub host: String,
    pub port: u16,
    pub exported_port: Option<u16>,
    pub max_connections: Option<usize>,
    pub proxy: bool,
    pub var_diff_config: VarDiffConfig,
    pub upstream_config: UpstreamConfig,
    pub initial_difficulty: f64,
    pub state: State,
    pub connection_state: CState,
    pub ready_indicator: ReadyIndicator,
}

//@todo we need API host + port here as well (if enabled.)
impl<State: Clone + Send + Sync + 'static, CState: Clone + Send + Sync + 'static>
    StratumServerBuilder<State, CState>
{
    pub fn new(state: State, connection_state: CState) -> Self {
        Self {
            host: String::from(""),
            port: 0,
            exported_port: None,
            max_connections: None,
            proxy: false,
            initial_difficulty: 0.0,
            state,
            connection_state,
            ready_indicator: ReadyIndicator::new(true),
            var_diff_config: VarDiffConfig {
                var_diff: false,
                minimum_difficulty: 1.0,
                maximum_difficulty: 100_000.0,
                retarget_time: 90,
                target_time: 15.0,
                variance_percent: 30.0,
            },
            upstream_config: UpstreamConfig {
                enabled: false,
                url: String::from(""),
            },
        }
    }

    pub fn with_host(mut self, host: &str) -> Self {
        self.host = host.to_owned();
        self
    }

    pub fn with_port(mut self, port: u16) -> Self {
        self.port = port;
        self
    }

    pub fn with_max_connections(mut self, max_connections: usize) -> Self {
        self.max_connections = Some(max_connections);
        self
    }

    pub fn with_proxy(mut self, value: bool) -> Self {
        self.proxy = value;
        self
    }

    pub fn with_var_diff(mut self, value: bool) -> Self {
        self.var_diff_config.var_diff = value;
        self
    }

    pub fn with_minimum_difficulty(mut self, difficulty: f64) -> Self {
        self.var_diff_config.minimum_difficulty = difficulty;
        self
    }

    pub fn with_maximum_difficulty(mut self, difficulty: f64) -> Self {
        self.var_diff_config.maximum_difficulty = difficulty;
        self
    }

    pub fn with_retarget_time(mut self, time: i64) -> Self {
        self.var_diff_config.retarget_time = time;
        self
    }

    pub fn with_target_time(mut self, time: f64) -> Self {
        self.var_diff_config.target_time = time;
        self
    }

    pub fn with_variance_percent(mut self, percent: f64) -> Self {
        self.var_diff_config.variance_percent = percent;
        self
    }

    pub fn with_initial_difficulty(mut self, difficulty: f64) -> Self {
        self.initial_difficulty = difficulty;
        self
    }

    pub fn with_expected_port(mut self, port: u16) -> Self {
        self.exported_port = Some(port);
        self
    }

    pub fn with_ready_indicator(mut self, ready_indicator: ReadyIndicator) -> Self {
        self.ready_indicator = ready_indicator;
        self
    }

    pub fn build(self) -> StratumServer<State, CState> {
        let connection_list = Arc::new(MinerList::new(self.max_connections));

        let expected_port = match self.exported_port {
            Some(exported_port) => exported_port,
            None => self.port,
        };

        let stop_source = StopSource::new();
        let stop_token = stop_source.stop_token();

        StratumServer {
            host: self.host,
            port: self.port,
            expected_port,
            proxy: self.proxy,
            initial_difficulty: self.initial_difficulty,
            connection_list,
            state: self.state,
            ban_manager: Arc::new(BanManager::new()),
            router: Arc::new(Router::new()),
            upstream_router: Arc::new(Router::new()),
            var_diff_config: self.var_diff_config,
            upstream_config: self.upstream_config,
            initial_connection_state: self.connection_state,
            stop_source: Arc::new(Mutex::new(Some(stop_source))),
            stop_token,
            global_thread_list: Vec::new(),
            ready_indicator: self.ready_indicator,
        }
    }
}
