use std::cmp::min;
use std::ops::Index;

use crate::keys::KeyTrait;
use crate::keys::array_key::ArrayKey;
use crate::partials::{Partial, prefix_length_bytes};

#[derive(Clone, Debug, Eq)]
pub struct ArrPartial<const SIZE: usize> {
    data: [u8; SIZE],
    len: usize,
}

impl<const SIZE: usize> AsRef<[u8]> for ArrPartial<SIZE> {
    fn as_ref(&self) -> &[u8] {
        &self.data[..self.len]
    }
}

impl<'a, const SIZE: usize> IntoIterator for &'a ArrPartial<SIZE> {
    type Item = &'a u8;
    type IntoIter = std::slice::Iter<'a, u8>;

    fn into_iter(self) -> Self::IntoIter {
        self.data[..self.len].iter()
    }
}

impl<const SIZE: usize> PartialEq for ArrPartial<SIZE> {
    fn eq(&self, other: &Self) -> bool {
        self.data[..self.len] == other.data[..other.len]
    }
}
impl<const SIZE: usize> ArrPartial<SIZE> {
    pub fn key(src: &[u8]) -> Self {
        debug_assert!(src.len() < SIZE);
        let mut data = [0; SIZE];
        data[..src.len()].copy_from_slice(src);
        Self {
            data,
            len: src.len() + 1,
        }
    }

    pub fn from_slice(src: &[u8]) -> Self {
        debug_assert!(
            src.len() <= SIZE,
            "data length {} is greater than maximum partial length {}",
            src.len(),
            SIZE
        );
        let mut data = [0; SIZE];
        data[..src.len()].copy_from_slice(src);
        Self {
            data,
            len: src.len(),
        }
    }

    pub fn to_slice(&self) -> &[u8] {
        &self.data[..self.len]
    }
}

impl<const SIZE: usize> Index<usize> for ArrPartial<SIZE> {
    type Output = u8;

    fn index(&self, index: usize) -> &Self::Output {
        self.data.index(index)
    }
}
impl<const SIZE: usize> Partial for ArrPartial<SIZE> {
    fn partial_before(&self, length: usize) -> Self {
        debug_assert!(length <= self.len);
        ArrPartial::from_slice(&self.data[..length])
    }

    fn partial_from(&self, src_offset: usize, length: usize) -> Self {
        debug_assert!(src_offset + length <= self.len);
        ArrPartial::from_slice(&self.data[src_offset..src_offset + length])
    }

    fn partial_after(&self, start: usize) -> Self {
        debug_assert!(start <= self.len);
        ArrPartial::from_slice(&self.data[start..self.len])
    }

    fn partial_extended_with(&self, other: &Self) -> Self {
        debug_assert!(self.len + other.len < SIZE);
        let mut data = [0; SIZE];
        data[..self.len].copy_from_slice(&self.data[..self.len]);
        data[self.len..self.len + other.len].copy_from_slice(&other.data[..other.len]);
        Self {
            data,
            len: self.len + other.len,
        }
    }

    #[inline(always)]
    fn at(&self, pos: usize) -> u8 {
        debug_assert!(pos < self.len);
        self.data[pos]
    }

    #[inline(always)]
    fn len(&self) -> usize {
        self.len
    }

    fn prefix_length_common(&self, other: &Self) -> usize {
        self.prefix_length_slice(other.to_slice())
    }

    fn prefix_length_key<'a, K>(&self, key: &'a K, at_depth: usize) -> usize
    where
        K: KeyTrait<PartialType = Self> + 'a,
    {
        let len = min(self.len, key.length_at(at_depth));
        let len = min(len, SIZE);
        prefix_length_bytes(&self.data[..len], &key.as_ref()[at_depth..at_depth + len])
    }

    fn prefix_length_slice(&self, slice: &[u8]) -> usize {
        let len = min(self.len, slice.len());
        let len = min(len, SIZE);
        prefix_length_bytes(&self.data[..len], &slice[..len])
    }

    fn to_slice(&self) -> &[u8] {
        &self.data[..self.len]
    }
}

impl<const SIZE: usize> From<&[u8]> for ArrPartial<SIZE> {
    fn from(src: &[u8]) -> Self {
        Self::from_slice(src)
    }
}

impl<const SIZE: usize> From<ArrayKey<SIZE>> for ArrPartial<SIZE> {
    fn from(value: ArrayKey<SIZE>) -> Self {
        value.to_partial(0)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_partial_before() {
        let arr: ArrPartial<16> = ArrPartial::from_slice(b"Hello, world!");
        assert_eq!(arr.partial_before(5).to_slice(), b"Hello");
    }

    #[test]
    fn test_partial_from() {
        let arr: ArrPartial<16> = ArrPartial::from_slice(b"Hello, world!");
        assert_eq!(arr.partial_from(7, 5).to_slice(), b"world");
    }

    #[test]
    fn test_prefix_after() {
        let arr: ArrPartial<16> = ArrPartial::from_slice(b"Hello, world!");
        assert_eq!(arr.partial_after(7).to_slice(), b"world!");
    }

    #[test]
    fn test_at() {
        let arr: ArrPartial<16> = ArrPartial::from_slice(b"Hello, world!");
        assert_eq!(arr.at(0), 72);
    }

    #[test]
    fn test_length() {
        let arr: ArrPartial<16> = ArrPartial::from_slice(b"Hello, world!");
        assert_eq!(arr.len(), 13);
    }

    #[test]
    fn test_prefix_length_common() {
        let arr1: ArrPartial<16> = ArrPartial::from_slice(b"Hello, world!");
        let arr2: ArrPartial<16> = ArrPartial::from_slice(b"Hello, there!");
        assert_eq!(arr1.prefix_length_common(&arr2), 7);
    }

    #[test]
    fn test_key() {
        let arr: ArrPartial<16> = ArrPartial::key(b"Hello, world!");
        assert_eq!(
            arr.to_slice(),
            &[
                72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33, 0
            ]
        );
    }

    #[test]
    fn test_from_slice() {
        let arr: ArrPartial<16> = ArrPartial::from_slice(b"Hello, world!");
        assert_eq!(arr.to_slice(), b"Hello, world!");
    }

    #[test]
    fn test_partial_chain_with_key() {
        let arr1: ArrPartial<16> = ArrPartial::key(b"Hello, world!");
        let arr2: ArrPartial<16> = ArrPartial::key(b"Hello, there!");
        let partial1 = arr1.partial_before(6);
        assert_eq!(partial1.to_slice(), b"Hello,");
        let partial2 = arr2.partial_from(7, 5);
        assert_eq!(partial2.to_slice(), b"there");
        let partial3 = partial1.partial_after(1);
        assert_eq!(partial3.to_slice(), b"ello,");
        assert_eq!(0, partial3.prefix_length_common(&partial2));
    }
}
