use std::error::Error;

use async_trait::async_trait;
use derive_new::new;

use super::{
    data::{Id, Version},
    executors::ExecutorError,
    inventory::Inventory,
    topology::Topology,
};

pub enum InterpretName {
    OPNSenseDHCP,
    OPNSenseDns,
    LoadBalancer,
    Tftp,
    Http,
    Dummy,
    Panic,
    OPNSense,
}

impl std::fmt::Display for InterpretName {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            InterpretName::OPNSenseDHCP => f.write_str("OPNSenseDHCP"),
            InterpretName::OPNSenseDns => f.write_str("OPNSenseDns"),
            InterpretName::LoadBalancer => f.write_str("LoadBalancer"),
            InterpretName::Tftp => f.write_str("Tftp"),
            InterpretName::Http => f.write_str("Http"),
            InterpretName::Dummy => f.write_str("Dummy"),
            InterpretName::Panic => f.write_str("Panic"),
            InterpretName::OPNSense => f.write_str("OPNSense"),
        }
    }
}

#[async_trait]
pub trait Interpret<T>: std::fmt::Debug + Send {
    async fn execute(&self, inventory: &Inventory, topology: &T)
    -> Result<Outcome, InterpretError>;
    fn get_name(&self) -> InterpretName;
    fn get_version(&self) -> Version;
    fn get_status(&self) -> InterpretStatus;
    fn get_children(&self) -> Vec<Id>;
}

#[derive(Debug, new, Clone)]
pub struct Outcome {
    pub status: InterpretStatus,
    pub message: String,
}

impl Outcome {
    pub fn noop() -> Self {
        Self {
            status: InterpretStatus::NOOP,
            message: String::new(),
        }
    }

    pub fn success(message: String) -> Self {
        Self {
            status: InterpretStatus::SUCCESS,
            message,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum InterpretStatus {
    SUCCESS,
    FAILURE,
    RUNNING,
    QUEUED,
    BLOCKED,
    NOOP,
}

impl std::fmt::Display for InterpretStatus {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let msg = match self {
            InterpretStatus::SUCCESS => "SUCCESS",
            InterpretStatus::FAILURE => "FAILURE",
            InterpretStatus::RUNNING => "RUNNING",
            InterpretStatus::QUEUED => "QUEUED",
            InterpretStatus::BLOCKED => "BLOCKED",
            InterpretStatus::NOOP => "NO_OP",
        };
        f.write_str(msg)
    }
}

#[derive(Debug, Clone, new)]
pub struct InterpretError {
    msg: String,
}

impl std::fmt::Display for InterpretError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&self.msg)
    }
}
impl Error for InterpretError {}

impl From<ExecutorError> for InterpretError {
    fn from(value: ExecutorError) -> Self {
        Self {
            msg: format!("InterpretError : {value}"),
        }
    }
}

impl From<kube::Error> for InterpretError {
    fn from(value: kube::Error) -> Self {
        Self {
            msg: format!("InterpretError : {value}"),
        }
    }
}
