// SPDX-FileCopyrightText: 2023 - 2024 Markus Haug (Korrat)
//
// SPDX-License-Identifier: EUPL-1.2

use alloc::collections::BTreeSet;
use delegate::delegate;
use miette::Diagnostic;
use snafu::Backtrace;

use snafu::ensure;
use snafu::Snafu;

use core::str::FromStr;

use core::fmt::Display;

use core::ops::Deref;

use core::borrow::Borrow;

#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct Link {
    pub(crate) name: String,
}

impl Borrow<Lnk> for Link {
    fn borrow(&self) -> &Lnk {
        self
    }
}

impl Deref for Link {
    type Target = Lnk;

    fn deref(&self) -> &Self::Target {
        #[allow(unsafe_code)]
        unsafe {
            // SAFETY: self.name is a valid link name, by construction
            Self::Target::from_unchecked(&self.name)
        }
    }
}

impl Display for Link {
    delegate! {
        to self.name {
            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result;
        }
    }
}

impl From<&Lnk> for Link {
    #[inline]
    fn from(link: &Lnk) -> Self {
        link.to_owned()
    }
}

impl FromStr for Link {
    type Err = Error;

    #[inline]
    fn from_str(name: &str) -> Result<Self, Self::Err> {
        <&Lnk>::try_from(name).map(Self::from)
    }
}

impl TryFrom<String> for Link {
    type Error = Error;

    #[inline]
    fn try_from(name: String) -> Result<Self, Self::Error> {
        ensure!(is_valid_link_name(&name), Snafu { name });

        Ok(Self { name })
    }
}

#[derive(Debug, Diagnostic, Snafu)]
pub struct Error {
    pub(crate) name: String,

    pub(crate) backtrace: Backtrace,
}

#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct Lnk {
    pub(crate) name: str,
}

impl Lnk {
    #[allow(unsafe_code)]
    #[inline]
    pub(crate) const unsafe fn from_unchecked(name: &str) -> &Self {
        let name: *const _ = name;
        let name = name as *const _;

        unsafe { &*name }
    }
}

impl ToOwned for Lnk {
    type Owned = Link;

    #[inline]
    fn to_owned(&self) -> Self::Owned {
        let name = self.name.to_owned();
        Self::Owned { name }
    }
}

impl<'l> TryFrom<&'l str> for &'l Lnk {
    type Error = Error;

    #[inline]
    fn try_from(name: &'l str) -> Result<Self, Self::Error> {
        ensure!(is_valid_link_name(name), Snafu { name });

        Ok(
            #[allow(unsafe_code)]
            unsafe {
                // SAFETY: we have ensured that `name` is a valid link.
                Lnk::from_unchecked(name)
            },
        )
    }
}

pub type Set = BTreeSet<Link>;

fn is_valid_link_name(name: &str) -> bool {
    lazy_regex::regex_is_match!(r"^(:?\^[A-Za-z0-9\-_/.]+)$", name)
}