use async_trait::async_trait;
use harmony_macros::ip;
use harmony_types::net::MacAddress;

use crate::executors::ExecutorError;

use super::DHCPStaticEntry;
use super::DhcpServer;
use super::DnsRecord;
use super::DnsRecordType;
use super::DnsServer;
use super::Firewall;
use super::HttpServer;
use super::IpAddress;
use super::LoadBalancer;
use super::LoadBalancerService;
use super::LogicalHost;
use super::OcK8sclient;
use super::Router;
use super::TftpServer;

use super::Topology;
use super::Url;
use super::openshift::OpenshiftClient;
use std::sync::Arc;

#[derive(Debug, Clone)]
pub struct HAClusterTopology {
    pub domain_name: String,
    pub router: Arc<dyn Router>,
    pub load_balancer: Arc<dyn LoadBalancer>,
    pub firewall: Arc<dyn Firewall>,
    pub dhcp_server: Arc<dyn DhcpServer>,
    pub tftp_server: Arc<dyn TftpServer>,
    pub http_server: Arc<dyn HttpServer>,
    pub dns_server: Arc<dyn DnsServer>,
    pub bootstrap_host: LogicalHost,
    pub control_plane: Vec<LogicalHost>,
    pub workers: Vec<LogicalHost>,
    pub switch: Vec<LogicalHost>,
}

impl Topology for HAClusterTopology {
    fn name(&self) -> &str {
        todo!()
    }
}

#[async_trait]
impl OcK8sclient for HAClusterTopology {
    async fn oc_client(&self) -> Result<Arc<OpenshiftClient>, kube::Error> {
        Ok(Arc::new(OpenshiftClient::try_default().await?))
    }
}

impl HAClusterTopology {
    pub fn autoload() -> Self {
        let dummy_infra = Arc::new(DummyInfra {});
        let dummy_host = LogicalHost {
            ip: ip!("0.0.0.0"),
            name: "dummyhost".to_string(),
        };

        Self {
            domain_name: "DummyTopology".to_string(),
            router: dummy_infra.clone(),
            load_balancer: dummy_infra.clone(),
            firewall: dummy_infra.clone(),
            dhcp_server: dummy_infra.clone(),
            tftp_server: dummy_infra.clone(),
            http_server: dummy_infra.clone(),
            dns_server: dummy_infra.clone(),
            bootstrap_host: dummy_host,
            control_plane: vec![],
            workers: vec![],
            switch: vec![],
        }
    }
}

#[async_trait]
impl DnsServer for HAClusterTopology {
    async fn register_dhcp_leases(&self, register: bool) -> Result<(), ExecutorError> {
        self.dns_server.register_dhcp_leases(register).await
    }
    async fn register_hosts(&self, hosts: Vec<DnsRecord>) -> Result<(), ExecutorError> {
        self.dns_server.register_hosts(hosts).await
    }
    fn remove_record(&self, name: &str, record_type: DnsRecordType) -> Result<(), ExecutorError> {
        self.dns_server.remove_record(name, record_type)
    }
    async fn list_records(&self) -> Vec<DnsRecord> {
        self.dns_server.list_records().await
    }
    fn get_ip(&self) -> IpAddress {
        self.dns_server.get_ip()
    }
    fn get_host(&self) -> LogicalHost {
        self.dns_server.get_host()
    }
    async fn commit_config(&self) -> Result<(), ExecutorError> {
        self.dns_server.commit_config().await
    }
}

#[async_trait]
impl LoadBalancer for HAClusterTopology {
    fn get_ip(&self) -> IpAddress {
        self.load_balancer.get_ip()
    }
    fn get_host(&self) -> LogicalHost {
        self.load_balancer.get_host()
    }
    async fn add_service(&self, service: &LoadBalancerService) -> Result<(), ExecutorError> {
        self.load_balancer.add_service(service).await
    }
    async fn remove_service(&self, service: &LoadBalancerService) -> Result<(), ExecutorError> {
        self.load_balancer.remove_service(service).await
    }
    async fn list_services(&self) -> Vec<LoadBalancerService> {
        self.load_balancer.list_services().await
    }
    async fn ensure_initialized(&self) -> Result<(), ExecutorError> {
        self.load_balancer.ensure_initialized().await
    }
    async fn commit_config(&self) -> Result<(), ExecutorError> {
        self.load_balancer.commit_config().await
    }
    async fn reload_restart(&self) -> Result<(), ExecutorError> {
        self.load_balancer.reload_restart().await
    }
}

#[async_trait]
impl DhcpServer for HAClusterTopology {
    async fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), ExecutorError> {
        self.dhcp_server.add_static_mapping(entry).await
    }
    async fn remove_static_mapping(&self, mac: &MacAddress) -> Result<(), ExecutorError> {
        self.dhcp_server.remove_static_mapping(mac).await
    }
    async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)> {
        self.dhcp_server.list_static_mappings().await
    }
    async fn set_next_server(&self, ip: IpAddress) -> Result<(), ExecutorError> {
        self.dhcp_server.set_next_server(ip).await
    }
    async fn set_boot_filename(&self, boot_filename: &str) -> Result<(), ExecutorError> {
        self.dhcp_server.set_boot_filename(boot_filename).await
    }
    fn get_ip(&self) -> IpAddress {
        self.dhcp_server.get_ip()
    }
    fn get_host(&self) -> LogicalHost {
        self.dhcp_server.get_host()
    }
    async fn commit_config(&self) -> Result<(), ExecutorError> {
        self.dhcp_server.commit_config().await
    }
}

#[async_trait]
impl TftpServer for HAClusterTopology {
    async fn serve_files(&self, url: &Url) -> Result<(), ExecutorError> {
        self.tftp_server.serve_files(url).await
    }
    fn get_ip(&self) -> IpAddress {
        self.tftp_server.get_ip()
    }

    async fn set_ip(&self, ip: IpAddress) -> Result<(), ExecutorError> {
        self.tftp_server.set_ip(ip).await
    }
    async fn ensure_initialized(&self) -> Result<(), ExecutorError> {
        self.tftp_server.ensure_initialized().await
    }
    async fn commit_config(&self) -> Result<(), ExecutorError> {
        self.tftp_server.commit_config().await
    }
    async fn reload_restart(&self) -> Result<(), ExecutorError> {
        self.tftp_server.reload_restart().await
    }
}

impl Router for HAClusterTopology {
    fn get_gateway(&self) -> super::IpAddress {
        self.router.get_gateway()
    }
    fn get_cidr(&self) -> cidr::Ipv4Cidr {
        self.router.get_cidr()
    }
    fn get_host(&self) -> LogicalHost {
        self.router.get_host()
    }
}

#[async_trait]
impl HttpServer for HAClusterTopology {
    async fn serve_files(&self, url: &Url) -> Result<(), ExecutorError> {
        self.http_server.serve_files(url).await
    }

    fn get_ip(&self) -> IpAddress {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn ensure_initialized(&self) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn commit_config(&self) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn reload_restart(&self) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
}

#[derive(Debug)]
struct DummyInfra;

const UNIMPLEMENTED_DUMMY_INFRA: &str = "This is a dummy infrastructure, no operation is supported";

impl Router for DummyInfra {
    fn get_gateway(&self) -> super::IpAddress {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    fn get_cidr(&self) -> cidr::Ipv4Cidr {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    fn get_host(&self) -> LogicalHost {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
}

impl Firewall for DummyInfra {
    fn add_rule(
        &mut self,
        _rule: super::FirewallRule,
    ) -> Result<(), crate::executors::ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    fn remove_rule(&mut self, _rule_id: &str) -> Result<(), crate::executors::ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    fn list_rules(&self) -> Vec<super::FirewallRule> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    fn get_ip(&self) -> super::IpAddress {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    fn get_host(&self) -> LogicalHost {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
}

#[async_trait]
impl DhcpServer for DummyInfra {
    async fn add_static_mapping(&self, _entry: &DHCPStaticEntry) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn remove_static_mapping(&self, _mac: &MacAddress) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn set_next_server(&self, _ip: IpAddress) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn set_boot_filename(&self, _boot_filename: &str) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    fn get_ip(&self) -> IpAddress {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    fn get_host(&self) -> LogicalHost {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn commit_config(&self) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
}

#[async_trait]
impl LoadBalancer for DummyInfra {
    fn get_ip(&self) -> IpAddress {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    fn get_host(&self) -> LogicalHost {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn add_service(&self, _service: &LoadBalancerService) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn remove_service(&self, _service: &LoadBalancerService) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn list_services(&self) -> Vec<LoadBalancerService> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn ensure_initialized(&self) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn commit_config(&self) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn reload_restart(&self) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
}

#[async_trait]
impl TftpServer for DummyInfra {
    async fn serve_files(&self, _url: &Url) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    fn get_ip(&self) -> IpAddress {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }

    async fn set_ip(&self, _ip: IpAddress) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn ensure_initialized(&self) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn commit_config(&self) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn reload_restart(&self) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
}

#[async_trait]
impl HttpServer for DummyInfra {
    async fn serve_files(&self, _url: &Url) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    fn get_ip(&self) -> IpAddress {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn ensure_initialized(&self) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn commit_config(&self) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn reload_restart(&self) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
}

#[async_trait]
impl DnsServer for DummyInfra {
    async fn register_dhcp_leases(&self, _register: bool) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn register_hosts(&self, _hosts: Vec<DnsRecord>) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    fn remove_record(&self, _name: &str, _record_type: DnsRecordType) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn list_records(&self) -> Vec<DnsRecord> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    fn get_ip(&self) -> IpAddress {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    fn get_host(&self) -> LogicalHost {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    async fn commit_config(&self) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
}
