use crate::Number;
use crate::data_result::DataResult;
use crate::list_builder::{ListBuilder, new_list_builder_impl};
use crate::map_like::MapLike;
use crate::struct_builder::ResultStructBuilder;
use std::{collections::HashMap, fmt::Display};

/// Generates a default implementation for a create_*number* function.
macro_rules! create_number_impl {
    ($name:ident, $ty:ty, $number_ty:ident, $func:ident) => {
        /// Returns how a generic
        #[doc = concat!("`", stringify!($name), "`")]
        /// in Java (equivalent to
        #[doc = concat!("[`", stringify!($ty), "`])")]
        /// is represented by this `DynamicOps`.
        fn $func(&self, data: $ty) -> Self::Value {
            self.create_number(Number::$number_ty(data))
        }
    };
}

/// A macro providing a default implementation of a get_... function in `DynamicOps`.
///
/// Those functions include:
/// - [`DynamicOps::get_byte_buffer`] (put `box` before the target expression (usually self))
/// - [`DynamicOps::get_int_list`]
/// - [`DynamicOps::get_long_list`]
#[macro_export]
macro_rules! impl_get_list {
    ($target:expr, $input:expr, $ty:literal) => {
        $target.get_iter($input).flat_map(|iter| {
            // We want all elements in the iterator to be numbers.
            iter.map(|e| $target.get_number(&e).into_result().map(Into::into))
                .collect::<Option<Vec<_>>>()
                .map_or_else(
                    || DataResult::new_error(concat!("Some elements are not ", $ty)),
                    DataResult::new_success,
                )
        })
    };
}

/// A trait describing methods to read and write a specific format (like NBT or JSON).
/// The `Value` of this trait is the type that can be used to represent anything in this format.
pub trait DynamicOps {
    type Value: PartialEq + Display + Clone;
    type StructBuilder: ResultStructBuilder<Value = Self::Value>;

    /// Returns how an empty value is represented by this `DynamicOps`.
    fn empty(&self) -> Self::Value;

    /// Returns how an empty list is represented by this `DynamicOps`.
    fn empty_list(&self) -> Self::Value {
        self.create_list(vec![])
    }

    /// Returns how an empty map is represented by this `DynamicOps`.
    fn empty_map(&self) -> Self::Value {
        self.create_map(HashMap::new())
    }

    /// Returns how a generic number is represented by this `DynamicOps`.
    fn create_number(&self, n: Number) -> Self::Value;

    create_number_impl!(byte, i8, Byte, create_byte);
    create_number_impl!(short, i16, Short, create_short);
    create_number_impl!(int, i32, Int, create_int);
    create_number_impl!(long, i64, Long, create_long);
    create_number_impl!(float, f32, Float, create_float);
    create_number_impl!(double, f64, Double, create_double);

    /// Returns how a boolean is represented by this `DynamicOps`.
    fn create_bool(&self, data: bool) -> Self::Value;

    /// Returns how a string is represented by this `DynamicOps`.
    fn create_string(&self, data: &str) -> Self::Value;

    /// Returns how a list is represented by this `DynamicOps`.
    fn create_list<I>(&self, values: I) -> Self::Value
    where
        I: IntoIterator<Item = Self::Value>;

    /// Returns how a map is represented by this `DynamicOps`.
    fn create_map<I>(&self, entries: I) -> Self::Value
    where
        I: IntoIterator<Item = (Self::Value, Self::Value)>;

    /// Tries to get a `bool` represented by this `DynamicOps`.
    fn get_bool(&self, input: &Self::Value) -> DataResult<bool>;

    /// Tries to get a number represented by this `DynamicOps`.
    fn get_number(&self, input: &Self::Value) -> DataResult<Number>;

    /// Tries to get a string represented by this `DynamicOps`.
    fn get_string(&self, input: &Self::Value) -> DataResult<String>;

    /// Gets an [`Iterator`] of key-value pairs from a map represented by this `DynamicOps`.
    /// This is only applicable to map-like values.
    fn get_map_iter<'a>(
        &'a self,
        input: &'a Self::Value,
    ) -> DataResult<impl Iterator<Item = (Self::Value, &'a Self::Value)> + 'a>;

    /// Tries to get a [`MapLike`] for a map represented by this `DynamicOps`.
    fn get_map<'a>(
        &self,
        input: &'a Self::Value,
    ) -> DataResult<impl MapLike<Value = Self::Value> + 'a>;

    /// Gets an [`Iterator`] from a generic value represented by this `DynamicOps`.
    /// This is the equivalent of DFU's `getStream()` function, and is only applicable to list-like values.
    fn get_iter(&self, input: Self::Value) -> DataResult<impl Iterator<Item = Self::Value>>;

    /// Gets a `Box<[u8]>` (byte buffer) from a generic value represented by this `DynamicOps`.
    /// This is the equivalent of DFU's `getByteBuffer()` function.
    fn get_byte_list(&self, input: Self::Value) -> DataResult<Vec<i8>> {
        impl_get_list!(self, input, "bytes")
    }

    /// Creates a byte buffer that can be represented by this `DynamicOps` using a [`Vec<u8>`].
    fn create_byte_list(&self, vec: Vec<i8>) -> Self::Value {
        self.create_list(vec.into_iter().map(|b| self.create_byte(b)))
    }

    /// Gets a [`Vec<i32>`] (`int` list) from a generic value represented by this `DynamicOps`.
    /// This is the equivalent of DFU's `getIntStream()` function.
    fn get_int_list(&self, input: Self::Value) -> DataResult<Vec<i32>> {
        impl_get_list!(self, input, "ints")
    }

    /// Creates an `int` list ([`Vec<i32>`]) that can be represented by this `DynamicOps`.
    fn create_int_list(&self, vec: Vec<i32>) -> Self::Value {
        self.create_list(vec.into_iter().map(|i| self.create_int(i)))
    }

    /// Gets a [`Vec<i64>`] (`long` list) from a generic value represented by this `DynamicOps`.
    /// This is the equivalent of DFU's `getLongStream()` function.
    fn get_long_list(&self, input: Self::Value) -> DataResult<Vec<i64>> {
        impl_get_list!(self, input, "longs")
    }

    /// Creates a `long` list ([`Vec<i64>`]) that can be represented by this `DynamicOps`.
    fn create_long_list(&self, vec: Vec<i64>) -> Self::Value {
        self.create_list(vec.into_iter().map(|l| self.create_long(l)))
    }

    /// Merges a value represented by this `DynamicOps` to a list represented by this `DynamicOps`.
    /// This is only valid if `list` is an actual list.
    fn merge_into_list(&self, list: Self::Value, value: Self::Value) -> DataResult<Self::Value>;

    /// Merges a list of values represented by this `DynamicOps` into another such list.
    /// This is only valid if `list` is an actual list.
    fn merge_values_into_list<I>(&self, list: Self::Value, values: I) -> DataResult<Self::Value>
    where
        I: IntoIterator<Item = Self::Value>,
    {
        let mut result = DataResult::new_success(list);

        for value in values {
            result = result.flat_map(|list_value| self.merge_into_list(list_value, value));
        }

        result
    }

    /// Adds a key-value pair, both represented by this `DynamicOps`, to a map also represented by this `DynamicOps`,
    /// returning the new map. This is only valid if `map` is an actual map or is empty.
    fn merge_into_map(
        &self,
        map: Self::Value,
        key: Self::Value,
        value: Self::Value,
    ) -> DataResult<Self::Value>
    where
        Self::Value: Clone;

    /// Merges a map represented by this `DynamicOps` into another such map, returning the new map.
    /// This is only valid if `map` is an actual map or is empty.
    fn merge_entries_into_map<I>(&self, map: Self::Value, entries: I) -> DataResult<Self::Value>
    where
        I: IntoIterator<Item = (Self::Value, Self::Value)>,
        Self::Value: Clone,
    {
        let mut result = DataResult::new_success(map);

        for (key, value) in entries {
            result = result.flat_map(|list_value| self.merge_into_map(list_value, key, value));
        }

        result
    }

    /// Merges a [`MapLike`] represented by this `DynamicOps` into another such map, returning the new map.
    /// This is only valid if `map` is an actual map or is empty.
    fn merge_map_like_into_map<M>(
        &self,
        map: Self::Value,
        other_map_like: M,
    ) -> DataResult<Self::Value>
    where
        M: MapLike<Value = Self::Value>,
        Self::Value: Clone,
    {
        let mut result = DataResult::new_success(map);

        for (key, value) in other_map_like.iter() {
            result =
                result.flat_map(|list_value| self.merge_into_map(list_value, key, value.clone()));
        }

        result
    }

    /// Merges a value represented by this `DynamicOps` into a primitive type.
    fn merge_into_primitive(
        &self,
        prefix: Self::Value,
        value: Self::Value,
    ) -> DataResult<Self::Value>
    where
        <Self as DynamicOps>::Value: PartialEq,
    {
        if prefix == self.empty() {
            DataResult::new_success(value)
        } else {
            DataResult::new_error(format!(
                "Do not know how to append a primitive value {value} to {prefix}"
            ))
        }
    }

    /// Tries to remove something from a value represented by this `DynamicOps` using a key.
    /// This returns the new value if successful, otherwise, this returns itself.
    fn remove(&self, input: Self::Value, key: &str) -> Self::Value;

    /// Tries to get a value from a value represented by this `DynamicOps` using a key.
    /// Only works for values that can be [`MapLike`]-viewed.
    fn get_element<'a>(&'a self, input: &'a Self::Value, key: &str) -> DataResult<&'a Self::Value> {
        self.get_element_generic(input, &self.create_string(key))
    }

    /// Tries to get a value from a value represented by this `DynamicOps` using a key also represented by this `DynamicOps`.
    fn get_element_generic<'a>(
        &'a self,
        input: &'a Self::Value,
        key: &Self::Value,
    ) -> DataResult<&'a Self::Value>
where {
        self.get_map_iter(input).flat_map(|mut iter| {
            iter.find(|(k, _)| k == key).map_or_else(
                || DataResult::new_error(format!("No element {key} in the map")),
                |(_, v)| DataResult::new_success(v),
            )
        })
    }

    /// Tries to set a value represented by this `DynamicOps` to a key to a map also represented by this `DynamicOps`.
    /// - It this was successful, this returns the new map value.
    /// - Otherwise, this simply returns `input`.
    fn set_element(&self, input: &Self::Value, key: &str, value: Self::Value) -> Self::Value
    where
        Self::Value: Clone,
    {
        self.merge_into_map(input.clone(), self.create_string(key), value)
            .into_result()
            .unwrap_or(input.clone())
    }

    /// Tries to update a value represented by this `DynamicOps` of a map also represented by this `DynamicOps`, with
    /// a key and a mapper function (`f`) whose return value will be the new value of the specified key.
    /// - It this was successful, this returns the newly manipulated map.
    /// - Otherwise, this simply returns `input`.
    fn update_element<F>(&self, input: &Self::Value, key: &str, f: F) -> Self::Value
    where
        F: FnOnce(&Self::Value) -> Self::Value,
    {
        self.get_element(input, key)
            .map(|v| self.set_element(input, key, f(v)))
            .into_result()
            .unwrap_or(input.clone())
    }

    /// Tries to update a value represented by this `DynamicOps` of a map also represented by this `DynamicOps`, with
    /// a key also represented by this `DynamicOps` and a mapper function (`f`) whose return value will be the new key's value.
    /// - It this was successful, this returns the newly manipulated map.
    /// - Otherwise, this simply returns `input`.
    fn update_element_generic<F>(&self, input: &Self::Value, key: &Self::Value, f: F) -> Self::Value
    where
        F: FnOnce(&Self::Value) -> Self::Value,
    {
        self.get_element_generic(input, key)
            .flat_map(|v| self.merge_into_map(input.clone(), key.clone(), f(v)))
            .into_result()
            .unwrap_or(input.clone())
    }

    /// Converts a value represented by this `DynamicOps` to another value represented by another `DynamicOps`.
    fn convert_to<U>(&self, out_ops: &impl DynamicOps<Value = U>, input: Self::Value) -> U;

    /// Converts a list represented by this `DynamicOps` to another list represented by another `DynamicOps`.
    fn convert_list<U>(&self, out_ops: &impl DynamicOps<Value = U>, input: Self::Value) -> U {
        out_ops.create_list(
            self.get_iter(input)
                .into_result()
                .into_iter()
                .flatten()
                .map(|v| self.convert_to(out_ops, v)),
        )
    }

    /// Converts a map represented by this `DynamicOps` to another map represented by another `DynamicOps`.
    fn convert_map<U>(&self, out_ops: &impl DynamicOps<Value = U>, input: Self::Value) -> U {
        out_ops.create_map(
            self.get_map_iter(&input)
                .into_result()
                .into_iter()
                .flatten()
                .map(|(k, v)| {
                    (
                        self.convert_to(out_ops, k),
                        self.convert_to(out_ops, v.clone()),
                    )
                }),
        )
    }

    /// Returns a [`ListBuilder`] for this `DynamicOps`.
    fn list_builder(&'static self) -> impl ListBuilder<Value = Self::Value>
    where
        Self: Sized,
    {
        new_list_builder_impl(self)
    }

    /// Returns a [`MapBuilder`] for this `DynamicOps`.
    fn map_builder(&'static self) -> Self::StructBuilder;
}
