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

use alloc::collections::BTreeMap;

use miette::Diagnostic;
use rust_decimal::Decimal;
use snafu::Backtrace;

use snafu::ensure;
use snafu::Snafu;
use time::Date;

use core::ops::Deref;
use core::str::FromStr;

use core::fmt::Display;

use core::borrow::Borrow;

pub mod common_keys;

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

impl Key {
    /// Create a new `Key` from a [`String`] without checking the format.
    ///
    /// # Safety
    ///
    /// This function assumes that the given name is a valid `Key`.
    #[must_use]
    pub const unsafe fn from_unchecked(name: String) -> Self {
        Self { name }
    }
}

impl Borrow<Ky> for Key {
    fn borrow(&self) -> &Ky {
        self
    }
}

impl Borrow<str> for Key {
    fn borrow(&self) -> &str {
        &self.name
    }
}

impl Deref for Key {
    type Target = Ky;

    fn deref(&self) -> &Self::Target {
        unsafe { Ky::from_unchecked(&self.name) }
    }
}

impl Display for Key {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&self.name)
    }
}

impl From<&Ky> for Key {
    fn from(value: &Ky) -> Self {
        value.to_owned()
    }
}

impl FromStr for Key {
    type Err = KeyError;

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

impl TryFrom<&str> for Key {
    type Error = KeyError;

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

        let name = name.to_owned();
        Ok(Self { name })
    }
}

impl TryFrom<String> for Key {
    type Error = KeyError;

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

        Ok(Self { name })
    }
}

#[derive(Debug, Diagnostic, Snafu)]
pub struct KeyError {
    pub(crate) backtrace: Backtrace,

    pub(crate) name: String,
}

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

impl Ky {
    /// Create a new `Ky` from a [`str`] without checking the format.
    ///
    /// # Safety
    ///
    /// This function assumes that the given name is a valid `Ky`.
    #[must_use]
    pub const unsafe fn from_unchecked(name: &str) -> &Self {
        unsafe {
            let name: *const _ = name;
            let name: *const Self = name as _;
            &*name
        }
    }
}

impl Borrow<str> for Ky {
    fn borrow(&self) -> &str {
        &self.name
    }
}

impl Display for Ky {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&self.name)
    }
}

impl ToOwned for Ky {
    type Owned = Key;

    fn to_owned(&self) -> Self::Owned {
        let name = self.name.to_owned();
        Key { name }
    }
}

impl<'s> TryFrom<&'s str> for &'s Ky {
    type Error = KeyError;

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

        Ok(unsafe { Ky::from_unchecked(name) })
    }
}

pub type Map = BTreeMap<Key, Value>;

#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Value {
    Date(Date),
    Number(Decimal),
    String(String),
}

impl Value {
    #[must_use]
    pub const fn as_number(&self) -> Option<Decimal> {
        if let Self::Number(v) = self {
            Some(*v)
        } else {
            None
        }
    }

    #[must_use]
    pub fn as_str(&self) -> Option<&str> {
        if let Self::String(inner) = self {
            Some(inner)
        } else {
            None
        }
    }
}

impl Display for Value {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Date(inner) => inner.fmt(f),
            Self::Number(inner) => inner.fmt(f),
            Self::String(inner) => write!(f, "{inner:?}"),
        }
    }
}

impl From<&str> for Value {
    fn from(value: &str) -> Self {
        Self::from(value.to_owned())
    }
}

impl From<Date> for Value {
    fn from(value: Date) -> Self {
        Self::Date(value)
    }
}

impl From<Decimal> for Value {
    fn from(value: Decimal) -> Self {
        Self::Number(value)
    }
}

impl From<String> for Value {
    fn from(value: String) -> Self {
        Self::String(value)
    }
}

impl From<u8> for Value {
    fn from(value: u8) -> Self {
        Self::Number(Decimal::from(value))
    }
}

impl From<u16> for Value {
    fn from(value: u16) -> Self {
        Self::Number(Decimal::from(value))
    }
}

impl From<u32> for Value {
    fn from(value: u32) -> Self {
        Self::Number(Decimal::from(value))
    }
}

impl From<u64> for Value {
    fn from(value: u64) -> Self {
        Self::Number(Decimal::from(value))
    }
}

impl PartialEq<&str> for Value {
    fn eq(&self, other: &&str) -> bool {
        self.as_str().map_or(false, |value| value == *other)
    }
}

impl PartialEq<Value> for &str {
    fn eq(&self, other: &Value) -> bool {
        other == self
    }
}

fn is_valid_key(name: &str) -> bool {
    lazy_regex::regex_is_match!(r"^(:?[a-z][a-zA-Z0-9\-_]+)$", name)
}