use crate::{Shape, ShapeExprLabel};
use iri_s::IriS;
use prefixmap::IriRef;
use serde::{Deserialize, Serialize};

use super::{Schema, ShapeDecl, ShapeExpr, TripleExpr, ValueSetValue};

#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
pub struct SimpleReprSchema {
    shapes: Vec<SimpleReprShape>,
}

impl Default for SimpleReprSchema {
    fn default() -> Self {
        Self::new()
    }
}

impl SimpleReprSchema {
    pub fn new() -> SimpleReprSchema {
        SimpleReprSchema { shapes: Vec::new() }
    }

    pub fn populate_from_schema(&mut self, schema: &Schema) {
        if let Some(shapes) = schema.shapes() {
            for shape in shapes {
                let simple_shape = self.convert_shape_decl(&shape, schema);
                self.shapes.push(simple_shape)
            }
        }
    }

    pub fn convert_shape_decl(&mut self, shape_decl: &ShapeDecl, schema: &Schema) -> SimpleReprShape {
        self.convert_shape_expr(&shape_decl.id, &shape_decl.shape_expr, schema)
    }

    pub fn convert_shape_expr(&mut self, name: &ShapeExprLabel, shape: &ShapeExpr, schema: &Schema) -> SimpleReprShape {
        match shape {
            ShapeExpr::ShapeOr { shape_exprs: _ } => todo!(),
            ShapeExpr::ShapeAnd { shape_exprs: _ } => todo!(),
            ShapeExpr::ShapeNot { shape_expr: _ } => todo!(),
            ShapeExpr::NodeConstraint(_) => todo!(),
            ShapeExpr::Shape(shape) => self.convert_shape(name, shape, schema),
            ShapeExpr::External => todo!(),
            ShapeExpr::Ref(_) => todo!(),
        }
    }

    pub fn convert_shape(&mut self, name: &ShapeExprLabel, shape: &Shape, schema: &Schema) -> SimpleReprShape {
        let mut simple = SimpleReprShape::new(name);
        if let Some(triple_expr) = &shape.expression {
            self.convert_triple_expr(&mut simple, &triple_expr.te, schema);
        }
        simple
    }

    pub fn convert_triple_expr(&mut self, shape: &mut SimpleReprShape, te: &TripleExpr, schema: &Schema) {
        match te {
            TripleExpr::EachOf {
                id: _,
                expressions,
                min: _,
                max: _,
                sem_acts: _,
                annotations: _,
            } => {
                for te in expressions {
                    self.convert_triple_expr(shape, &(te.te), schema);
                }
            },
            TripleExpr::OneOf {
                id: _,
                expressions,
                min: _,
                max: _,
                sem_acts: _,
                annotations: _,
            } => {
                for te in expressions {
                    self.convert_triple_expr(shape, &te.te, schema);
                }
            },
            TripleExpr::TripleConstraint {
                id: _,
                negated: _,
                inverse: _,
                predicate,
                value_expr,
                min: _,
                max: _,
                sem_acts: _,
                annotations: _,
            } => {
                let iri = schema.resolve_iriref(predicate);
                if iri == IriS::rdf_type() {
                    if let Some(se) = value_expr {
                        self.extract_class_values(se, shape);
                    }
                } else {
                    shape.add_predicate(predicate)
                }
            },
            TripleExpr::Ref(_) => todo!(),
        }
    }

    fn extract_class_values(&mut self, se: &ShapeExpr, shape: &mut SimpleReprShape) {
        match se {
            ShapeExpr::NodeConstraint(nc) => {
                if let Some(values) = nc.values() {
                    for value in values {
                        match value {
                            ValueSetValue::ObjectValue(ov) => match ov {
                                super::ObjectValue::IriRef(iri_ref) => shape.add_class(&iri_ref),
                                super::ObjectValue::Literal(_) => todo!(),
                            },
                            _ => todo!(),
                        }
                    }
                }
            },
            _ => todo!(),
        }
    }
}

#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
pub struct SimpleReprShape {
    #[serde(skip_serializing)]
    id: ShapeExprLabel,

    #[serde(skip_serializing_if = "Vec::is_empty")]
    class: Vec<IriRef>,

    predicates: Vec<IriRef>,
}

impl SimpleReprShape {
    pub fn new(label: &ShapeExprLabel) -> SimpleReprShape {
        SimpleReprShape {
            id: label.clone(),
            class: Vec::new(),
            predicates: Vec::new(),
        }
    }

    pub fn add_predicate(&mut self, pred: &IriRef) {
        self.predicates.push(pred.clone())
    }

    pub fn add_class(&mut self, cls: &IriRef) {
        self.class.push(cls.clone())
    }
}
