use crate::{
    medrecord::querying::{
        group_by::{GroupOperand, GroupedOperand},
        wrapper::{CardinalityWrapper, Wrapper},
        DeepClone,
    },
    prelude::{EdgeDirection, Group, MedRecordAttribute},
};
use medmodels_utils::traits::ReadWriteOrPanic;

pub trait Attribute {
    type ReturnOperand;

    fn attribute(&mut self, attribute: MedRecordAttribute) -> Wrapper<Self::ReturnOperand>;
}

impl<O: Attribute> Wrapper<O> {
    pub fn attribute(&self, attribute: impl Into<MedRecordAttribute>) -> Wrapper<O::ReturnOperand> {
        self.0.write_or_panic().attribute(attribute.into())
    }
}

impl<O: GroupedOperand + Attribute> Attribute for GroupOperand<O>
where
    Self: DeepClone,
    O::ReturnOperand: GroupedOperand,
    <O::ReturnOperand as GroupedOperand>::Context: From<Self>,
{
    type ReturnOperand = GroupOperand<O::ReturnOperand>;

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

        Wrapper::<GroupOperand<O::ReturnOperand>>::new(self.deep_clone().into(), operand)
    }
}

pub trait Attributes {
    type ReturnOperand;

    fn attributes(&mut self) -> Wrapper<Self::ReturnOperand>;
}

impl<O: Attributes> Wrapper<O> {
    pub fn attributes(&self) -> Wrapper<O::ReturnOperand> {
        self.0.write_or_panic().attributes()
    }
}

impl<O: GroupedOperand + Attributes> Attributes for GroupOperand<O>
where
    Self: DeepClone,
    O::ReturnOperand: GroupedOperand,
    <O::ReturnOperand as GroupedOperand>::Context: From<Self>,
{
    type ReturnOperand = GroupOperand<O::ReturnOperand>;

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

        Wrapper::<GroupOperand<O::ReturnOperand>>::new(self.deep_clone().into(), operand)
    }
}

pub trait HasAttribute {
    fn has_attribute<A: Into<CardinalityWrapper<MedRecordAttribute>>>(&mut self, attribute: A);
}

impl<O: HasAttribute> Wrapper<O> {
    pub fn has_attribute<A: Into<CardinalityWrapper<MedRecordAttribute>>>(&self, attribute: A) {
        self.0.write_or_panic().has_attribute(attribute);
    }
}

impl<O: GroupedOperand + HasAttribute> HasAttribute for GroupOperand<O> {
    fn has_attribute<A: Into<CardinalityWrapper<MedRecordAttribute>>>(&mut self, attribute: A) {
        self.operand.has_attribute(attribute);
    }
}

pub trait InGroup {
    fn in_group<G: Into<CardinalityWrapper<Group>>>(&mut self, group: G);
}

impl<O: InGroup> Wrapper<O> {
    pub fn in_group<G: Into<CardinalityWrapper<Group>>>(&self, group: G) {
        self.0.write_or_panic().in_group(group);
    }
}

impl<O: GroupedOperand + InGroup> InGroup for GroupOperand<O> {
    fn in_group<G: Into<CardinalityWrapper<Group>>>(&mut self, group: G) {
        self.operand.in_group(group);
    }
}

pub trait Edges {
    type ReturnOperand;

    fn edges(&mut self, edge_direction: EdgeDirection) -> Wrapper<Self::ReturnOperand>;
}

impl<O: Edges> Wrapper<O> {
    pub fn edges(&self, edge_direction: EdgeDirection) -> Wrapper<O::ReturnOperand> {
        self.0.write_or_panic().edges(edge_direction)
    }
}

impl<O: GroupedOperand + Edges> Edges for GroupOperand<O>
where
    Self: DeepClone,
    O::ReturnOperand: GroupedOperand,
    <O::ReturnOperand as GroupedOperand>::Context: From<Self>,
{
    type ReturnOperand = GroupOperand<O::ReturnOperand>;

    fn edges(&mut self, edge_direction: EdgeDirection) -> Wrapper<Self::ReturnOperand> {
        let operand = self.operand.edges(edge_direction);

        Wrapper::<GroupOperand<O::ReturnOperand>>::new(self.deep_clone().into(), operand)
    }
}

pub trait Neighbors {
    type ReturnOperand;

    fn neighbors(&mut self, edge_direction: EdgeDirection) -> Wrapper<Self::ReturnOperand>;
}

impl<O: Neighbors> Wrapper<O> {
    pub fn neighbors(&self, edge_direction: EdgeDirection) -> Wrapper<O::ReturnOperand> {
        self.0.write_or_panic().neighbors(edge_direction)
    }
}

impl<O: GroupedOperand + Neighbors> Neighbors for GroupOperand<O>
where
    Self: DeepClone,
    O::ReturnOperand: GroupedOperand,
    <O::ReturnOperand as GroupedOperand>::Context: From<Self>,
{
    type ReturnOperand = GroupOperand<O::ReturnOperand>;

    fn neighbors(&mut self, edge_direction: EdgeDirection) -> Wrapper<Self::ReturnOperand> {
        let operand = self.operand.neighbors(edge_direction);

        Wrapper::<GroupOperand<O::ReturnOperand>>::new(self.deep_clone().into(), operand)
    }
}

pub trait SourceNode {
    type ReturnOperand;

    fn source_node(&mut self) -> Wrapper<Self::ReturnOperand>;
}

impl<O: SourceNode> Wrapper<O> {
    pub fn source_node(&self) -> Wrapper<O::ReturnOperand> {
        self.0.write_or_panic().source_node()
    }
}

impl<O: GroupedOperand + SourceNode> SourceNode for GroupOperand<O>
where
    Self: DeepClone,
    O::ReturnOperand: GroupedOperand,
    <O::ReturnOperand as GroupedOperand>::Context: From<Self>,
{
    type ReturnOperand = GroupOperand<O::ReturnOperand>;

    fn source_node(&mut self) -> Wrapper<Self::ReturnOperand> {
        let operand = self.operand.source_node();

        Wrapper::<GroupOperand<O::ReturnOperand>>::new(self.deep_clone().into(), operand)
    }
}

pub trait TargetNode {
    type ReturnOperand;

    fn target_node(&mut self) -> Wrapper<Self::ReturnOperand>;
}

impl<O: TargetNode> Wrapper<O> {
    pub fn target_node(&self) -> Wrapper<O::ReturnOperand> {
        self.0.write_or_panic().target_node()
    }
}

impl<O: GroupedOperand + TargetNode> TargetNode for GroupOperand<O>
where
    Self: DeepClone,
    O::ReturnOperand: GroupedOperand,
    <O::ReturnOperand as GroupedOperand>::Context: From<Self>,
{
    type ReturnOperand = GroupOperand<O::ReturnOperand>;

    fn target_node(&mut self) -> Wrapper<Self::ReturnOperand> {
        let operand = self.operand.target_node();

        Wrapper::<GroupOperand<O::ReturnOperand>>::new(self.deep_clone().into(), operand)
    }
}

pub trait ToValues {
    type ReturnOperand;

    fn to_values(&mut self) -> Wrapper<Self::ReturnOperand>;
}

impl<O: ToValues> Wrapper<O> {
    pub fn to_values(&self) -> Wrapper<O::ReturnOperand> {
        self.0.write_or_panic().to_values()
    }
}

impl<O: GroupedOperand + ToValues> ToValues for GroupOperand<O>
where
    Self: DeepClone,
    O::ReturnOperand: GroupedOperand,
    <O::ReturnOperand as GroupedOperand>::Context: From<Self>,
{
    type ReturnOperand = GroupOperand<O::ReturnOperand>;

    fn to_values(&mut self) -> Wrapper<Self::ReturnOperand> {
        let operand = self.operand.to_values();

        Wrapper::<GroupOperand<O::ReturnOperand>>::new(self.deep_clone().into(), operand)
    }
}
