// SPDX-FileCopyrightText: 2023 Greenbone AG
//
// SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception

use std::fmt::{Display, Formatter};

use super::{credential::Credential, port::Port};

pub type Host = String;

/// Information about a target of a scan
#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Target {
    /// List of hosts to scan
    pub hosts: Vec<Host>,
    /// List of ports used for scanning
    #[serde(default)]
    pub ports: Vec<Port>,
    #[serde(default)]
    /// List of excluded hosts to scan
    pub excluded_hosts: Vec<Host>,
    #[serde(default)]
    /// List of credentials used to get access to a system
    pub credentials: Vec<Credential>,
    #[serde(default)]
    /// List of ports used for alive testing
    pub alive_test_ports: Vec<Port>,
    #[serde(default)]
    /// Methods used for alive testing
    pub alive_test_methods: Vec<AliveTestMethods>,
    #[serde(default)]
    /// If multiple IP addresses resolve to the same DNS name the DNS name will only get scanned
    /// once.
    pub reverse_lookup_unify: Option<bool>,
    #[serde(default)]
    /// Only scan IP addresses that can be resolved into a DNS name.
    pub reverse_lookup_only: Option<bool>,
}

/// Enum of possible alive test methods
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AliveTestMethods {
    TcpAck = 0x01,
    Icmp = 0x02,
    Arp = 0x04,
    ConsiderAlive = 0x08,
    TcpSyn = 0x10,
    HostDiscoveryIpv6 = 0x20,
}

#[derive(Debug, thiserror::Error)]
pub enum AliveTestMethodsError {
    #[error("Invalid value for AliveTestMethods: {0:#04x}")]
    InvalidValue(u8),
}

impl AsRef<str> for AliveTestMethods {
    fn as_ref(&self) -> &str {
        match self {
            AliveTestMethods::TcpAck => "tcp_ack",
            AliveTestMethods::Icmp => "icmp",
            AliveTestMethods::Arp => "arp",
            AliveTestMethods::ConsiderAlive => "consider_alive",
            AliveTestMethods::TcpSyn => "tcp_syn",
            AliveTestMethods::HostDiscoveryIpv6 => "host_discovery_ipv6",
        }
    }
}

impl TryFrom<u8> for AliveTestMethods {
    type Error = AliveTestMethodsError;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0x01 => Ok(AliveTestMethods::TcpAck),
            0x02 => Ok(AliveTestMethods::Icmp),
            0x04 => Ok(AliveTestMethods::Arp),
            0x08 => Ok(AliveTestMethods::ConsiderAlive),
            0x10 => Ok(AliveTestMethods::TcpSyn),
            0x20 => Ok(AliveTestMethods::HostDiscoveryIpv6),
            _ => Err(AliveTestMethodsError::InvalidValue(value)),
        }
    }
}

impl From<&str> for AliveTestMethods {
    fn from(value: &str) -> Self {
        match value {
            "tcp_ack" => AliveTestMethods::TcpAck,
            "tcp_syn" => AliveTestMethods::TcpSyn,
            "icmp" => AliveTestMethods::Icmp,
            "arp" => AliveTestMethods::Arp,
            "host_discovery_ipv6" => AliveTestMethods::HostDiscoveryIpv6,
            _ => AliveTestMethods::ConsiderAlive,
        }
    }
}

impl Display for AliveTestMethods {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            AliveTestMethods::TcpAck => write!(f, "tcp_ping"),
            AliveTestMethods::Icmp => write!(f, "icmp"),
            AliveTestMethods::Arp => write!(f, "arp"),
            AliveTestMethods::ConsiderAlive => write!(f, "consider_alive"),
            AliveTestMethods::TcpSyn => write!(f, "tcp_ping"),
            AliveTestMethods::HostDiscoveryIpv6 => write!(f, "host_discovery_ipv6"),
        }
    }
}
