mod feature;
pub mod features;
pub mod oci;
mod rust;
use std::sync::Arc;

pub use feature::*;
use log::info;
pub use oci::*;
pub use rust::*;

use async_trait::async_trait;
use serde::Serialize;

use crate::{
    data::{Id, Version},
    interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
    inventory::Inventory,
    topology::Topology,
};

pub trait Application: std::fmt::Debug + Send + Sync {
    fn name(&self) -> String;
}

#[derive(Debug)]
pub struct ApplicationInterpret<A: Application, T: Topology + std::fmt::Debug> {
    features: Vec<Box<dyn ApplicationFeature<T>>>,
    application: Arc<A>,
}

#[async_trait]
impl<A: Application, T: Topology + std::fmt::Debug> Interpret<T> for ApplicationInterpret<A, T> {
    async fn execute(
        &self,
        _inventory: &Inventory,
        topology: &T,
    ) -> Result<Outcome, InterpretError> {
        let app_name = self.application.name();
        info!(
            "Preparing {} features [{}] for application {app_name}",
            self.features.len(),
            self.features
                .iter()
                .map(|f| f.name())
                .collect::<Vec<String>>()
                .join(", ")
        );
        for feature in self.features.iter() {
            info!(
                "Installing feature {} for application {app_name}",
                feature.name()
            );
            let _ = match feature.ensure_installed(topology).await {
                Ok(()) => (),
                Err(msg) => {
                    return Err(InterpretError::new(format!(
                        "Application Interpret failed to install feature : {msg}"
                    )));
                }
            };
        }
        Ok(Outcome::success("successfully created app".to_string()))
    }

    fn get_name(&self) -> InterpretName {
        InterpretName::Application
    }

    fn get_version(&self) -> Version {
        Version::from("1.0.0").unwrap()
    }

    fn get_status(&self) -> InterpretStatus {
        todo!()
    }

    fn get_children(&self) -> Vec<Id> {
        todo!()
    }
}

impl Serialize for dyn Application {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        todo!()
    }
}
