use std::marker::PhantomData;

use crate::storage::{NormalWritingMode, OverwritingWritingMode, WritingMode};

// MARK: - readable singleton storage

#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub(crate) struct ReadableSingletonStorage<V> {
    value: Option<V>,
}

impl<V> ReadableSingletonStorage<V> {
    pub fn new() -> Self {
        return Self { value: None };
    }

    pub fn get(&self) -> Option<&V> {
        return self.value.as_ref();
    }
}

// MARK: - read-write singleton storage

#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub(crate) struct SingletonStorage<V, M: WritingMode> {
    mode: PhantomData<M>,
    r: ReadableSingletonStorage<V>,
}

impl<V, M: WritingMode> SingletonStorage<V, M> {
    pub fn new() -> Self {
        return Self {
            mode: PhantomData {},
            r: ReadableSingletonStorage::new(),
        };
    }

    pub fn r(&self) -> &ReadableSingletonStorage<V> {
        return &self.r;
    }
}

impl<V> SingletonStorage<V, NormalWritingMode> {
    pub fn set(&mut self, value: V) {
        self.r.value = Some(value);
    }
}

impl<V> SingletonStorage<V, OverwritingWritingMode> {
    pub fn set(&mut self, value: V, overwrite: bool) {
        if overwrite || self.r.value.is_none() {
            self.r.value = Some(value);
        }
    }

    pub fn set_opt(&mut self, value: Option<V>, overwrite: bool) {
        if let Some(value) = value {
            self.set(value, overwrite)
        }
    }
}

// MARK: - tests

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

    #[rstest]
    fn test_readable_new() {
        // arrange
        let s: ReadableSingletonStorage<i32> = ReadableSingletonStorage::new();
        // act-assert
        assert_eq!(None, s.get());
    }

    #[rstest]
    fn test_readable_has_item() {
        // arrange
        let mut s: ReadableSingletonStorage<i32> = ReadableSingletonStorage::new();
        s.value = Some(123);
        // act-assert
        assert_eq!(Some(&123), s.get());
    }

    #[rstest]
    fn test_new() {
        // arrange
        let s: SingletonStorage<i32, NormalWritingMode> = SingletonStorage::new();
        // act-assert
        assert_eq!(None, s.r.value);
        assert!(std::ptr::eq(&s.r, s.r()));
    }

    #[rstest]
    fn test_set_item_normal_mode() {
        // arrange
        let mut s: SingletonStorage<i32, NormalWritingMode> = SingletonStorage::new();
        // act
        s.set(123);
        // assert
        assert_eq!(Some(123), s.r.value);
    }

    #[rstest]
    #[case(false)]
    #[case(true)]
    fn test_set_item_overwriting_mode(#[case] overwrite: bool) {
        // arrange
        let mut s: SingletonStorage<i32, OverwritingWritingMode> = SingletonStorage::new();
        // act
        s.set(123, overwrite);
        // assert
        assert_eq!(Some(123), s.r.value);
    }

    #[rstest]
    fn test_set_item_normal_mode_overwriting() {
        // arrange
        let mut s: SingletonStorage<i32, NormalWritingMode> = SingletonStorage::new();
        s.r.value = Some(123);
        // act
        s.set(321);
        // assert
        assert_eq!(Some(321), s.r.value);
    }

    #[rstest]
    #[case(false)]
    #[case(true)]
    fn test_set_item_overwriting_mode_overweiting(#[case] overwrite: bool) {
        // arrange
        let mut s: SingletonStorage<i32, OverwritingWritingMode> = SingletonStorage::new();
        s.r.value = Some(123);
        // act
        s.set(321, overwrite);
        // assert
        assert_eq!(Some(if overwrite { 321 } else { 123 }), s.r.value);
    }
}
