/*
    This file is part of jellything (https://codeberg.org/metamuffin/jellything)
    which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
    Copyright (C) 2026 metamuffin <metamuffin.org>
*/

use crate::{Object, Tag, slice_to_ob};
use core::{marker::Sized, unimplemented};
use std::{borrow::Cow, fmt::Display, str::FromStr};

pub trait ValueMarker<'a> {
    type Inner: ValueSer<'a>;
}

impl<'a> ValueMarker<'a> for str {
    type Inner = &'a str;
}
impl<'a> ValueMarker<'a> for Object {
    type Inner = &'a Object;
}
impl<'a> ValueMarker<'a> for [u8] {
    type Inner = &'a [u8];
}

impl<'a> ValueMarker<'a> for Tag {
    type Inner = Tag;
}
impl<'a> ValueMarker<'a> for u32 {
    type Inner = u32;
}
impl<'a> ValueMarker<'a> for i64 {
    type Inner = i64;
}
impl<'a> ValueMarker<'a> for u64 {
    type Inner = u64;
}
impl<'a> ValueMarker<'a> for () {
    type Inner = ();
}
impl<'a> ValueMarker<'a> for f64 {
    type Inner = f64;
}

pub trait ValueSer<'a>: Sized {
    const TYPE: ValueType;
    fn load_aligned(_buf: &'a [u32]) -> Option<Self> {
        unimplemented!()
    }
    fn load_unaligned(_buf: &'a [u8]) -> Option<Self> {
        unimplemented!()
    }
    fn store_aligned(&self, _buf: &mut Vec<u32>) {
        unimplemented!()
    }
    fn store_unaligned(&self, _buf: &mut Vec<u8>) {
        unimplemented!()
    }
    fn size(&self) -> usize;
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ValueType {
    Binary,
    String,
    Object,
    UInt,
    Int,
    Tag,
    Float,
    Bool,
}

impl ValueType {
    #[inline]
    pub const fn is_aligned(self) -> bool {
        match self {
            ValueType::Binary | ValueType::String => false,
            _ => true,
        }
    }
    pub fn from_num(n: u32) -> Self {
        match n {
            0 => Self::Binary,
            1 => Self::String,
            2 => Self::Object,
            3 => Self::UInt,
            4 => Self::Int,
            5 => Self::Tag,
            6 => Self::Float,
            7 => Self::Bool,
            _ => unreachable!(),
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum Value<'a> {
    Tag(Tag),
    U32(u32),
    U64(u64),
    I64(i64),
    String(Cow<'a, str>),
    Binary(Cow<'a, [u8]>),
}

impl<'a> From<&'a str> for Value<'a> {
    fn from(value: &'a str) -> Self {
        Self::String(value.into())
    }
}
impl From<String> for Value<'static> {
    fn from(value: String) -> Self {
        Self::String(value.into())
    }
}
impl From<u32> for Value<'static> {
    fn from(value: u32) -> Self {
        Self::U32(value)
    }
}
impl From<u64> for Value<'static> {
    fn from(value: u64) -> Self {
        Self::U64(value)
    }
}
impl From<i64> for Value<'static> {
    fn from(value: i64) -> Self {
        Self::I64(value)
    }
}
impl From<Tag> for Value<'static> {
    fn from(value: Tag) -> Self {
        Self::Tag(value)
    }
}

impl Display for Value<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Value::Tag(tag) => write!(f, "{tag}"),
            Value::U32(x) => write!(f, "{x}"),
            Value::U64(x) => write!(f, "{x}"),
            Value::I64(x) => write!(f, "{x}"),
            Value::String(x) => write!(f, "{x:?}"),
            Value::Binary(x) => write!(f, "{x:?}"),
        }
    }
}
impl FromStr for Value<'static> {
    type Err = &'static str;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(if s.len() == 4 {
            Value::Tag(Tag::new(s.as_bytes().try_into().unwrap()))
        } else if let Some(s) = s.strip_suffix("i64") {
            Value::I64(s.parse().map_err(|_| "invalid i64 literal")?)
        } else if let Some(s) = s.strip_suffix("u64") {
            Value::U64(s.parse().map_err(|_| "invalid u64 literal")?)
        } else if let Some(s) = s.strip_suffix("u32") {
            Value::U32(s.parse().map_err(|_| "invalid u32 literal")?)
        } else if let Some(s) = s.strip_circumfix("\"", "\"") {
            Value::String(s.to_owned().into())
        } else {
            return Err("invalid value literal");
        })
    }
}

impl<'a> ValueSer<'a> for &'a str {
    const TYPE: ValueType = ValueType::String;
    fn load_unaligned(buf: &'a [u8]) -> Option<Self> {
        str::from_utf8(buf).ok()
    }
    fn store_unaligned(&self, buf: &mut Vec<u8>) {
        buf.extend(self.as_bytes());
    }
    fn size(&self) -> usize {
        self.len()
    }
}
impl ValueSer<'_> for u32 {
    const TYPE: ValueType = ValueType::UInt;
    fn load_aligned(buf: &[u32]) -> Option<Self> {
        buf.get(0).copied().map(u32::from_be)
    }
    fn store_aligned(&self, buf: &mut Vec<u32>) {
        buf.push(self.to_be());
    }
    fn size(&self) -> usize {
        4
    }
}
impl ValueSer<'_> for Tag {
    const TYPE: ValueType = ValueType::Tag;
    fn load_aligned(buf: &[u32]) -> Option<Self> {
        buf.get(0).copied().map(u32::from_be).map(Tag)
    }
    fn store_aligned(&self, buf: &mut Vec<u32>) {
        buf.push(self.0.to_be());
    }
    fn size(&self) -> usize {
        4
    }
}
impl ValueSer<'_> for u64 {
    const TYPE: ValueType = ValueType::UInt;
    fn load_aligned(buf: &[u32]) -> Option<Self> {
        let hi = u32::from_be(*buf.get(0)?) as u64;
        let lo = u32::from_be(*buf.get(1)?) as u64;
        Some(hi << 32 | lo)
    }
    fn store_aligned(&self, buf: &mut Vec<u32>) {
        buf.push(((self >> 32) as u32).to_be());
        buf.push((*self as u32).to_be());
    }
    fn size(&self) -> usize {
        8
    }
}
impl ValueSer<'_> for f64 {
    const TYPE: ValueType = ValueType::Float;
    fn load_aligned(buf: &[u32]) -> Option<Self> {
        if buf.len() < 2 {
            return None;
        };
        let a = u32::to_be_bytes(buf[0]);
        let b = u32::to_be_bytes(buf[1]);
        Some(f64::from_be_bytes([
            a[0], a[1], a[2], a[3], b[0], b[1], b[2], b[3],
        ]))
    }
    fn store_aligned(&self, buf: &mut Vec<u32>) {
        let b = self.to_be_bytes();
        buf.push(u32::from_be_bytes([b[0], b[1], b[2], b[3]]));
        buf.push(u32::from_be_bytes([b[4], b[5], b[6], b[7]]));
    }
    fn size(&self) -> usize {
        8
    }
}
impl ValueSer<'_> for i64 {
    const TYPE: ValueType = ValueType::Int;
    fn load_aligned(buf: &[u32]) -> Option<Self> {
        u64::load_aligned(buf).map(|x| x as i64)
    }
    fn store_aligned(&self, buf: &mut Vec<u32>) {
        (*self as u64).store_aligned(buf);
    }
    fn size(&self) -> usize {
        8
    }
}
impl<'a> ValueSer<'a> for &'a Object {
    const TYPE: ValueType = ValueType::Object;
    fn load_aligned(buf: &'a [u32]) -> Option<Self> {
        // TODO validate
        Some(slice_to_ob(buf))
    }
    fn store_aligned(&self, buf: &mut Vec<u32>) {
        buf.extend(&self.0);
    }
    fn size(&self) -> usize {
        self.0.len() * size_of::<u32>()
    }
}
impl<'a> ValueSer<'a> for &'a [u8] {
    const TYPE: ValueType = ValueType::Binary;
    fn load_unaligned(buf: &'a [u8]) -> Option<Self> {
        Some(buf)
    }
    fn store_unaligned(&self, buf: &mut Vec<u8>) {
        buf.extend(*self);
    }
    fn size(&self) -> usize {
        self.len()
    }
}
impl<'a> ValueSer<'a> for () {
    const TYPE: ValueType = ValueType::Int;
    fn load_aligned(_buf: &'a [u32]) -> Option<Self> {
        Some(())
    }
    fn store_aligned(&self, _buf: &mut Vec<u32>) {}
    fn size(&self) -> usize {
        0
    }
}
