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

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

/// Score that always errors. This is only useful for development/testing purposes. It does nothing
/// except returning Err(InterpretError) when interpreted.
#[derive(Debug, Clone, Serialize)]
pub struct ErrorScore;

impl<T: Topology> Score<T> for ErrorScore {
    fn create_interpret(&self) -> Box<dyn crate::interpret::Interpret<T>> {
        Box::new(DummyInterpret {
            result: Err(InterpretError::new("Error Score default error".to_string())),
            status: InterpretStatus::QUEUED,
        })
    }

    fn name(&self) -> String {
        "ErrorScore".to_string()
    }
}

/// Score that always succeeds. This is only useful for development/testing purposes. It does nothing
/// except returning Ok(Outcome::success) when interpreted.
#[derive(Debug, Clone, Serialize)]
pub struct SuccessScore;

impl<T: Topology> Score<T> for SuccessScore {
    fn create_interpret(&self) -> Box<dyn Interpret<T>> {
        Box::new(DummyInterpret {
            result: Ok(Outcome::success("SuccessScore default success".to_string())),
            status: InterpretStatus::QUEUED,
        })
    }

    fn name(&self) -> String {
        "SuccessScore".to_string()
    }
}

/// An interpret that only returns the result it is given when built. It does nothing else. Only
/// useful for development/testing purposes.
#[derive(Debug)]
struct DummyInterpret {
    status: InterpretStatus,
    result: Result<Outcome, InterpretError>,
}

#[async_trait]
impl<T: Topology> Interpret<T> for DummyInterpret {
    fn get_name(&self) -> InterpretName {
        InterpretName::Dummy
    }

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

    fn get_status(&self) -> InterpretStatus {
        self.status.clone()
    }

    fn get_children(&self) -> Vec<crate::domain::data::Id> {
        todo!()
    }

    async fn execute(
        &self,
        _inventory: &Inventory,
        _topology: &T,
    ) -> Result<Outcome, InterpretError> {
        self.result.clone()
    }
}

/// Score that always panics. This is only useful for development/testing purposes. It does nothing
/// except panic! with an error message when interpreted
#[derive(Debug, Clone, Serialize)]
pub struct PanicScore;

impl<T: Topology> Score<T> for PanicScore {
    fn create_interpret(&self) -> Box<dyn Interpret<T>> {
        Box::new(PanicInterpret {})
    }

    fn name(&self) -> String {
        "PanicScore".to_string()
    }
}

/// An interpret that always panics when executed. Useful for development/testing purposes.
#[derive(Debug)]
struct PanicInterpret;

#[async_trait]
impl<T: Topology> Interpret<T> for PanicInterpret {
    fn get_name(&self) -> InterpretName {
        InterpretName::Panic
    }

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

    fn get_status(&self) -> InterpretStatus {
        InterpretStatus::QUEUED
    }

    fn get_children(&self) -> Vec<crate::domain::data::Id> {
        todo!()
    }

    async fn execute(
        &self,
        _inventory: &Inventory,
        _topology: &T,
    ) -> Result<Outcome, InterpretError> {
        panic!("Panic interpret always panics when executed")
    }
}
