use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use rustls_pemfile::Item;
use std::{
    collections::BTreeSet,
    fmt::{Display, Formatter, Result as FmtResult},
    fs::File,
    io::{BufReader, Read},
    iter,
    net::IpAddr,
    ops::{BitAnd, RangeInclusive},
    path::Path,
    str::FromStr,
};
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
use sysctl::CtlValue;
#[cfg(any(
    target_os = "macos",
    target_os = "ios",
    target_os = "freebsd",
    target_os = "linux",
    target_os = "android"
))]
use sysctl::Sysctl;

use asport::Flags;

use crate::error::Error;

pub fn load_certs<P: AsRef<Path>>(path: P) -> Result<Vec<CertificateDer<'static>>, Error> {
    let mut file = BufReader::new(File::open(&path).map_err(Error::Io)?);
    let mut certs = Vec::new();

    let mut certs_bytes = Vec::new();
    file.read_to_end(&mut certs_bytes).map_err(Error::Io)?;

    // Try to read PEM format first
    match parse_pem_certs(certs_bytes.clone()) {
        Ok(parsed_certs) => {
            certs.extend(parsed_certs);
        }
        Err(_) => {
            // If PEM parsing fails, try to read DER format
            if certs.is_empty() {
                certs = vec![CertificateDer::from(certs_bytes)];
            }
        }
    }

    Ok(certs)
}

pub fn parse_pem_certs(pem_text: Vec<u8>) -> Result<Vec<CertificateDer<'static>>, Error> {
    let mut certs = Vec::new();
    let mut reader = BufReader::new(pem_text.as_slice());

    while let Ok(Some(item)) = rustls_pemfile::read_one(&mut reader) {
        if let Item::X509Certificate(cert) = item {
            certs.push(cert);
        }
    }

    Ok(certs)
}

pub fn load_priv_key<P: AsRef<Path>>(path: P) -> Result<PrivateKeyDer<'static>, Error> {
    let mut file = BufReader::new(File::open(&path).map_err(Error::Io)?);
    let mut priv_key: Option<PrivateKeyDer> = None;
    let mut key_bytes = Vec::new();
    file.read_to_end(&mut key_bytes).map_err(Error::Io)?;
    // Try to read PEM format first
    match parse_pem_priv_key(key_bytes.clone()) {
        Ok(parsed_key) => {
            priv_key = Some(parsed_key);
        }
        Err(_) => {
            // If PEM parsing fails, try to read DER format
            if priv_key.is_none() {
                priv_key =
                    Some(PrivateKeyDer::try_from(key_bytes).map_err(Error::InvalidPrivateKey)?);
            }
        }
    }

    match priv_key {
        Some(key) => Ok(key),
        None => Err(Error::InvalidPrivateKey("No valid private key found")),
    }
}

pub fn parse_pem_priv_key(pem_text: Vec<u8>) -> Result<PrivateKeyDer<'static>, Error> {
    let mut reader = BufReader::new(pem_text.as_slice());
    let mut priv_key: Option<PrivateKeyDer> = None;

    for item in iter::from_fn(|| rustls_pemfile::read_one(&mut reader).transpose()) {
        match item {
            Ok(Item::Pkcs1Key(key)) => priv_key = Some(PrivateKeyDer::from(key)),
            Ok(Item::Pkcs8Key(key)) => priv_key = Some(PrivateKeyDer::from(key)),
            Ok(Item::Sec1Key(key)) => priv_key = Some(PrivateKeyDer::from(key)),
            _ => {}
        }
    }

    match priv_key {
        Some(key) => Ok(key),
        None => Err(Error::InvalidPrivateKey("No valid private key found")),
    }
}

#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
pub fn ephemeral_port_range() -> RangeInclusive<u16> {
    let first_ctl = sysctl::Ctl::new("net.inet.ip.portrange.first");
    let last_ctl = sysctl::Ctl::new("net.inet.ip.portrange.last");

    if let (Ok(first_ctl), Ok(last_ctl)) = (first_ctl, last_ctl) {
        // Actually, the first and last values should be u16, but sysctl crate returns them as i32.
        if let (Ok(CtlValue::Int(first)), Ok(CtlValue::Int(last))) =
            (first_ctl.value(), last_ctl.value())
        {
            return (first as u16)..=(last as u16);
        }
    }

    // Default value for macOS, iOS and FreeBSD is 49152..=65535.
    49152..=65535
}

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn ephemeral_port_range() -> RangeInclusive<u16> {
    let ctl = sysctl::Ctl::new("net.ipv4.ip_local_port_range");
    if let Ok(ctl) = ctl {
        if let Ok(value_str) = ctl.value_string() {
            let mut iter = value_str.split_whitespace();
            if let (Some(start), Some(end)) = (iter.next(), iter.next()) {
                if let (Ok(start), Ok(end)) = (start.parse::<u16>(), end.parse::<u16>()) {
                    return start..=end;
                }
            }
        }
    }

    // Default value for Linux is 32768..=60999.
    // See also: https://www.kernel.org/doc/html/latest//networking/ip-sysctl.html#ip-variables
    32768..=60999
}

#[cfg(not(any(
    target_os = "macos",
    target_os = "ios",
    target_os = "freebsd",
    target_os = "linux",
    target_os = "android"
)))]
pub fn ephemeral_port_range() -> RangeInclusive<u16> {
    // The suggested range by RFC6335 and IANA.
    // See also: https://tools.ietf.org/html/rfc6335
    // https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml
    // Since Windows Vista/Server 2008 (6.0), the default dynamic port range is 49152..65535.
    // And earlier versions had been dropped from support.
    // See also: https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/default-dynamic-port-range-tcpip-chang
    // https://doc.rust-lang.org/stable/rustc/platform-support.html
    49152..=65535
}

#[derive(Debug)]
pub enum SecurityType {
    Tls,
    Noise,
}
impl FromStr for SecurityType {
    type Err = &'static str;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "tls" => Ok(Self::Tls),
            "noise" => Ok(Self::Noise),
            _ => Err("invalid security type"),
        }
    }
}

impl Display for SecurityType {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        match self {
            Self::Tls => write!(f, "tls"),
            Self::Noise => write!(f, "noise"),
        }
    }
}

pub enum CongestionControl {
    Cubic,
    NewReno,
    Bbr,
}

impl FromStr for CongestionControl {
    type Err = &'static str;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "cubic" => Ok(Self::Cubic),
            "new_reno" | "newreno" => Ok(Self::NewReno),
            "bbr" => Ok(Self::Bbr),
            _ => Err("invalid congestion control"),
        }
    }
}

bitflags::bitflags! {
    #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
    pub struct Network: u8 {
        const TCP = 1 << 0;
        const UDP = 1 << 1;
    }
}

impl Network {
    pub fn tcp_enabled(self) -> bool {
        self.contains(Self::TCP)
    }

    pub fn udp_enabled(self) -> bool {
        self.contains(Self::UDP)
    }

    pub fn both_enabled(self) -> bool {
        self.intersects(Self::TCP | Self::UDP)
    }
}

impl FromStr for Network {
    type Err = &'static str;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "tcp" => Ok(Self::TCP),
            "udp" => Ok(Self::UDP),
            "both" | "tcpudp" | "tcp_udp" | "tcp-udp" | "all" => Ok(Self::TCP | Self::UDP),
            _ => Err("invalid network"),
        }
    }
}

impl Display for Network {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        if self.both_enabled() {
            write!(f, "both")
        } else if self.tcp_enabled() {
            write!(f, "tcp")
        } else if self.udp_enabled() {
            write!(f, "udp")
        } else {
            write!(f, "none")
        }
    }
}

pub fn merge_network(allow_network: Network, expected_network: Network) -> Result<Network, Error> {
    let allow_network = allow_network.bitand(expected_network);
    if allow_network.is_empty() {
        Err(Error::NetworkDenied(expected_network))
    } else {
        Ok(allow_network)
    }
}

#[derive(Clone, Copy)]
pub enum UdpForwardMode {
    Native,
    Quic,
}

impl Display for UdpForwardMode {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        match self {
            Self::Native => write!(f, "native"),
            Self::Quic => write!(f, "quic"),
        }
    }
}

pub struct RemoteConfiguredFlgas(Network, UdpForwardMode);

impl RemoteConfiguredFlgas {
    pub fn new(network: Network, mode: UdpForwardMode) -> Self {
        Self(network, mode)
    }
}

impl TryFrom<Flags> for RemoteConfiguredFlgas {
    type Error = Error;
    fn try_from(flags: Flags) -> Result<Self, Self::Error> {
        // network is lower 2 bits of flags
        let network = Network::from_bits_truncate(flags.bits());

        let udp_forward_mode = if flags.contains(Flags::UDP_MODE_QUIC) {
            UdpForwardMode::Quic
        } else {
            UdpForwardMode::Native
        };

        Ok(Self(network, udp_forward_mode))
    }
}

impl From<RemoteConfiguredFlgas> for (Network, UdpForwardMode) {
    fn from(value: RemoteConfiguredFlgas) -> Self {
        (value.0, value.1)
    }
}

pub struct User {
    password: Box<[u8]>,
    bind_ip: IpAddr,
    only_v6: Option<bool>,
    allow_ports: BTreeSet<u16>,
    allow_network: Network,
}

impl User {
    pub fn new(
        password: Box<[u8]>,
        bind_ip: IpAddr,
        allow_ports: BTreeSet<u16>,
        only_v6: Option<bool>,
        allow_network: Network,
    ) -> Self {
        Self {
            password,
            bind_ip,
            only_v6,
            allow_ports,
            allow_network,
        }
    }

    pub fn password(&self) -> &[u8] {
        &self.password
    }

    pub fn listen_ip(&self) -> IpAddr {
        self.bind_ip
    }

    pub fn allow_ports(&self) -> BTreeSet<u16> {
        self.allow_ports.clone()
    }

    pub fn only_v6(&self) -> Option<bool> {
        self.only_v6
    }

    pub fn allow_network(&self) -> &Network {
        &self.allow_network
    }
}
