use o3::buffer::{Owned, Shared};

use super::super::{HotTextInner, TextItemInner};
use super::headers::HeaderNameToken;
use crate::http::request::LocalFrameBytesRef;

#[derive(Clone)]
pub enum HeaderValueInner<'req> {
    Static(&'static [u8]),
    Inline(InlineHeaderValue),
    Shared(Shared),
    Local(LocalFrameBytesRef<'req>),
}

#[derive(Clone, Copy)]
pub struct InlineHeaderValue {
    bytes: [u8; 31],
    len: u8,
}

impl InlineHeaderValue {
    pub fn new(value: &[u8]) -> Self {
        assert!(
            value.len() <= 31,
            "inline header value overflow: max 31, got {}",
            value.len()
        );
        let mut bytes = [0u8; 31];
        bytes[..value.len()].copy_from_slice(value);
        Self {
            bytes,
            len: value.len() as u8,
        }
    }

    pub fn from_decimal(value: usize) -> Self {
        let mut raw = [0u8; 20];
        let n = crate::http::codec::Wire::write_dec(value, &mut raw);
        Self::new(&raw[..n])
    }

    pub fn as_bytes(&self) -> &[u8] {
        &self.bytes[..usize::from(self.len)]
    }
}

impl std::fmt::Debug for InlineHeaderValue {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("InlineHeaderValue")
            .field("len", &self.len)
            .finish()
    }
}

impl<'req> std::fmt::Debug for HeaderValueInner<'req> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Static(value) => f
                .debug_struct("HeaderValue::Static")
                .field("len", &value.len())
                .finish(),
            Self::Inline(value) => f
                .debug_struct("HeaderValue::Inline")
                .field("len", &value.as_bytes().len())
                .finish(),
            Self::Shared(value) => f
                .debug_struct("HeaderValue::Shared")
                .field("len", &value.len())
                .finish(),
            Self::Local(value) => f
                .debug_struct("HeaderValue::Local")
                .field("len", &value.len())
                .finish(),
        }
    }
}

impl<'req> HeaderValueInner<'req> {
    pub fn as_bytes(&self) -> &[u8] {
        match self {
            Self::Static(value) => value,
            Self::Inline(value) => value.as_bytes(),
            Self::Shared(value) => value.as_ref(),
            Self::Local(value) => value.as_bytes(),
        }
    }

    pub fn len(&self) -> usize {
        self.as_bytes().len()
    }

    pub fn is_empty(&self) -> bool {
        self.as_bytes().is_empty()
    }
}

#[derive(Clone, Debug)]
pub struct HeaderItemInner<'req> {
    pub(super) name: HeaderNameToken,
    pub(super) value: HeaderValueInner<'req>,
}

pub type HeaderItem = HeaderItemInner<'static>;

impl<'req> HeaderItemInner<'req> {
    pub fn from_value<V>(name: HeaderNameToken, value: V) -> Self
    where
        V: IntoHeaderValue<'req>,
    {
        Self {
            name,
            value: value.into_header_value(),
        }
    }

    pub(crate) fn placeholder() -> Self {
        Self {
            name: HeaderNameToken::empty_placeholder(),
            value: HeaderValueInner::Static(&[]),
        }
    }

    pub fn wire_len(&self) -> usize {
        self.name.as_bytes().len() + 2 + self.value.len() + 2
    }

    pub fn name_bytes(&self) -> &'static [u8] {
        self.name.as_bytes()
    }

    pub fn value_bytes(&self) -> &[u8] {
        self.value.as_bytes()
    }
}

pub trait IntoHeaderValue<'req> {
    fn into_header_value(self) -> HeaderValueInner<'req>;
}

impl<'req> IntoHeaderValue<'req> for HeaderValueInner<'req> {
    fn into_header_value(self) -> Self {
        self
    }
}

impl<'req> IntoHeaderValue<'req> for InlineHeaderValue {
    fn into_header_value(self) -> HeaderValueInner<'req> {
        HeaderValueInner::Inline(self)
    }
}

impl<'req> IntoHeaderValue<'req> for LocalFrameBytesRef<'req> {
    fn into_header_value(self) -> HeaderValueInner<'req> {
        HeaderValueInner::Local(self)
    }
}

impl<'req> IntoHeaderValue<'req> for Shared {
    fn into_header_value(self) -> HeaderValueInner<'req> {
        HeaderValueInner::Shared(self)
    }
}

impl<'req> IntoHeaderValue<'req> for Owned {
    fn into_header_value(self) -> HeaderValueInner<'req> {
        HeaderValueInner::Shared(self.freeze())
    }
}

impl<'req> IntoHeaderValue<'req> for String {
    fn into_header_value(self) -> HeaderValueInner<'req> {
        HeaderValueInner::Shared(Shared::from(self.into_bytes()))
    }
}

impl<'req> IntoHeaderValue<'req> for &'static str {
    fn into_header_value(self) -> HeaderValueInner<'req> {
        HeaderValueInner::Static(self.as_bytes())
    }
}

pub trait TextSpec<'req> {
    fn into_hot_text(self) -> HotTextInner<'req>;
}

impl<'req, const N: usize> TextSpec<'req> for [TextItemInner<'req>; N] {
    fn into_hot_text(self) -> HotTextInner<'req> {
        HotTextInner::from_items(self)
    }
}

impl<'req> TextSpec<'req> for HotTextInner<'req> {
    fn into_hot_text(self) -> Self {
        self
    }
}
