use crate::{state::*, wsclient::WsClient};
use crate::opamp::{ Client, OpAMPClientError };
use std::time::SystemTime;
use ulid::Generator;
use rand::RngCore;

pub struct ConnectionSettings {
    pub server_endpoint: String,
    pub listen_path: String,
    pub name: String,
    pub version: String,
    pub instance_id: String,
    pub debugmode: log::LevelFilter,
}

impl ConnectionSettings {
    fn create_instance_id() -> ulid::Ulid {
        // Generate a new instance id
        let dt = SystemTime::now();
        let mut entropy = [0; 10];
        let mut rng = rand::thread_rng();
        rng.fill_bytes(&mut entropy[6..]);
        let mut gen = Generator::new();
        let instance_id = gen
            .generate_from_datetime_with_source(dt, &mut rng)
            .unwrap();
        instance_id
    }
}

impl Default for ConnectionSettings {
    fn default() -> ConnectionSettings {
        ConnectionSettings {
            server_endpoint: "ws://127.0.0.1:4320".to_string(),
            listen_path: "/v1/opamp".to_string(),
            name: "OpAmpClient".to_string(),
            version: std::env::var("CARGO_PKG_VERSION").unwrap_or("0.0.1".to_string()),
            instance_id: ConnectionSettings::create_instance_id().to_string(),
            debugmode: log::LevelFilter::Info,
        }
    }
}

pub struct Api {
    pub client: Box<dyn Client>,
    state: Box<dyn State>,
}

impl Api {
    pub fn new(settings: ConnectionSettings) -> Api {
        // TODO: Choose between Websocket and HTTP based
        // on the settings endpoint prefix (http, ws, etc)

        Api {
            client: Box::new(WsClient::new(settings)),
            state: Box::new(Disconnected),
        }
    }

    pub async fn connect(&mut self) -> Result<(), OpAMPClientError> {
        self.client.connect().await
    }

    pub fn probe(mut api: Api) -> Api {
        api.state = api.state.evaluate(api.client.as_mut());
        api
    }
}