use std::{
    net::{IpAddr, Ipv4Addr},
    sync::Arc,
};

use cidr::Ipv4Cidr;
use harmony::{
    hardware::{FirewallGroup, HostCategory, Location, PhysicalHost, SwitchGroup},
    infra::opnsense::OPNSenseManagementInterface,
    inventory::Inventory,
    maestro::Maestro,
    modules::{
        http::HttpScore,
        ipxe::IpxeScore,
        okd::{
            bootstrap_dhcp::OKDBootstrapDhcpScore,
            bootstrap_load_balancer::OKDBootstrapLoadBalancerScore, dhcp::OKDDhcpScore,
            dns::OKDDnsScore,
        },
        tftp::TftpScore,
    },
    topology::{LogicalHost, UnmanagedRouter, Url},
};
use harmony_macros::{ip, mac_address};

#[tokio::main]
async fn main() {
    let firewall = harmony::topology::LogicalHost {
        ip: ip!("192.168.33.1"),
        name: String::from("fw0"),
    };

    let opnsense = Arc::new(
        harmony::infra::opnsense::OPNSenseFirewall::new(firewall, None, "root", "opnsense").await,
    );
    let lan_subnet = Ipv4Addr::new(192, 168, 33, 0);
    let gateway_ipv4 = Ipv4Addr::new(192, 168, 33, 1);
    let gateway_ip = IpAddr::V4(gateway_ipv4);
    let topology = harmony::topology::HAClusterTopology {
        domain_name: "ncd0.harmony.mcd".to_string(), // TODO this must be set manually correctly
        // when setting up the opnsense firewall
        router: Arc::new(UnmanagedRouter::new(
            gateway_ip,
            Ipv4Cidr::new(lan_subnet, 24).unwrap(),
        )),
        load_balancer: opnsense.clone(),
        firewall: opnsense.clone(),
        tftp_server: opnsense.clone(),
        http_server: opnsense.clone(),
        dhcp_server: opnsense.clone(),
        dns_server: opnsense.clone(),
        control_plane: vec![
            LogicalHost {
                ip: ip!("192.168.33.20"),
                name: "cp0".to_string(),
            },
            LogicalHost {
                ip: ip!("192.168.33.21"),
                name: "cp1".to_string(),
            },
            LogicalHost {
                ip: ip!("192.168.33.22"),
                name: "cp2".to_string(),
            },
        ],
        bootstrap_host: LogicalHost {
            ip: ip!("192.168.33.66"),
            name: "bootstrap".to_string(),
        },
        workers: vec![
            LogicalHost {
                ip: ip!("192.168.33.30"),
                name: "wk0".to_string(),
            },
            LogicalHost {
                ip: ip!("192.168.33.31"),
                name: "wk1".to_string(),
            },
            LogicalHost {
                ip: ip!("192.168.33.32"),
                name: "wk2".to_string(),
            },
        ],
        switch: vec![],
    };

    let inventory = Inventory {
        location: Location::new("I am mobile".to_string(), "earth".to_string()),
        switch: SwitchGroup::from([]),
        firewall: FirewallGroup::from([PhysicalHost::empty(HostCategory::Firewall)
            .management(Arc::new(OPNSenseManagementInterface::new()))]),
        storage_host: vec![],
        worker_host: vec![
            PhysicalHost::empty(HostCategory::Server)
                .mac_address(mac_address!("C4:62:37:02:61:0F")),
            PhysicalHost::empty(HostCategory::Server)
                .mac_address(mac_address!("C4:62:37:02:61:26")),
            // thisone
            // Then create the ipxe file
            // set the dns static leases
            // bootstrap nodes
            // start ceph cluster
            // try installation of lampscore
            // bingo?
            PhysicalHost::empty(HostCategory::Server)
                .mac_address(mac_address!("C4:62:37:02:61:70")),
        ],
        control_plane_host: vec![
            PhysicalHost::empty(HostCategory::Server)
                .mac_address(mac_address!("C4:62:37:02:60:FA")),
            PhysicalHost::empty(HostCategory::Server)
                .mac_address(mac_address!("C4:62:37:02:61:1A")),
            PhysicalHost::empty(HostCategory::Server)
                .mac_address(mac_address!("C4:62:37:01:BC:68")),
        ],
    };

    // TODO regroup smaller scores in a larger one such as this
    // let okd_boostrap_preparation();

    let bootstrap_dhcp_score = OKDBootstrapDhcpScore::new(&topology, &inventory);
    let bootstrap_load_balancer_score = OKDBootstrapLoadBalancerScore::new(&topology);
    let dhcp_score = OKDDhcpScore::new(&topology, &inventory);
    let dns_score = OKDDnsScore::new(&topology);
    let load_balancer_score =
        harmony::modules::okd::load_balancer::OKDLoadBalancerScore::new(&topology);

    let tftp_score = TftpScore::new(Url::LocalFolder("./data/watchguard/tftpboot".to_string()));
    let http_score = HttpScore::new(Url::LocalFolder(
        "./data/watchguard/pxe-http-files".to_string(),
    ));
    let ipxe_score = IpxeScore::new();
    let mut maestro = Maestro::initialize(inventory, topology).await.unwrap();
    maestro.register_all(vec![
        Box::new(dns_score),
        Box::new(bootstrap_dhcp_score),
        Box::new(bootstrap_load_balancer_score),
        Box::new(load_balancer_score),
        Box::new(tftp_score),
        Box::new(http_score),
        Box::new(ipxe_score),
        Box::new(dhcp_score),
    ]);
    harmony_tui::init(maestro).await.unwrap();
}
