use std::sync::{Arc, RwLock};

use log::info;

use super::{
    interpret::{InterpretError, Outcome},
    inventory::Inventory,
    score::Score,
    topology::Topology,
};

type ScoreVec<T> = Vec<Box<dyn Score<T>>>;

pub struct Maestro<T: Topology> {
    inventory: Inventory,
    topology: T,
    scores: Arc<RwLock<ScoreVec<T>>>,
}

impl<T: Topology> Maestro<T> {
    pub fn new(inventory: Inventory, topology: T) -> Self {
        Self {
            inventory,
            topology,
            scores: Arc::new(RwLock::new(Vec::new())),
        }
    }

    // Load the inventory and inventory from environment.
    // This function is able to discover the context that it is running in, such as k8s clusters, aws cloud, linux host, etc.
    // When the HARMONY_TOPOLOGY environment variable is not set, it will default to install k3s
    // locally (lazily, if not installed yet, when the first execution occurs) and use that as a topology
    // So, by default, the inventory is a single host that the binary is running on, and the
    // topology is a single node k3s
    //
    // By default :
    // - Linux => k3s
    // - macos, windows => docker compose
    //
    // To run more complex cases like OKDHACluster, either provide the default target in the
    // harmony infrastructure as code or as an environment variable
    pub fn load_from_env() -> Self {
        // Load env var HARMONY_TOPOLOGY
        match std::env::var("HARMONY_TOPOLOGY") {
            Ok(_) => todo!(),
            Err(_) => todo!(),
        }
    }

    pub fn start(&mut self) {
        info!("Starting Maestro");
    }

    pub fn register_all(&mut self, mut scores: ScoreVec<T>) {
        let mut score_mut = self.scores.write().expect("Should acquire lock");
        score_mut.append(&mut scores);
    }

    pub async fn interpret(&self, score: Box<dyn Score<T>>) -> Result<Outcome, InterpretError> {
        info!("Running score {score:?}");
        let interpret = score.create_interpret();
        info!("Launching interpret {interpret:?}");
        let result = interpret.execute(&self.inventory, &self.topology).await;
        info!("Got result {result:?}");
        result
    }

    pub fn scores(&self) -> Arc<RwLock<ScoreVec<T>>> {
        self.scores.clone()
    }
}
