use async_trait::async_trait;
use derive_new::new;
use log::info;
use serde::Serialize;

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

#[derive(Debug, new, Clone, Serialize)]
pub struct DnsScore {
    dns_entries: Vec<DnsRecord>,
    register_dhcp_leases: Option<bool>,
}

impl<T: Topology + DnsServer> Score<T> for DnsScore {
    fn create_interpret(&self) -> Box<dyn Interpret<T>> {
        Box::new(DnsInterpret::new(self.clone()))
    }

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

// https://docs.opnsense.org/manual/dhcp.html#advanced-settings
#[derive(Debug, Clone)]
pub struct DnsInterpret {
    score: DnsScore,
    version: Version,
    status: InterpretStatus,
}

impl DnsInterpret {
    pub fn new(score: DnsScore) -> Self {
        let version = Version::from("1.0.0").expect("Version should be valid");

        Self {
            version,
            score,
            status: InterpretStatus::QUEUED,
        }
    }
    async fn serve_dhcp_entries<T: DnsServer>(
        &self,
        _inventory: &Inventory,
        dns: &T,
    ) -> Result<Outcome, InterpretError> {
        if let Some(register) = self.score.register_dhcp_leases {
            dns.register_dhcp_leases(register).await?;
        }

        Ok(Outcome::new(
            InterpretStatus::SUCCESS,
            "DNS Interpret execution successfull".to_string(),
        ))
    }

    async fn ensure_hosts_registered<D: DnsServer>(
        &self,
        dns_server: &D,
    ) -> Result<Outcome, InterpretError> {
        let entries = &self.score.dns_entries;
        dns_server.ensure_hosts_registered(entries.clone()).await?;

        Ok(Outcome::new(
            InterpretStatus::SUCCESS,
            format!(
                "DnsInterpret registered {} hosts successfully",
                entries.len()
            ),
        ))
    }
}

#[async_trait]
impl<T: Topology + DnsServer> Interpret<T> for DnsInterpret {
    fn get_name(&self) -> InterpretName {
        InterpretName::OPNSenseDns
    }

    fn get_version(&self) -> crate::domain::data::Version {
        self.version.clone()
    }

    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> {
        info!(
            "Executing {} on inventory {inventory:?}",
            <DnsInterpret as Interpret<T>>::get_name(self)
        );

        self.serve_dhcp_entries(inventory, topology).await?;
        self.ensure_hosts_registered(topology).await?;

        topology.commit_config().await?;

        Ok(Outcome::new(
            InterpretStatus::SUCCESS,
            format!("Dns Interpret execution successful"),
        ))
    }
}
