use super::{
    operation::{EdgeDirection, NodeIndexOperation, NodeIndicesOperation, NodeOperation},
    BinaryArithmeticKind, MultipleComparisonKind, NodeOperandContext, SingleComparisonKind,
    SingleKind, UnaryArithmeticKind,
};
use crate::{
    errors::MedRecordResult,
    medrecord::{
        querying::{
            attributes::{AttributesTreeContext, AttributesTreeOperand},
            edges::{self, EdgeOperand},
            group_by::{GroupKey, GroupOperand, PartitionGroups},
            nodes::{
                group_by::{self, NodeOperandGroupDiscriminator},
                NodeIndicesOperandContext,
            },
            operand_traits::{
                Abs, Add, Attribute, Attributes, Contains, Count, Edges, EitherOr, EndsWith,
                EqualTo, Exclude, GreaterThan, GreaterThanOrEqualTo, HasAttribute, InGroup, Index,
                IsIn, IsInt, IsMax, IsMin, IsNotIn, IsString, LessThan, LessThanOrEqualTo,
                Lowercase, Max, Min, Mod, Mul, Neighbors, NotEqualTo, Pow, Random, Slice,
                StartsWith, Sub, Sum, Trim, TrimEnd, TrimStart, Uppercase,
            },
            values::{self, MultipleValuesWithIndexOperand},
            wrapper::{CardinalityWrapper, Wrapper},
            BoxedIterator, DeepClone, EvaluateBackward, EvaluateForward, EvaluateForwardGrouped,
            GroupedIterator, ReduceInput, RootOperand,
        },
        Group, MedRecordAttribute, NodeIndex,
    },
    prelude::MedRecordValue,
    MedRecord,
};
use medmodels_utils::traits::ReadWriteOrPanic;
use std::{collections::HashSet, fmt::Debug};

#[derive(Debug, Clone)]
pub struct NodeOperand {
    context: Option<NodeOperandContext>,
    operations: Vec<NodeOperation>,
}

impl DeepClone for NodeOperand {
    fn deep_clone(&self) -> Self {
        Self {
            context: self.context.clone(),
            operations: self.operations.deep_clone(),
        }
    }
}

impl RootOperand for NodeOperand {
    type Index = NodeIndex;
    type Discriminator = NodeOperandGroupDiscriminator;

    fn _evaluate_forward<'a>(
        &self,
        medrecord: &'a MedRecord,
        node_indices: BoxedIterator<'a, &'a Self::Index>,
    ) -> MedRecordResult<BoxedIterator<'a, &'a Self::Index>> {
        self.operations
            .iter()
            .try_fold(node_indices, |node_indices, operation| {
                operation.evaluate(medrecord, node_indices)
            })
    }

    fn _evaluate_forward_grouped<'a>(
        &self,
        medrecord: &'a MedRecord,
        node_indices: GroupedIterator<'a, BoxedIterator<'a, &'a Self::Index>>,
    ) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, &'a Self::Index>>> {
        self.operations
            .iter()
            .try_fold(node_indices, |node_indices, operation| {
                operation.evaluate_grouped(medrecord, node_indices)
            })
    }

    fn _evaluate_backward<'a>(
        &self,
        medrecord: &'a MedRecord,
    ) -> MedRecordResult<BoxedIterator<'a, &'a Self::Index>> {
        let node_indices: BoxedIterator<_> = match &self.context {
            Some(NodeOperandContext::Neighbors { operand, direction }) => {
                let node_indices = operand.evaluate_backward(medrecord)?;

                match direction {
                    EdgeDirection::Incoming => Box::new(node_indices.flat_map(move |node_index| {
                        medrecord
                            .neighbors_incoming(node_index)
                            .expect("Node must exist")
                    })),
                    EdgeDirection::Outgoing => Box::new(node_indices.flat_map(move |node_index| {
                        medrecord
                            .neighbors_outgoing(node_index)
                            .expect("Node must exist")
                    })),
                    EdgeDirection::Both => Box::new(node_indices.flat_map(move |node_index| {
                        medrecord
                            .neighbors_undirected(node_index)
                            .expect("Node must exist")
                    })),
                }
            }
            Some(NodeOperandContext::SourceNode { operand }) => {
                let edge_indices = operand.evaluate_backward(medrecord)?;

                Box::new(edge_indices.map(move |edge_index| {
                    medrecord
                        .edge_endpoints(edge_index)
                        .expect("Node must exist")
                        .0
                }))
            }
            Some(NodeOperandContext::TargetNode { operand }) => {
                let edge_indices = operand.evaluate_backward(medrecord)?;

                Box::new(edge_indices.map(move |edge_index| {
                    medrecord
                        .edge_endpoints(edge_index)
                        .expect("Node must exist")
                        .1
                }))
            }
            Some(NodeOperandContext::GroupBy { operand }) => {
                operand.evaluate_backward(medrecord)?
            }
            None => Box::new(medrecord.node_indices()),
        };

        self.evaluate_forward(medrecord, node_indices)
    }

    fn _evaluate_backward_grouped_operand<'a>(
        group_operand: &GroupOperand<Self>,
        medrecord: &'a MedRecord,
    ) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, &'a Self::Index>>> {
        match &group_operand.context {
            group_by::NodeOperandContext::Discriminator(discriminator) => {
                let node_indices = group_operand.operand.evaluate_backward(medrecord)?;

                Ok(Self::partition(medrecord, node_indices, discriminator))
            }
            group_by::NodeOperandContext::Nodes(operand) => {
                let partitions = operand.evaluate_backward(medrecord)?;

                let Some(NodeOperandContext::Neighbors {
                    operand: _,
                    direction,
                }) = &group_operand.operand.0.read_or_panic().context
                else {
                    unreachable!();
                };

                let indices: Vec<_> = partitions
                    .map(|(key, partition)| {
                        let reducued_partition: BoxedIterator<_> = match direction {
                            EdgeDirection::Incoming => {
                                Box::new(partition.flat_map(move |node_index| {
                                    medrecord
                                        .neighbors_incoming(node_index)
                                        .expect("Node must exist")
                                }))
                            }
                            EdgeDirection::Outgoing => {
                                Box::new(partition.flat_map(move |node_index| {
                                    medrecord
                                        .neighbors_outgoing(node_index)
                                        .expect("Node must exist")
                                }))
                            }
                            EdgeDirection::Both => {
                                Box::new(partition.flat_map(move |node_index| {
                                    medrecord
                                        .neighbors_undirected(node_index)
                                        .expect("Node must exist")
                                }))
                            }
                        };

                        let partition = group_operand
                            .operand
                            .evaluate_forward(medrecord, reducued_partition)?;

                        Ok((key, partition))
                    })
                    .collect::<MedRecordResult<_>>()?;

                Ok(Box::new(indices.into_iter()))
            }
            group_by::NodeOperandContext::Edges(operand) => {
                let partitions = operand.evaluate_backward(medrecord)?;

                match &group_operand.operand.0.read_or_panic().context {
                    Some(NodeOperandContext::SourceNode { operand: _ }) => {
                        let indices: Vec<_> = partitions
                            .map(|(key, partition)| {
                                let reduced_partition = partition.map(move |edge_index| {
                                    medrecord
                                        .edge_endpoints(edge_index)
                                        .expect("Node must exist")
                                        .0
                                });

                                let partition = group_operand
                                    .operand
                                    .evaluate_forward(medrecord, Box::new(reduced_partition))?;

                                Ok((key, partition))
                            })
                            .collect::<MedRecordResult<_>>()?;

                        Ok(Box::new(indices.into_iter()))
                    }
                    Some(NodeOperandContext::TargetNode { operand: _ }) => {
                        let indices: Vec<_> = partitions
                            .map(|(key, partition)| {
                                let reduced_partition = partition.map(move |edge_index| {
                                    medrecord
                                        .edge_endpoints(edge_index)
                                        .expect("Node must exist")
                                        .1
                                });

                                let partition = group_operand
                                    .operand
                                    .evaluate_forward(medrecord, Box::new(reduced_partition))?;

                                Ok((key, partition))
                            })
                            .collect::<MedRecordResult<_>>()?;

                        Ok(Box::new(indices.into_iter()))
                    }
                    _ => unreachable!(),
                }
            }
        }
    }

    fn _group_by(&mut self, discriminator: Self::Discriminator) -> Wrapper<GroupOperand<Self>> {
        let node_operand = Wrapper::<Self>::new(Some(NodeOperandContext::GroupBy {
            operand: Box::new(self.deep_clone()),
        }));
        let operand = Wrapper::<GroupOperand<Self>>::new(discriminator.into(), node_operand);

        self.operations.push(NodeOperation::GroupBy {
            operand: operand.clone(),
        });

        operand
    }

    fn _partition<'a>(
        medrecord: &'a MedRecord,
        node_indices: BoxedIterator<'a, &'a Self::Index>,
        discriminator: &Self::Discriminator,
    ) -> GroupedIterator<'a, BoxedIterator<'a, &'a Self::Index>> {
        match discriminator {
            NodeOperandGroupDiscriminator::Attribute(attribute) => {
                let mut buckets: Vec<(Option<&'a MedRecordValue>, Vec<&'a NodeIndex>)> = Vec::new();

                for node_index in node_indices {
                    let value = medrecord
                        .node_attributes(node_index)
                        .expect("Node must exist")
                        .get(attribute);

                    if let Some((_, bucket)) = buckets.iter_mut().find(|(k, _)| *k == value) {
                        bucket.push(node_index);
                    } else {
                        buckets.push((value, vec![node_index]));
                    }
                }

                Box::new(buckets.into_iter().map(|(key, group)| {
                    (
                        GroupKey::OptionalValue(key),
                        Box::new(group.into_iter()) as BoxedIterator<_>,
                    )
                }))
            }
        }
    }

    fn _merge<'a>(
        node_indices: GroupedIterator<'a, BoxedIterator<'a, &'a Self::Index>>,
    ) -> BoxedIterator<'a, &'a Self::Index> {
        Box::new(node_indices.flat_map(|(_, node_indices)| node_indices))
    }
}

impl Attribute for NodeOperand {
    type ReturnOperand = MultipleValuesWithIndexOperand<Self>;

    fn attribute(&mut self, attribute: MedRecordAttribute) -> Wrapper<Self::ReturnOperand> {
        let operand = Wrapper::<Self::ReturnOperand>::new(
            values::MultipleValuesWithIndexContext::Operand((self.deep_clone(), attribute)),
        );

        self.operations.push(NodeOperation::Values {
            operand: operand.clone(),
        });

        operand
    }
}

impl Attributes for NodeOperand {
    type ReturnOperand = AttributesTreeOperand<Self>;

    fn attributes(&mut self) -> Wrapper<Self::ReturnOperand> {
        let operand =
            Wrapper::<Self::ReturnOperand>::new(AttributesTreeContext::Operand(self.deep_clone()));

        self.operations.push(NodeOperation::Attributes {
            operand: operand.clone(),
        });

        operand
    }
}

impl Index for NodeOperand {
    type ReturnOperand = NodeIndicesOperand;

    fn index(&mut self) -> Wrapper<Self::ReturnOperand> {
        let operand = Wrapper::<Self::ReturnOperand>::new(NodeIndicesOperandContext::NodeOperand(
            self.deep_clone(),
        ));

        self.operations.push(NodeOperation::Indices {
            operand: operand.clone(),
        });

        operand
    }
}

impl InGroup for NodeOperand {
    fn in_group<G: Into<CardinalityWrapper<Group>>>(&mut self, group: G) {
        self.operations.push(NodeOperation::InGroup {
            group: group.into(),
        });
    }
}

impl HasAttribute for NodeOperand {
    fn has_attribute<A: Into<CardinalityWrapper<MedRecordAttribute>>>(&mut self, attribute: A) {
        self.operations.push(NodeOperation::HasAttribute {
            attribute: attribute.into(),
        });
    }
}

impl Edges for NodeOperand {
    type ReturnOperand = EdgeOperand;

    fn edges(&mut self, edge_direction: EdgeDirection) -> Wrapper<Self::ReturnOperand> {
        let operand = Wrapper::<Self::ReturnOperand>::new(Some(edges::EdgeOperandContext::Edges {
            operand: Box::new(self.deep_clone()),
            kind: edge_direction.clone(),
        }));

        self.operations.push(NodeOperation::Edges {
            operand: operand.clone(),
            direction: edge_direction,
        });

        operand
    }
}

impl Neighbors for NodeOperand {
    type ReturnOperand = Self;

    fn neighbors(&mut self, edge_direction: EdgeDirection) -> Wrapper<Self::ReturnOperand> {
        let operand = Wrapper::<Self::ReturnOperand>::new(Some(NodeOperandContext::Neighbors {
            operand: Box::new(self.deep_clone()),
            direction: edge_direction.clone(),
        }));

        self.operations.push(NodeOperation::Neighbors {
            operand: operand.clone(),
            direction: edge_direction,
        });

        operand
    }
}

impl EitherOr for NodeOperand {
    type QueryOperand = Self;

    fn either_or<EQ, OQ>(&mut self, either_query: EQ, or_query: OQ)
    where
        EQ: FnOnce(&mut Wrapper<Self::QueryOperand>),
        OQ: FnOnce(&mut Wrapper<Self::QueryOperand>),
    {
        let mut either_operand = Wrapper::<Self::QueryOperand>::new(self.context.clone());
        let mut or_operand = Wrapper::<Self::QueryOperand>::new(self.context.clone());

        either_query(&mut either_operand);
        or_query(&mut or_operand);

        self.operations.push(NodeOperation::EitherOr {
            either: either_operand,
            or: or_operand,
        });
    }
}

impl Exclude for NodeOperand {
    type QueryOperand = Self;

    fn exclude<Q>(&mut self, query: Q)
    where
        Q: FnOnce(&mut Wrapper<Self::QueryOperand>),
    {
        let mut operand = Wrapper::<Self::QueryOperand>::new(self.context.clone());

        query(&mut operand);

        self.operations.push(NodeOperation::Exclude { operand });
    }
}

impl NodeOperand {
    pub(crate) fn new(context: Option<NodeOperandContext>) -> Self {
        Self {
            context,
            operations: Vec::new(),
        }
    }
}

impl Wrapper<NodeOperand> {
    pub(crate) fn new(context: Option<NodeOperandContext>) -> Self {
        NodeOperand::new(context).into()
    }
}

#[derive(Debug, Clone)]
pub enum NodeIndexComparisonOperand {
    Operand(NodeIndexOperand),
    Index(NodeIndex),
}

impl DeepClone for NodeIndexComparisonOperand {
    fn deep_clone(&self) -> Self {
        match self {
            Self::Operand(operand) => Self::Operand(operand.deep_clone()),
            Self::Index(index) => Self::Index(index.clone()),
        }
    }
}

impl From<Wrapper<NodeIndexOperand>> for NodeIndexComparisonOperand {
    fn from(index: Wrapper<NodeIndexOperand>) -> Self {
        Self::Operand(index.0.read_or_panic().deep_clone())
    }
}

impl From<&Wrapper<NodeIndexOperand>> for NodeIndexComparisonOperand {
    fn from(index: &Wrapper<NodeIndexOperand>) -> Self {
        Self::Operand(index.0.read_or_panic().deep_clone())
    }
}

impl<V: Into<NodeIndex>> From<V> for NodeIndexComparisonOperand {
    fn from(index: V) -> Self {
        Self::Index(index.into())
    }
}

impl NodeIndexComparisonOperand {
    pub(crate) fn evaluate_backward(
        &self,
        medrecord: &MedRecord,
    ) -> MedRecordResult<Option<NodeIndex>> {
        match self {
            Self::Operand(operand) => operand.evaluate_backward(medrecord),
            Self::Index(index) => Ok(Some(index.clone())),
        }
    }
}

#[derive(Debug, Clone)]
pub enum NodeIndicesComparisonOperand {
    Operand(NodeIndicesOperand),
    Indices(HashSet<NodeIndex>),
}

impl DeepClone for NodeIndicesComparisonOperand {
    fn deep_clone(&self) -> Self {
        match self {
            Self::Operand(operand) => Self::Operand(operand.deep_clone()),
            Self::Indices(indices) => Self::Indices(indices.clone()),
        }
    }
}

impl From<Wrapper<NodeIndicesOperand>> for NodeIndicesComparisonOperand {
    fn from(indices: Wrapper<NodeIndicesOperand>) -> Self {
        Self::Operand(indices.0.read_or_panic().deep_clone())
    }
}

impl From<&Wrapper<NodeIndicesOperand>> for NodeIndicesComparisonOperand {
    fn from(indices: &Wrapper<NodeIndicesOperand>) -> Self {
        Self::Operand(indices.0.read_or_panic().deep_clone())
    }
}

impl<V: Into<NodeIndex>> From<Vec<V>> for NodeIndicesComparisonOperand {
    fn from(indices: Vec<V>) -> Self {
        Self::Indices(indices.into_iter().map(Into::into).collect())
    }
}

impl<V: Into<NodeIndex>> From<HashSet<V>> for NodeIndicesComparisonOperand {
    fn from(indices: HashSet<V>) -> Self {
        Self::Indices(indices.into_iter().map(Into::into).collect())
    }
}

impl<V: Into<NodeIndex> + Clone, const N: usize> From<[V; N]> for NodeIndicesComparisonOperand {
    fn from(indices: [V; N]) -> Self {
        indices.to_vec().into()
    }
}

impl NodeIndicesComparisonOperand {
    pub(crate) fn evaluate_backward(
        &self,
        medrecord: &MedRecord,
    ) -> MedRecordResult<HashSet<NodeIndex>> {
        Ok(match self {
            Self::Operand(operand) => operand.evaluate_backward(medrecord)?.collect(),
            Self::Indices(indices) => indices.clone(),
        })
    }
}

#[derive(Debug, Clone)]
pub struct NodeIndicesOperand {
    context: NodeIndicesOperandContext,
    operations: Vec<NodeIndicesOperation>,
}

impl DeepClone for NodeIndicesOperand {
    fn deep_clone(&self) -> Self {
        Self {
            context: self.context.clone(),
            operations: self.operations.iter().map(DeepClone::deep_clone).collect(),
        }
    }
}

impl<'a> EvaluateForward<'a> for NodeIndicesOperand {
    type InputValue = BoxedIterator<'a, NodeIndex>;
    type ReturnValue = BoxedIterator<'a, NodeIndex>;

    fn evaluate_forward(
        &self,
        medrecord: &'a MedRecord,
        node_indices: Self::InputValue,
    ) -> MedRecordResult<Self::ReturnValue> {
        let node_indices: BoxedIterator<_> = Box::new(node_indices);

        self.operations
            .iter()
            .try_fold(node_indices, |index_tuples, operation| {
                operation.evaluate(medrecord, index_tuples)
            })
    }
}

impl<'a> EvaluateForwardGrouped<'a> for NodeIndicesOperand {
    fn evaluate_forward_grouped(
        &self,
        medrecord: &'a MedRecord,
        node_indices: GroupedIterator<'a, Self::InputValue>,
    ) -> MedRecordResult<GroupedIterator<'a, Self::ReturnValue>> {
        self.operations
            .iter()
            .try_fold(node_indices, |index_tuples, operation| {
                operation.evaluate_grouped(medrecord, index_tuples)
            })
    }
}

impl<'a> EvaluateBackward<'a> for NodeIndicesOperand {
    type ReturnValue = BoxedIterator<'a, NodeIndex>;

    fn evaluate_backward(&self, medrecord: &'a MedRecord) -> MedRecordResult<Self::ReturnValue> {
        let node_indices = self.context.evaluate_backward(medrecord)?;

        self.evaluate_forward(medrecord, Box::new(node_indices))
    }
}

impl<'a> ReduceInput<'a> for NodeIndicesOperand {
    type Context = NodeOperand;

    #[inline]
    fn reduce_input(
        &self,
        node_indices: <Self::Context as EvaluateBackward<'a>>::ReturnValue,
    ) -> MedRecordResult<<Self as EvaluateForward<'a>>::InputValue> {
        Ok(Box::new(node_indices.cloned()))
    }
}

impl Max for NodeIndicesOperand {
    type ReturnOperand = NodeIndexOperand;

    fn max(&mut self) -> Wrapper<Self::ReturnOperand> {
        let operand = Wrapper::<Self::ReturnOperand>::new(self.deep_clone(), SingleKind::Max);

        self.operations
            .push(NodeIndicesOperation::NodeIndexOperation {
                operand: operand.clone(),
            });

        operand
    }
}

impl Min for NodeIndicesOperand {
    type ReturnOperand = NodeIndexOperand;

    fn min(&mut self) -> Wrapper<Self::ReturnOperand> {
        let operand = Wrapper::<Self::ReturnOperand>::new(self.deep_clone(), SingleKind::Min);

        self.operations
            .push(NodeIndicesOperation::NodeIndexOperation {
                operand: operand.clone(),
            });

        operand
    }
}

impl Count for NodeIndicesOperand {
    type ReturnOperand = NodeIndexOperand;

    fn count(&mut self) -> Wrapper<Self::ReturnOperand> {
        let operand = Wrapper::<Self::ReturnOperand>::new(self.deep_clone(), SingleKind::Count);

        self.operations
            .push(NodeIndicesOperation::NodeIndexOperation {
                operand: operand.clone(),
            });

        operand
    }
}

impl Sum for NodeIndicesOperand {
    type ReturnOperand = NodeIndexOperand;

    fn sum(&mut self) -> Wrapper<Self::ReturnOperand> {
        let operand = Wrapper::<Self::ReturnOperand>::new(self.deep_clone(), SingleKind::Sum);

        self.operations
            .push(NodeIndicesOperation::NodeIndexOperation {
                operand: operand.clone(),
            });

        operand
    }
}

impl Random for NodeIndicesOperand {
    type ReturnOperand = NodeIndexOperand;

    fn random(&mut self) -> Wrapper<Self::ReturnOperand> {
        let operand = Wrapper::<Self::ReturnOperand>::new(self.deep_clone(), SingleKind::Random);

        self.operations
            .push(NodeIndicesOperation::NodeIndexOperation {
                operand: operand.clone(),
            });

        operand
    }
}

impl GreaterThan for NodeIndicesOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn greater_than<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndicesOperation::NodeIndexComparisonOperation {
                operand: value.into(),
                kind: SingleComparisonKind::GreaterThan,
            });
    }
}

impl GreaterThanOrEqualTo for NodeIndicesOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn greater_than_or_equal_to<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndicesOperation::NodeIndexComparisonOperation {
                operand: value.into(),
                kind: SingleComparisonKind::GreaterThanOrEqualTo,
            });
    }
}

impl LessThan for NodeIndicesOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn less_than<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndicesOperation::NodeIndexComparisonOperation {
                operand: value.into(),
                kind: SingleComparisonKind::LessThan,
            });
    }
}

impl LessThanOrEqualTo for NodeIndicesOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn less_than_or_equal_to<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndicesOperation::NodeIndexComparisonOperation {
                operand: value.into(),
                kind: SingleComparisonKind::LessThanOrEqualTo,
            });
    }
}

impl EqualTo for NodeIndicesOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn equal_to<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndicesOperation::NodeIndexComparisonOperation {
                operand: value.into(),
                kind: SingleComparisonKind::EqualTo,
            });
    }
}

impl NotEqualTo for NodeIndicesOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn not_equal_to<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndicesOperation::NodeIndexComparisonOperation {
                operand: value.into(),
                kind: SingleComparisonKind::NotEqualTo,
            });
    }
}

impl StartsWith for NodeIndicesOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn starts_with<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndicesOperation::NodeIndexComparisonOperation {
                operand: value.into(),
                kind: SingleComparisonKind::StartsWith,
            });
    }
}

impl EndsWith for NodeIndicesOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn ends_with<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndicesOperation::NodeIndexComparisonOperation {
                operand: value.into(),
                kind: SingleComparisonKind::EndsWith,
            });
    }
}

impl Contains for NodeIndicesOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn contains<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndicesOperation::NodeIndexComparisonOperation {
                operand: value.into(),
                kind: SingleComparisonKind::Contains,
            });
    }
}

impl IsIn for NodeIndicesOperand {
    type ComparisonOperand = NodeIndicesComparisonOperand;

    fn is_in<V: Into<Self::ComparisonOperand>>(&mut self, indices: V) {
        self.operations
            .push(NodeIndicesOperation::NodeIndicesComparisonOperation {
                operand: indices.into(),
                kind: MultipleComparisonKind::IsIn,
            });
    }
}

impl IsNotIn for NodeIndicesOperand {
    type ComparisonOperand = NodeIndicesComparisonOperand;

    fn is_not_in<V: Into<Self::ComparisonOperand>>(&mut self, indices: V) {
        self.operations
            .push(NodeIndicesOperation::NodeIndicesComparisonOperation {
                operand: indices.into(),
                kind: MultipleComparisonKind::IsNotIn,
            });
    }
}

impl Add for NodeIndicesOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn add<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndicesOperation::BinaryArithmeticOperation {
                operand: value.into(),
                kind: BinaryArithmeticKind::Add,
            });
    }
}

impl Sub for NodeIndicesOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn sub<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndicesOperation::BinaryArithmeticOperation {
                operand: value.into(),
                kind: BinaryArithmeticKind::Sub,
            });
    }
}

impl Mul for NodeIndicesOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn mul<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndicesOperation::BinaryArithmeticOperation {
                operand: value.into(),
                kind: BinaryArithmeticKind::Mul,
            });
    }
}

impl Pow for NodeIndicesOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn pow<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndicesOperation::BinaryArithmeticOperation {
                operand: value.into(),
                kind: BinaryArithmeticKind::Pow,
            });
    }
}

impl Mod for NodeIndicesOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn r#mod<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndicesOperation::BinaryArithmeticOperation {
                operand: value.into(),
                kind: BinaryArithmeticKind::Mod,
            });
    }
}

impl Abs for NodeIndicesOperand {
    fn abs(&mut self) {
        self.operations
            .push(NodeIndicesOperation::UnaryArithmeticOperation {
                kind: UnaryArithmeticKind::Abs,
            });
    }
}

impl Trim for NodeIndicesOperand {
    fn trim(&mut self) {
        self.operations
            .push(NodeIndicesOperation::UnaryArithmeticOperation {
                kind: UnaryArithmeticKind::Trim,
            });
    }
}

impl TrimStart for NodeIndicesOperand {
    fn trim_start(&mut self) {
        self.operations
            .push(NodeIndicesOperation::UnaryArithmeticOperation {
                kind: UnaryArithmeticKind::TrimStart,
            });
    }
}

impl TrimEnd for NodeIndicesOperand {
    fn trim_end(&mut self) {
        self.operations
            .push(NodeIndicesOperation::UnaryArithmeticOperation {
                kind: UnaryArithmeticKind::TrimEnd,
            });
    }
}

impl Lowercase for NodeIndicesOperand {
    fn lowercase(&mut self) {
        self.operations
            .push(NodeIndicesOperation::UnaryArithmeticOperation {
                kind: UnaryArithmeticKind::Lowercase,
            });
    }
}

impl Uppercase for NodeIndicesOperand {
    fn uppercase(&mut self) {
        self.operations
            .push(NodeIndicesOperation::UnaryArithmeticOperation {
                kind: UnaryArithmeticKind::Uppercase,
            });
    }
}

impl Slice for NodeIndicesOperand {
    fn slice(&mut self, start: usize, end: usize) {
        self.operations
            .push(NodeIndicesOperation::Slice(start..end));
    }
}

impl IsString for NodeIndicesOperand {
    fn is_string(&mut self) {
        self.operations.push(NodeIndicesOperation::IsString);
    }
}

impl IsInt for NodeIndicesOperand {
    fn is_int(&mut self) {
        self.operations.push(NodeIndicesOperation::IsInt);
    }
}

impl IsMax for NodeIndicesOperand {
    fn is_max(&mut self) {
        self.operations.push(NodeIndicesOperation::IsMax);
    }
}

impl IsMin for NodeIndicesOperand {
    fn is_min(&mut self) {
        self.operations.push(NodeIndicesOperation::IsMin);
    }
}

impl EitherOr for NodeIndicesOperand {
    type QueryOperand = Self;

    fn either_or<EQ, OQ>(&mut self, either_query: EQ, or_query: OQ)
    where
        EQ: FnOnce(&mut Wrapper<Self::QueryOperand>),
        OQ: FnOnce(&mut Wrapper<Self::QueryOperand>),
    {
        let mut either_operand = Wrapper::<Self::QueryOperand>::new(self.context.clone());
        let mut or_operand = Wrapper::<Self::QueryOperand>::new(self.context.clone());

        either_query(&mut either_operand);
        or_query(&mut or_operand);

        self.operations.push(NodeIndicesOperation::EitherOr {
            either: either_operand,
            or: or_operand,
        });
    }
}

impl Exclude for NodeIndicesOperand {
    type QueryOperand = Self;

    fn exclude<Q>(&mut self, query: Q)
    where
        Q: FnOnce(&mut Wrapper<Self::QueryOperand>),
    {
        let mut operand = Wrapper::<Self::QueryOperand>::new(self.context.clone());

        query(&mut operand);

        self.operations
            .push(NodeIndicesOperation::Exclude { operand });
    }
}

impl NodeIndicesOperand {
    pub(crate) fn new(context: NodeIndicesOperandContext) -> Self {
        Self {
            context,
            operations: Vec::new(),
        }
    }

    pub(crate) fn push_merge_operation(&mut self, operand: Wrapper<NodeIndicesOperand>) {
        self.operations
            .push(NodeIndicesOperation::Merge { operand });
    }
}

impl Wrapper<NodeIndicesOperand> {
    pub(crate) fn new(context: NodeIndicesOperandContext) -> Self {
        NodeIndicesOperand::new(context).into()
    }

    pub(crate) fn push_merge_operation(&self, operand: Wrapper<NodeIndicesOperand>) {
        self.0.write_or_panic().push_merge_operation(operand);
    }
}

#[derive(Debug, Clone)]
pub struct NodeIndexOperand {
    context: NodeIndicesOperand,
    pub(crate) kind: SingleKind,
    operations: Vec<NodeIndexOperation>,
}

impl DeepClone for NodeIndexOperand {
    fn deep_clone(&self) -> Self {
        Self {
            context: self.context.deep_clone(),
            kind: self.kind.clone(),
            operations: self.operations.iter().map(DeepClone::deep_clone).collect(),
        }
    }
}

impl<'a> EvaluateForward<'a> for NodeIndexOperand {
    type InputValue = Option<NodeIndex>;
    type ReturnValue = Option<NodeIndex>;

    fn evaluate_forward(
        &self,
        medrecord: &'a MedRecord,
        node_index: Self::InputValue,
    ) -> MedRecordResult<Self::ReturnValue> {
        self.operations
            .iter()
            .try_fold(node_index, |node_index, operation| {
                operation.evaluate(medrecord, node_index)
            })
    }
}

impl<'a> EvaluateForwardGrouped<'a> for NodeIndexOperand {
    fn evaluate_forward_grouped(
        &self,
        medrecord: &'a MedRecord,
        node_indices: GroupedIterator<'a, Self::InputValue>,
    ) -> MedRecordResult<GroupedIterator<'a, Self::ReturnValue>> {
        self.operations
            .iter()
            .try_fold(node_indices, |node_indices, operation| {
                operation.evaluate_grouped(medrecord, node_indices)
            })
    }
}

impl<'a> EvaluateBackward<'a> for NodeIndexOperand {
    type ReturnValue = Option<NodeIndex>;

    fn evaluate_backward(&self, medrecord: &'a MedRecord) -> MedRecordResult<Self::ReturnValue> {
        let node_indices = self.context.evaluate_backward(medrecord)?;

        let node_index = self.reduce_input(node_indices)?;

        self.evaluate_forward(medrecord, node_index)
    }
}

impl<'a> ReduceInput<'a> for NodeIndexOperand {
    type Context = NodeIndicesOperand;

    #[inline]
    fn reduce_input(
        &self,
        node_indices: <Self::Context as EvaluateBackward<'a>>::ReturnValue,
    ) -> MedRecordResult<<Self as EvaluateForward<'a>>::InputValue> {
        Ok(match self.kind {
            SingleKind::Max => NodeIndicesOperation::get_max(node_indices)?,
            SingleKind::Min => NodeIndicesOperation::get_min(node_indices)?,
            SingleKind::Count => Some(NodeIndicesOperation::get_count(node_indices)),
            SingleKind::Sum => NodeIndicesOperation::get_sum(node_indices)?,
            SingleKind::Random => NodeIndicesOperation::get_random(node_indices),
        })
    }
}

impl GreaterThan for NodeIndexOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn greater_than<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndexOperation::NodeIndexComparisonOperation {
                operand: value.into(),
                kind: SingleComparisonKind::GreaterThan,
            });
    }
}

impl GreaterThanOrEqualTo for NodeIndexOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn greater_than_or_equal_to<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndexOperation::NodeIndexComparisonOperation {
                operand: value.into(),
                kind: SingleComparisonKind::GreaterThanOrEqualTo,
            });
    }
}

impl LessThan for NodeIndexOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn less_than<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndexOperation::NodeIndexComparisonOperation {
                operand: value.into(),
                kind: SingleComparisonKind::LessThan,
            });
    }
}

impl LessThanOrEqualTo for NodeIndexOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn less_than_or_equal_to<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndexOperation::NodeIndexComparisonOperation {
                operand: value.into(),
                kind: SingleComparisonKind::LessThanOrEqualTo,
            });
    }
}

impl EqualTo for NodeIndexOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn equal_to<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndexOperation::NodeIndexComparisonOperation {
                operand: value.into(),
                kind: SingleComparisonKind::EqualTo,
            });
    }
}

impl NotEqualTo for NodeIndexOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn not_equal_to<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndexOperation::NodeIndexComparisonOperation {
                operand: value.into(),
                kind: SingleComparisonKind::NotEqualTo,
            });
    }
}

impl StartsWith for NodeIndexOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn starts_with<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndexOperation::NodeIndexComparisonOperation {
                operand: value.into(),
                kind: SingleComparisonKind::StartsWith,
            });
    }
}

impl EndsWith for NodeIndexOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn ends_with<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndexOperation::NodeIndexComparisonOperation {
                operand: value.into(),
                kind: SingleComparisonKind::EndsWith,
            });
    }
}

impl Contains for NodeIndexOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn contains<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndexOperation::NodeIndexComparisonOperation {
                operand: value.into(),
                kind: SingleComparisonKind::Contains,
            });
    }
}

impl IsIn for NodeIndexOperand {
    type ComparisonOperand = NodeIndicesComparisonOperand;

    fn is_in<V: Into<Self::ComparisonOperand>>(&mut self, indices: V) {
        self.operations
            .push(NodeIndexOperation::NodeIndicesComparisonOperation {
                operand: indices.into(),
                kind: MultipleComparisonKind::IsIn,
            });
    }
}

impl IsNotIn for NodeIndexOperand {
    type ComparisonOperand = NodeIndicesComparisonOperand;

    fn is_not_in<V: Into<Self::ComparisonOperand>>(&mut self, indices: V) {
        self.operations
            .push(NodeIndexOperation::NodeIndicesComparisonOperation {
                operand: indices.into(),
                kind: MultipleComparisonKind::IsNotIn,
            });
    }
}

impl Add for NodeIndexOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn add<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndexOperation::BinaryArithmeticOperation {
                operand: value.into(),
                kind: BinaryArithmeticKind::Add,
            });
    }
}

impl Sub for NodeIndexOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn sub<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndexOperation::BinaryArithmeticOperation {
                operand: value.into(),
                kind: BinaryArithmeticKind::Sub,
            });
    }
}

impl Mul for NodeIndexOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn mul<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndexOperation::BinaryArithmeticOperation {
                operand: value.into(),
                kind: BinaryArithmeticKind::Mul,
            });
    }
}

impl Pow for NodeIndexOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn pow<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndexOperation::BinaryArithmeticOperation {
                operand: value.into(),
                kind: BinaryArithmeticKind::Pow,
            });
    }
}

impl Mod for NodeIndexOperand {
    type ComparisonOperand = NodeIndexComparisonOperand;

    fn r#mod<V: Into<Self::ComparisonOperand>>(&mut self, value: V) {
        self.operations
            .push(NodeIndexOperation::BinaryArithmeticOperation {
                operand: value.into(),
                kind: BinaryArithmeticKind::Mod,
            });
    }
}

impl Abs for NodeIndexOperand {
    fn abs(&mut self) {
        self.operations
            .push(NodeIndexOperation::UnaryArithmeticOperation {
                kind: UnaryArithmeticKind::Abs,
            });
    }
}

impl Trim for NodeIndexOperand {
    fn trim(&mut self) {
        self.operations
            .push(NodeIndexOperation::UnaryArithmeticOperation {
                kind: UnaryArithmeticKind::Trim,
            });
    }
}

impl TrimStart for NodeIndexOperand {
    fn trim_start(&mut self) {
        self.operations
            .push(NodeIndexOperation::UnaryArithmeticOperation {
                kind: UnaryArithmeticKind::TrimStart,
            });
    }
}

impl TrimEnd for NodeIndexOperand {
    fn trim_end(&mut self) {
        self.operations
            .push(NodeIndexOperation::UnaryArithmeticOperation {
                kind: UnaryArithmeticKind::TrimEnd,
            });
    }
}

impl Lowercase for NodeIndexOperand {
    fn lowercase(&mut self) {
        self.operations
            .push(NodeIndexOperation::UnaryArithmeticOperation {
                kind: UnaryArithmeticKind::Lowercase,
            });
    }
}

impl Uppercase for NodeIndexOperand {
    fn uppercase(&mut self) {
        self.operations
            .push(NodeIndexOperation::UnaryArithmeticOperation {
                kind: UnaryArithmeticKind::Uppercase,
            });
    }
}

impl Slice for NodeIndexOperand {
    fn slice(&mut self, start: usize, end: usize) {
        self.operations.push(NodeIndexOperation::Slice(start..end));
    }
}

impl IsString for NodeIndexOperand {
    fn is_string(&mut self) {
        self.operations.push(NodeIndexOperation::IsString);
    }
}

impl IsInt for NodeIndexOperand {
    fn is_int(&mut self) {
        self.operations.push(NodeIndexOperation::IsInt);
    }
}

impl EitherOr for NodeIndexOperand {
    type QueryOperand = Self;

    fn either_or<EQ, OQ>(&mut self, either_query: EQ, or_query: OQ)
    where
        EQ: FnOnce(&mut Wrapper<Self::QueryOperand>),
        OQ: FnOnce(&mut Wrapper<Self::QueryOperand>),
    {
        let mut either_operand =
            Wrapper::<Self::QueryOperand>::new(self.context.clone(), self.kind.clone());
        let mut or_operand =
            Wrapper::<Self::QueryOperand>::new(self.context.clone(), self.kind.clone());

        either_query(&mut either_operand);
        or_query(&mut or_operand);

        self.operations.push(NodeIndexOperation::EitherOr {
            either: either_operand,
            or: or_operand,
        });
    }
}

impl Exclude for NodeIndexOperand {
    type QueryOperand = Self;

    fn exclude<Q>(&mut self, query: Q)
    where
        Q: FnOnce(&mut Wrapper<Self::QueryOperand>),
    {
        let mut operand =
            Wrapper::<Self::QueryOperand>::new(self.context.clone(), self.kind.clone());

        query(&mut operand);

        self.operations
            .push(NodeIndexOperation::Exclude { operand });
    }
}

impl NodeIndexOperand {
    pub(crate) fn new(context: NodeIndicesOperand, kind: SingleKind) -> Self {
        Self {
            context,
            kind,
            operations: Vec::new(),
        }
    }

    pub(crate) fn push_merge_operation(&mut self, operand: Wrapper<NodeIndicesOperand>) {
        self.operations.push(NodeIndexOperation::Merge { operand });
    }
}

impl Wrapper<NodeIndexOperand> {
    pub(crate) fn new(context: NodeIndicesOperand, kind: SingleKind) -> Self {
        NodeIndexOperand::new(context, kind).into()
    }

    pub(crate) fn push_merge_operation(&self, operand: Wrapper<NodeIndicesOperand>) {
        self.0.write_or_panic().push_merge_operation(operand);
    }
}
