use super::shacl_parser_error::ShaclParserError;
use iri_s::IriS;
use prefixmap::{IriRef, PrefixMap};
use shacl_ast::reifier_info::ReifierInfo;
use shacl_ast::severity::Severity;
use shacl_ast::shacl_vocab::{
    sh_and, sh_class, sh_closed, sh_datatype, sh_has_value, sh_in, sh_language_in, sh_max_count, sh_max_exclusive,
    sh_max_inclusive, sh_max_length, sh_min_count, sh_min_exclusive, sh_min_inclusive, sh_min_length, sh_node,
    sh_node_kind, sh_node_shape, sh_not, sh_or, sh_pattern, sh_property_shape, sh_qualified_value_shapes_disjoint,
    sh_target_class, sh_target_node, sh_target_objects_of, sh_target_subjects_of, sh_xone,
};
use shacl_ast::{
    component::Component, node_kind::NodeKind, node_shape::NodeShape, property_shape::PropertyShape, schema::Schema,
    shape::Shape, target::Target, value::Value, *,
};
use srdf::{FnOpaque, rdf_type, rdfs_class};
use srdf::{
    FocusRDF, Iri as _, Object, PResult, RDFNode, RDFNodeParse, RDFParseError, RDFParser, Rdf, SHACLPath, SLiteral,
    Term, Triple, combine_parsers, combine_parsers_vec, combine_vec, get_focus, has_type, instances_of, lang::Lang,
    matcher::Any, not, object, ok, opaque, optional, parse_property_values, property_bool, property_integer,
    property_iri, property_iris, property_objects, property_string, property_value, property_value_as_list,
    property_values, property_values_bool, property_values_int, property_values_iri, property_values_literal,
    property_values_non_empty, property_values_string, rdf_list, term,
};
use srdf::{set_focus, shacl_path_parse};
use std::collections::{HashMap, HashSet};
use tracing::trace;

/// Result type for the ShaclParser
type Result<A> = std::result::Result<A, ShaclParserError>;

/// State used during the parsing process
/// This is used to keep track of pending shapes to be parsed
struct State {
    pending: Vec<RDFNode>,
}

impl State {
    fn from(pending: Vec<RDFNode>) -> Self {
        State { pending }
    }

    fn pop_pending(&mut self) -> Option<RDFNode> {
        self.pending.pop()
    }
}

pub struct ShaclParser<RDF>
where
    RDF: FocusRDF,
{
    rdf_parser: RDFParser<RDF>,
    shapes: HashMap<RDFNode, Shape<RDF>>,
}

impl<RDF> ShaclParser<RDF>
where
    RDF: FocusRDF,
{
    pub fn new(rdf: RDF) -> ShaclParser<RDF> {
        ShaclParser {
            rdf_parser: RDFParser::new(rdf),
            shapes: HashMap::new(),
        }
    }

    pub fn parse(&mut self) -> Result<Schema<RDF>> {
        let prefixmap: PrefixMap = self.rdf_parser.prefixmap().unwrap_or_default();

        let mut state = State::from(self.shapes_candidates()?);
        while let Some(node) = state.pop_pending() {
            if let std::collections::hash_map::Entry::Vacant(e) = self.shapes.entry(node.clone()) {
                self.rdf_parser.rdf.set_focus(&node.clone().into());
                let shape = Self::shape(&mut state)
                    .parse_impl(&mut self.rdf_parser.rdf)
                    .map_err(|e| ShaclParserError::RDFParseError { err: e })?;
                e.insert(shape);
            }
        }

        Ok(Schema::new().with_prefixmap(prefixmap).with_shapes(self.shapes.clone()))
    }

    /// Shapes candidates are defined in Appendix A of SHACL spec (Syntax rules)
    /// The text is:
    /// A shape is an IRI or blank node s that fulfills at least one of the following conditions in the shapes graph:
    /// - s is a SHACL instance of sh:NodeShape or sh:PropertyShape.
    /// - s is subject of a triple that has sh:targetClass, sh:targetNode, sh:targetObjectsOf or sh:targetSubjectsOf as predicate.
    /// - s is subject of a triple that has a parameter as predicate.
    /// - s is a value of a shape-expecting, non-list-taking parameter such as sh:node,
    ///   or a member of a SHACL list that is a value of a shape-expecting and list-taking parameter such as sh:or.
    fn shapes_candidates(&mut self) -> Result<Vec<RDFNode>> {
        // instances of `sh:NodeShape`
        let node_shape_instances: HashSet<_> = self
            .rdf_parser
            .rdf
            .triples_matching(Any, Self::rdf_type_iri(), Self::sh_node_shape_iri())
            .map_err(|e| ShaclParserError::Custom { msg: e.to_string() })?
            .map(Triple::into_subject)
            .collect();

        // instances of `sh:PropertyShape`
        let property_shapes_instances: HashSet<_> = self
            .rdf_parser
            .rdf
            .triples_matching(Any, Self::rdf_type_iri(), Self::sh_property_shape_iri())
            .map_err(|e| ShaclParserError::Custom { msg: e.to_string() })?
            .map(Triple::into_subject)
            .collect();

        // Instances of `sh:Shape`
        let shape_instances: HashSet<_> = self
            .rdf_parser
            .rdf
            .triples_matching(Any, Self::rdf_type_iri(), Self::sh_shape_iri())
            .map_err(|e| ShaclParserError::Custom { msg: e.to_string() })?
            .map(Triple::into_subject)
            .collect();

        // Subjects of sh:targetClass
        let subjects_target_class: HashSet<_> = self
            .rdf_parser
            .rdf
            .triples_matching(Any, into_iri::<RDF>(sh_target_class()), Any)
            .map_err(|e| ShaclParserError::Custom { msg: e.to_string() })?
            .map(Triple::into_subject)
            .collect();

        // Subjects of sh:targetSubjectsOf
        let subjects_target_subjects_of: HashSet<_> = self
            .rdf_parser
            .rdf
            .triples_matching(Any, into_iri::<RDF>(sh_target_subjects_of()), Any)
            .map_err(|e| ShaclParserError::Custom { msg: e.to_string() })?
            .map(Triple::into_subject)
            .collect();

        // Subjects of sh:targetObjectsOf
        let subjects_target_objects_of: HashSet<_> = self
            .rdf_parser
            .rdf
            .triples_matching(Any, into_iri::<RDF>(sh_target_objects_of()), Any)
            .map_err(|e| ShaclParserError::Custom { msg: e.to_string() })?
            .map(Triple::into_subject)
            .collect();

        // Subjects of sh:targetNode
        let subjects_target_node: HashSet<_> = self
            .rdf_parser
            .rdf
            .triples_matching(Any, into_iri::<RDF>(sh_target_node()), Any)
            .map_err(|e| ShaclParserError::Custom { msg: e.to_string() })?
            .map(Triple::into_subject)
            .collect();

        // Search shape expecting parameters: https://www.w3.org/TR/shacl12-core/#dfn-shape-expecting
        // elements of `sh:and` list
        let sh_and_values = self.get_sh_and_values()?;

        // elements of `sh:or` list
        let sh_or_values = self.get_sh_or_values()?;

        // elements of `sh:not` list
        let sh_not_values = self.get_sh_not_values()?;

        // subjects with property `sh:property`
        let subjects_property = self.objects_with_predicate(Self::sh_property_iri())?;

        // elements of `sh:node` list
        let sh_qualified_value_shape_nodes = self.get_sh_qualified_value_shape()?;

        // elements of `sh:node` list
        let sh_node_values = self.get_sh_node_values()?;

        // elements of `sh:xone` list
        let sh_xone_values = self.get_sh_xone_values()?;

        // elements of `sh:reifierShape` list
        let sh_reifier_shape_values = self.get_sh_reifier_shape_values()?;

        // I would prefer a code like: node_shape_instances.union(subjects_property).union(...)
        // But looking to the union API in HashSet, I think it can't be chained
        let mut candidates = HashSet::new();
        candidates.extend(node_shape_instances);
        candidates.extend(subjects_property);
        candidates.extend(sh_or_values);
        candidates.extend(sh_xone_values);
        candidates.extend(sh_and_values);
        candidates.extend(sh_not_values);
        candidates.extend(sh_qualified_value_shape_nodes);
        candidates.extend(sh_node_values);
        candidates.extend(property_shapes_instances);
        candidates.extend(sh_reifier_shape_values);
        candidates.extend(shape_instances);
        candidates.extend(subjects_target_class);
        candidates.extend(subjects_target_subjects_of);
        candidates.extend(subjects_target_objects_of);
        candidates.extend(subjects_target_node);

        Ok(subjects_as_nodes::<RDF>(candidates)?)
    }

    fn get_sh_or_values(&mut self) -> Result<HashSet<RDF::Subject>> {
        let mut rs = HashSet::new();
        for subject in self.objects_with_predicate(Self::sh_or_iri())? {
            self.rdf_parser.set_focus(&subject.into());
            let vs = rdf_list().parse_impl(&mut self.rdf_parser.rdf)?;
            for v in vs {
                if let Ok(subj) = term_to_subject::<RDF>(&v, "sh:or") {
                    rs.insert(subj.clone());
                } else {
                    return Err(ShaclParserError::OrValueNoSubject { term: format!("{v}") });
                }
            }
        }
        Ok(rs)
    }

    fn get_sh_xone_values(&mut self) -> Result<HashSet<RDF::Subject>> {
        let mut rs = HashSet::new();
        for subject in self.objects_with_predicate(Self::sh_xone_iri())? {
            self.rdf_parser.set_focus(&subject.into());
            let vs = rdf_list().parse_impl(&mut self.rdf_parser.rdf)?;
            for v in vs {
                if let Ok(subj) = &term_to_subject::<RDF>(&v, "sh:xone") {
                    rs.insert(subj.clone());
                } else {
                    return Err(ShaclParserError::XOneValueNoSubject { term: format!("{v}") });
                }
            }
        }
        Ok(rs)
    }

    fn get_sh_reifier_shape_values(&mut self) -> Result<HashSet<RDF::Subject>> {
        let mut rs = HashSet::new();
        for s in self.objects_with_predicate(Self::sh_reifier_shape_iri())? {
            rs.insert(s);
        }
        Ok(rs)
    }

    fn get_sh_and_values(&mut self) -> Result<HashSet<RDF::Subject>> {
        let mut rs = HashSet::new();
        for subject in self.objects_with_predicate(Self::sh_and_iri())? {
            self.rdf_parser.set_focus(&subject.into());
            let vs = rdf_list().parse_impl(&mut self.rdf_parser.rdf)?;
            for v in vs {
                if let Ok(subj) = term_to_subject::<RDF>(&v, "sh:and") {
                    rs.insert(subj);
                } else {
                    return Err(ShaclParserError::AndValueNoSubject { term: format!("{v}") });
                }
            }
        }
        Ok(rs)
    }

    fn get_sh_not_values(&mut self) -> Result<HashSet<RDF::Subject>> {
        let mut rs = HashSet::new();
        for s in self.objects_with_predicate(Self::sh_not_iri())? {
            rs.insert(s);
        }
        Ok(rs)
    }

    fn get_sh_qualified_value_shape(&mut self) -> Result<HashSet<RDF::Subject>> {
        let mut rs = HashSet::new();
        for s in self.objects_with_predicate(Self::sh_qualified_value_shape_iri())? {
            rs.insert(s);
        }
        Ok(rs)
    }

    fn get_sh_node_values(&mut self) -> Result<HashSet<RDF::Subject>> {
        let mut rs = HashSet::new();
        for s in self.objects_with_predicate(Self::sh_node_iri())? {
            rs.insert(s);
        }
        Ok(rs)
    }

    fn objects_with_predicate(&self, pred: RDF::IRI) -> Result<HashSet<RDF::Subject>> {
        let msg = format!("objects with predicate {pred}");
        let values_as_subjects = self
            .rdf_parser
            .rdf
            .triples_with_predicate(pred)
            .map_err(|e| ShaclParserError::Custom { msg: e.to_string() })?
            .map(Triple::into_object)
            .flat_map(|t| term_to_subject::<RDF>(&t.clone(), msg.as_str()))
            .collect();
        Ok(values_as_subjects)
    }

    fn rdf_type_iri() -> RDF::IRI {
        rdf_type().clone().into()
    }

    fn sh_node_shape_iri() -> RDF::Term {
        RDF::iris_as_term(sh_node_shape())
    }

    fn sh_property_shape_iri() -> RDF::Term {
        RDF::iris_as_term(sh_property_shape())
    }

    fn sh_shape_iri() -> RDF::Term {
        RDF::iris_as_term(sh_shape())
    }

    fn sh_property_iri() -> RDF::IRI {
        sh_property().clone().into()
    }

    fn sh_or_iri() -> RDF::IRI {
        sh_or().clone().into()
    }

    fn sh_xone_iri() -> RDF::IRI {
        sh_xone().clone().into()
    }

    fn sh_reifier_shape_iri() -> RDF::IRI {
        sh_reifier_shape().clone().into()
    }

    fn sh_and_iri() -> RDF::IRI {
        sh_and().clone().into()
    }

    fn sh_not_iri() -> RDF::IRI {
        sh_not().clone().into()
    }

    fn sh_node_iri() -> RDF::IRI {
        into_iri::<RDF>(sh_node())
    }

    fn sh_qualified_value_shape_iri() -> RDF::IRI {
        sh_qualified_value_shape().clone().into()
    }

    fn shape(state: &mut State) -> impl RDFNodeParse<RDF, Output = Shape<RDF>>
    where
        RDF: FocusRDF + 'static,
    {
        node_shape()
            .then(move |ns| ok(Shape::NodeShape(Box::new(ns))))
            .or(property_shape(state).then(|ps| ok(Shape::PropertyShape(Box::new(ps)))))
    }
}

fn components<RDF>() -> impl RDFNodeParse<RDF, Output = Vec<Component>>
where
    RDF: FocusRDF,
{
    // Note: the following code could be replaced by
    // combine_parsers(min_count(), max_count(),...)
    // But we found that the compiler takes too much memory when the number of parsers is large
    combine_parsers_vec(vec![
        // Value type
        class(),
        node_kind(),
        datatype(),
        // Cardinality
        min_count(),
        max_count(),
        // Value range
        min_inclusive(),
        min_exclusive(),
        max_inclusive(),
        max_exclusive(),
        // String based
        min_length(),
        max_length(),
        pattern(),
        // TODO: SHACL 1.2: single line ?
        // single_line(),
        language_in(),
        unique_lang(),
        // SHACL 1.2: List constraint components
        // member_shape(),
        // min_list_length(),
        // max_list_length(),
        // unique_members(),

        // Property pair
        equals(),
        disjoint(),
        less_than(),
        less_than_or_equals(),
        // Logical constraint components
        not_component(),
        and(),
        or(),
        xone(),
        // Shape based constraint components
        node(),
        // property is handled differently
        qualified_value_shape(),
        // Other
        closed_component(),
        has_value(),
        in_component(),
        // SPARQL based constraints and SPARQL based constraint components
        // TODO

        // TODO: deactivated is not a shape component...move this code elsewhere?
        deactivated(),
    ])
}

fn property_shape<RDF>(_state: &mut State) -> impl RDFNodeParse<RDF, Output = PropertyShape<RDF>>
where
    RDF: FocusRDF + 'static,
{
    get_focus().then(move |focus: RDF::Term| {
        optional(has_type(sh_property_shape().clone()))
            .with(
                object()
                    .and(path())
                    .then(move |(id, path)| ok(PropertyShape::new(id, path))),
            )
            // The following line is required because the path parser moves the focus node
            .then(move |ps| set_focus(&focus.clone()).with(ok(ps)))
            .then(|ps| optional(severity()).flat_map(move |sev| Ok(ps.clone().with_severity(sev))))
            .then(|ps| {
                reifier_shape().flat_map(move |r_shape| Ok(ps.clone().with_reifier_shape(r_shape)))
            })
            .then(|ps| targets().flat_map(move |ts| Ok(ps.clone().with_targets(ts))))
            .then(|ps| {
                property_shapes()
                    .flat_map(move |prop_shapes| Ok(ps.clone().with_property_shapes(prop_shapes)))
            })
            .then(move |ps| property_shape_components(ps))
    })
}

fn property_shape_components<RDF>(ps: PropertyShape<RDF>) -> impl RDFNodeParse<RDF, Output = PropertyShape<RDF>>
where
    RDF: FocusRDF,
{
    components().flat_map(move |cs| Ok(ps.clone().with_components(cs)))
}

fn node_shape<RDF>() -> impl RDFNodeParse<RDF, Output = NodeShape<RDF>>
where
    RDF: FocusRDF,
{
    not(property_values_non_empty(sh_path().clone())).with(
        object()
            .then(move |t: RDFNode| ok(NodeShape::new(t)))
            .then(|ns| optional(severity()).flat_map(move |sev| Ok(ns.clone().with_severity(sev))))
            .then(|ns| targets().flat_map(move |ts| Ok(ns.clone().with_targets(ts))))
            .then(|ns| property_shapes().flat_map(move |ps| Ok(ns.clone().with_property_shapes(ps))))
            .then(|ns| components().flat_map(move |cs| Ok(ns.clone().with_components(cs)))),
    )
}

fn severity<RDF: FocusRDF>() -> FnOpaque<RDF, Severity> {
    opaque!(property_iri(sh_severity().clone()).map(|iri| match iri.as_str() {
        "http://www.w3.org/ns/shacl#Violation" => Severity::Violation,
        "http://www.w3.org/ns/shacl#Warning" => Severity::Warning,
        "http://www.w3.org/ns/shacl#Info" => Severity::Info,
        _ => Severity::Generic(IriRef::iri(iri)),
    }))
}

fn property_shapes<RDF: FocusRDF>() -> impl RDFNodeParse<RDF, Output = Vec<RDFNode>> {
    property_objects(sh_property().clone()).map(|ps| ps.into_iter().collect())
}

fn parse_xone_values<RDF: FocusRDF>() -> impl RDFNodeParse<RDF, Output = Component> {
    rdf_list().flat_map(|ls| cnv_xone_list::<RDF>(ls))
}

fn cnv_xone_list<RDF>(ls: Vec<RDF::Term>) -> PResult<Component>
where
    RDF: Rdf,
{
    let shapes: Vec<_> = terms_as_nodes::<RDF>(ls)?; // ls.into_iter().map(|t| t.try_into()).collect();
    Ok(Component::Xone { shapes })
}

fn parse_and_values<RDF: FocusRDF>() -> impl RDFNodeParse<RDF, Output = Component> {
    rdf_list().flat_map(|ls| cnv_and_list::<RDF>(ls))
}

fn cnv_and_list<RDF>(ls: Vec<RDF::Term>) -> PResult<Component>
where
    RDF: Rdf,
{
    let shapes: Vec<_> = terms_as_nodes::<RDF>(ls)?;
    Ok(Component::And { shapes })
}

fn parse_not_value<RDF: FocusRDF>() -> impl RDFNodeParse<RDF, Output = Component> {
    term().flat_map(|t| cnv_not::<RDF>(t))
}

fn parse_node_value<RDF: FocusRDF>() -> impl RDFNodeParse<RDF, Output = Component> {
    term().flat_map(|t| cnv_node::<RDF>(t))
}

fn qualified_value_shape_disjoint_parser<RDF: FocusRDF>() -> FnOpaque<RDF, Option<bool>> {
    opaque!(optional(property_bool(sh_qualified_value_shapes_disjoint().clone())))
}

fn qualified_min_count_parser<RDF: FocusRDF>() -> FnOpaque<RDF, Option<isize>> {
    opaque!(optional(property_integer(sh_qualified_min_count().clone())))
}

fn qualified_max_count_parser<RDF: FocusRDF>() -> FnOpaque<RDF, Option<isize>> {
    opaque!(optional(property_integer(sh_qualified_max_count().clone())))
}

fn parse_qualified_value_shape<RDF: FocusRDF>(
    qvs: HashSet<RDFNode>,
) -> impl RDFNodeParse<RDF, Output = Vec<Component>> {
    qualified_value_shape_disjoint_parser()
        .and(qualified_min_count_parser())
        .and(qualified_max_count_parser())
        .and(qualified_value_shape_siblings())
        .flat_map(move |(((maybe_disjoint, maybe_mins), maybe_maxs), siblings)| {
            Ok(build_qualified_shape(
                qvs.clone(),
                maybe_disjoint,
                maybe_mins,
                maybe_maxs,
                siblings,
            ))
        })
}

fn qualified_value_shape_siblings<RDF: FocusRDF>() -> QualifiedValueShapeSiblings<RDF> {
    QualifiedValueShapeSiblings {
        _marker: std::marker::PhantomData,
        property_qualified_value_shape_path: SHACLPath::sequence(vec![
            SHACLPath::iri(sh_property().clone()),
            SHACLPath::iri(sh_qualified_value_shape().clone()),
        ]),
    }
}

/// This parser gets the siblings of a focus node
/// Siblings are the other qualified value shapes that share the same parent(s)
/// The defnition in the spec is: https://www.w3.org/TR/shacl12-core/#dfn-sibling-shapes
/// "Let Q be a shape in shapes graph G that declares a qualified cardinality constraint
/// (by having values for sh:qualifiedValueShape and at least one of sh:qualifiedMinCount
/// or sh:qualifiedMaxCount).
/// Let ps be the set of shapes in G that have Q as a value of sh:property.
/// If Q has true as a value for sh:qualifiedValueShapesDisjoint then the set of sibling
/// shapes for Q is defined as the set of all values of the SPARQL property path
/// sh:property/sh:qualifiedValueShape for any shape in ps minus the value of
/// sh:qualifiedValueShape of Q itself. The set of sibling shapes is empty otherwise."
struct QualifiedValueShapeSiblings<RDF: FocusRDF> {
    _marker: std::marker::PhantomData<RDF>,
    property_qualified_value_shape_path: SHACLPath,
}

impl<RDF> RDFNodeParse<RDF> for QualifiedValueShapeSiblings<RDF>
where
    RDF: FocusRDF,
{
    type Output = Vec<RDFNode>;

    fn parse_impl(&mut self, rdf: &mut RDF) -> PResult<Self::Output> {
        match rdf.get_focus() {
            Some(focus) => {
                let mut siblings = Vec::new();
                let maybe_disjoint = rdf.object_for(focus, &into_iri::<RDF>(sh_qualified_value_shapes_disjoint()))?;
                if let Some(disjoint) = maybe_disjoint {
                    match disjoint {
                        Object::Literal(SLiteral::Boolean(true)) => {
                            trace!("QualifiedValueShapeSiblings: Focus node {focus} has disjoint=true");
                            let qvs = rdf.objects_for(focus, &into_iri::<RDF>(sh_qualified_value_shape()))?;
                            if qvs.is_empty() {
                                trace!("Focus node {focus} has disjoint=true but no qualifiedValueShape");
                            } else {
                                trace!("QVS of focus node {focus}: {qvs:?}");
                                let ps = rdf.subjects_for(&into_iri::<RDF>(sh_property()), focus)?;
                                trace!("Property parents of focus node {focus}: {ps:?}");
                                for property_parent in ps {
                                    let candidate_siblings = rdf.objects_for_shacl_path(
                                        &property_parent,
                                        &self.property_qualified_value_shape_path,
                                    )?;
                                    trace!("Candidate siblings: {candidate_siblings:?}");
                                    for sibling in candidate_siblings {
                                        if !qvs.contains(&sibling) {
                                            let sibling_node = RDF::term_as_object(&sibling)?;
                                            siblings.push(sibling_node);
                                        }
                                    }
                                }
                            }
                        },
                        Object::Literal(SLiteral::Boolean(false)) => {},
                        _ => {
                            trace!("Value of disjoint: {disjoint} is not boolean (Should we raise an error here?)");
                        },
                    }
                }
                Ok(siblings)
            },
            None => Err(RDFParseError::NoFocusNode),
        }
    }
}

fn build_qualified_shape(
    terms: HashSet<RDFNode>,
    disjoint: Option<bool>,
    q_min_count: Option<isize>,
    q_max_count: Option<isize>,
    siblings: Vec<RDFNode>,
) -> Vec<Component> {
    let mut result = Vec::new();
    for term in terms {
        let shape = Component::QualifiedValueShape {
            shape: term.clone(),
            q_min_count,
            q_max_count,
            disjoint,
            siblings: siblings.clone(),
        };
        result.push(shape);
    }
    result
}

fn cnv_node<RDF>(t: RDF::Term) -> PResult<Component>
where
    RDF: Rdf,
{
    let shape = RDF::term_as_object(&t).map_err(|_| RDFParseError::TermToRDFNodeFailed { term: t.to_string() })?;
    Ok(Component::Node { shape })
}

fn cnv_not<RDF>(t: RDF::Term) -> PResult<Component>
where
    RDF: Rdf,
{
    let shape = RDF::term_as_object(&t).map_err(|_| RDFParseError::TermToRDFNodeFailed { term: t.to_string() })?;
    Ok(Component::Not { shape })
}

fn parse_or_values<RDF: FocusRDF>() -> impl RDFNodeParse<RDF, Output = Component> {
    rdf_list().flat_map(|ls| cnv_or_list::<RDF>(ls))
}

fn cnv_or_list<RDF: Rdf>(ls: Vec<RDF::Term>) -> PResult<Component> {
    let shapes: Vec<_> = terms_as_nodes::<RDF>(ls)?;
    Ok(Component::Or { shapes })
}

fn term_to_subject<RDF>(term: &RDF::Term, context: &str) -> std::result::Result<RDF::Subject, ShaclParserError>
where
    RDF: FocusRDF,
{
    RDF::term_as_subject(term).map_err(|_| ShaclParserError::ExpectedSubject {
        term: term.to_string(),
        context: context.to_string(),
    })
}

fn terms_as_nodes<RDF: Rdf>(terms: Vec<RDF::Term>) -> std::result::Result<Vec<RDFNode>, RDFParseError> {
    terms
        .into_iter()
        .map(|t| {
            let term_name = t.to_string();
            RDF::term_as_object(&t).map_err(|_| RDFParseError::TermToRDFNodeFailed { term: term_name })
        })
        .collect()
}

fn subjects_as_nodes<RDF: Rdf>(subjs: HashSet<RDF::Subject>) -> std::result::Result<Vec<RDFNode>, RDFParseError> {
    subjs
        .into_iter()
        .map(|s| RDF::subject_as_node(&s).map_err(|_| RDFParseError::SubjToRDFNodeFailed { subj: s.to_string() }))
        .collect()
}

/// Parses the property value of the focus node as a SHACL path
fn path<RDF>() -> impl RDFNodeParse<RDF, Output = SHACLPath>
where
    RDF: FocusRDF,
{
    property_value(sh_path().clone()).then(shacl_path_parse)
}

fn targets<RDF>() -> impl RDFNodeParse<RDF, Output = Vec<Target<RDF>>>
where
    RDF: FocusRDF,
{
    combine_parsers!(
        targets_class(),
        targets_node(),
        targets_implicit_class(),
        targets_subjects_of(),
        targets_objects_of()
    )
}

fn closed<RDF>() -> impl RDFNodeParse<RDF, Output = bool>
where
    RDF: FocusRDF,
{
    property_bool(sh_closed().clone())
}

fn min_count<RDF>() -> FnOpaque<RDF, Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(property_values_int(sh_min_count().clone()).map(|ns| ns.iter().map(|n| Component::MinCount(*n)).collect()))
}

fn max_count<RDF>() -> FnOpaque<RDF, Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(property_values_int(sh_max_count().clone()).map(|ns| ns.iter().map(|n| Component::MaxCount(*n)).collect()))
}

fn min_length<RDF>() -> FnOpaque<RDF, Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(
        property_values_int(sh_min_length().clone()).map(|ns| ns.iter().map(|n| Component::MinLength(*n)).collect())
    )
}

fn deactivated<RDF>() -> FnOpaque<RDF, Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(
        property_values_bool(sh_deactivated().clone())
            .map(|ns| ns.iter().map(|n| Component::Deactivated(*n)).collect())
    )
}

fn reifier_shape<RDF>() -> FnOpaque<RDF, Option<ReifierInfo>>
where
    RDF: FocusRDF,
{
    opaque!(property_values(sh_reifier_shape().clone()).then(move |vs| {
        optional(property_bool(sh_reification_required().clone())).map(move |requires_reifier| {
            let reifier_shape = vs.iter().filter_map(|v| RDF::term_as_object(v).ok()).collect();
            if vs.is_empty() {
                None
            } else {
                Some(ReifierInfo::new(requires_reifier.unwrap_or(false), reifier_shape))
            }
        })
    }))
}

fn closed_component<RDF>() -> FnOpaque<RDF, Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(optional(closed()).then(move |maybe_closed| {
        ignored_properties().map(move |is| maybe_closed.map_or(vec![], |b| vec![Component::closed(b, is)]))
    }))
}

fn ignored_properties<RDF>() -> FnOpaque<RDF, HashSet<IriS>>
where
    RDF: FocusRDF,
{
    opaque!(
        optional(property_value_as_list(sh_ignored_properties().clone())).flat_map(|is| {
            match is {
                None => Ok(HashSet::new()),
                Some(vs) => {
                    let mut hs = HashSet::new();
                    for v in vs {
                        if let Ok(iri) = RDF::term_as_iri(&v) {
                            let iri: RDF::IRI = iri;
                            let iri_string = iri.as_str();
                            let iri_s = IriS::new_unchecked(iri_string);
                            hs.insert(iri_s);
                        } else {
                            return Err(RDFParseError::ExpectedIRI { term: v.to_string() });
                        }
                    }
                    Ok(hs)
                },
            }
        })
    )
}

fn min_inclusive<RDF>() -> FnOpaque<RDF, Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(
        property_values_literal(sh_min_inclusive().clone())
            .map(|ns| { ns.iter().map(|lit| Component::MinInclusive(lit.clone())).collect() })
    )
}

fn min_exclusive<RDF>() -> FnOpaque<RDF, Vec<Component>>
// impl RDFNodeParse<RDF, Output = Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(
        property_values_literal(sh_min_exclusive().clone())
            .map(|ns| { ns.iter().map(|lit| Component::MinExclusive(lit.clone())).collect() })
    )
}

fn max_inclusive<RDF>() -> FnOpaque<RDF, Vec<Component>>
// impl RDFNodeParse<RDF, Output = Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(
        property_values_literal(sh_max_inclusive().clone())
            .map(|ns| { ns.iter().map(|lit| Component::MaxInclusive(lit.clone())).collect() })
    )
}

fn max_exclusive<RDF>() -> FnOpaque<RDF, Vec<Component>>
// impl RDFNodeParse<RDF, Output = Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(
        property_values_literal(sh_max_exclusive().clone())
            .map(|ns| { ns.iter().map(|lit| Component::MaxExclusive(lit.clone())).collect() })
    )
}

fn equals<RDF>() -> FnOpaque<RDF, Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(property_values_iri(sh_equals().clone()).map(|ns| {
        ns.iter()
            .map(|n| {
                let iri: IriRef = IriRef::iri(n.clone());
                Component::Equals(iri)
            })
            .collect()
    }))
}

fn disjoint<RDF>() -> FnOpaque<RDF, Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(property_values_iri(sh_disjoint().clone()).map(|ns| {
        ns.iter()
            .map(|n| {
                let iri: IriRef = IriRef::iri(n.clone());
                Component::Disjoint(iri)
            })
            .collect()
    }))
}

fn less_than<RDF>() -> FnOpaque<RDF, Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(property_values_iri(sh_less_than().clone()).map(|ns| {
        ns.iter()
            .map(|n| {
                let iri: IriRef = IriRef::iri(n.clone());
                Component::LessThan(iri)
            })
            .collect()
    }))
}

fn less_than_or_equals<RDF>() -> FnOpaque<RDF, Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(property_values_iri(sh_less_than_or_equals().clone()).map(|ns| {
        ns.iter()
            .map(|n| {
                let iri: IriRef = IriRef::iri(n.clone());
                Component::LessThanOrEquals(iri)
            })
            .collect()
    }))
}

fn max_length<RDF>() -> FnOpaque<RDF, Vec<Component>>
// impl RDFNodeParse<RDF, Output = Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(
        property_values_int(sh_max_length().clone()).map(|ns| ns.iter().map(|n| Component::MaxLength(*n)).collect())
    )
}

fn datatype<RDF>() -> FnOpaque<RDF, Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(property_values_iri(sh_datatype().clone()).map(|ns| {
        ns.iter()
            .map(|iri| Component::Datatype(IriRef::iri(iri.clone())))
            .collect()
    }))
}

fn class<RDF>() -> FnOpaque<RDF, Vec<Component>>
// impl RDFNodeParse<RDF, Output = Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(property_objects(sh_class().clone()).map(|ns| ns.iter().map(|n| Component::Class(n.clone())).collect()))
}

fn node_kind<RDF>() -> FnOpaque<RDF, Vec<Component>>
// impl RDFNodeParse<RDF, Output = Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(property_values(sh_node_kind().clone()).flat_map(|ns| {
        let nks: Vec<_> = ns
            .iter()
            .flat_map(|term| {
                let nk = term_to_node_kind::<RDF>(term)?;
                Ok::<Component, ShaclParserError>(Component::NodeKind(nk))
            })
            .collect();
        Ok(nks)
    }))
}

fn has_value<RDF>() -> FnOpaque<RDF, Vec<Component>>
// impl RDFNodeParse<RDF, Output = Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(parse_components_for_iri(sh_has_value(), parse_has_value_values()))
}

fn in_component<RDF>() -> FnOpaque<RDF, Vec<Component>>
// impl RDFNodeParse<RDF, Output = Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(parse_components_for_iri(sh_in(), parse_in_values()))
}

fn language_in<RDF: FocusRDF>() -> FnOpaque<RDF, Vec<Component>> {
    // impl RDFNodeParse<R, Output = Vec<Component>> {
    opaque!(parse_components_for_iri(sh_language_in(), parse_language_in_values()))
}

fn pattern<RDF: FocusRDF>() -> FnOpaque<RDF, Vec<Component>> {
    opaque!(optional(flags()).then(move |maybe_flags| {
        property_values_string(sh_pattern().clone()).flat_map(move |strs| match strs.len() {
            0 => Ok(Vec::new()),
            1 => {
                let pattern = strs.first().unwrap().clone();
                let flags = maybe_flags.clone();
                Ok(vec![Component::Pattern { pattern, flags }])
            },
            _ => todo!(), // Error...
        })
    }))
}

fn flags<RDF: FocusRDF>() -> impl RDFNodeParse<RDF, Output = String> {
    property_string(sh_flags().clone())
}

fn parse_in_values<RDF>() -> impl RDFNodeParse<RDF, Output = Component>
where
    RDF: FocusRDF,
{
    rdf_list().flat_map(cnv_in_list::<RDF>)
}

fn parse_has_value_values<RDF>() -> impl RDFNodeParse<RDF, Output = Component>
where
    RDF: FocusRDF,
{
    term().flat_map(|t| cnv_has_value::<RDF>(t))
}

fn parse_language_in_values<R: FocusRDF>() -> impl RDFNodeParse<R, Output = Component> {
    rdf_list().flat_map(cnv_language_in_list::<R>)
}

fn cnv_has_value<RDF>(term: RDF::Term) -> std::result::Result<Component, RDFParseError>
where
    RDF: Rdf,
{
    let value = term_to_value::<RDF>(&term, "parsing hasValue")?;
    Ok(Component::HasValue { value })
}

fn cnv_language_in_list<R: FocusRDF>(terms: Vec<R::Term>) -> std::result::Result<Component, RDFParseError> {
    let langs: Vec<Lang> = terms.iter().flat_map(R::term_as_lang).collect();
    Ok(Component::LanguageIn { langs })
}

fn term_to_value<RDF>(term: &RDF::Term, msg: &str) -> std::result::Result<Value, RDFParseError>
where
    RDF: Rdf,
{
    if term.is_blank_node() {
        Err(RDFParseError::BlankNodeNoValue {
            bnode: term.to_string(),
            msg: msg.to_string(),
        })
    } else if let Ok(iri) = RDF::term_as_iri(term) {
        let iri: RDF::IRI = iri;
        let iri_string = iri.as_str();
        let iri_s = IriS::new_unchecked(iri_string);
        Ok(Value::Iri(IriRef::Iri(iri_s)))
    } else if let Ok(literal) = RDF::term_as_literal(term) {
        let literal: RDF::Literal = literal;
        let slit: SLiteral = literal
            .clone()
            .try_into()
            .map_err(|_e| RDFParseError::LiteralToSLiteralFailed {
                literal: literal.to_string(),
            })?;
        Ok(Value::Literal(slit))
    } else {
        println!("Unexpected code in term_to_value: {term}: {msg}");
        todo!()
    }
}

fn cnv_in_list<RDF>(ls: Vec<RDF::Term>) -> std::result::Result<Component, RDFParseError>
where
    RDF: Rdf,
{
    let values = ls
        .iter()
        .flat_map(|t| term_to_value::<RDF>(t, "parsing in list"))
        .collect();
    Ok(Component::In { values })
}

fn parse_components_for_iri<RDF, P>(iri: &IriS, component_parser: P) -> impl RDFNodeParse<RDF, Output = Vec<Component>>
where
    RDF: FocusRDF,
    P: RDFNodeParse<RDF, Output = Component>,
{
    parse_property_values(iri, component_parser)
}

fn or<RDF>() -> FnOpaque<RDF, Vec<Component>>
// impl RDFNodeParse<RDF, Output = Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(parse_components_for_iri(sh_or(), parse_or_values()))
}

fn xone<RDF>() -> FnOpaque<RDF, Vec<Component>>
// impl RDFNodeParse<RDF, Output = Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(parse_components_for_iri(sh_xone(), parse_xone_values()))
}

fn and<RDF>() -> FnOpaque<RDF, Vec<Component>>
// impl RDFNodeParse<RDF, Output = Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(parse_components_for_iri(sh_and(), parse_and_values()))
}

fn not_component<RDF>() -> FnOpaque<RDF, Vec<Component>>
// impl RDFNodeParse<RDF, Output = Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(parse_components_for_iri(sh_not(), parse_not_value()))
}

fn node<RDF>() -> FnOpaque<RDF, Vec<Component>>
// impl RDFNodeParse<RDF, Output = Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(parse_components_for_iri(sh_node(), parse_node_value()))
}

fn qualified_value_shape<RDF>() -> FnOpaque<RDF, Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(
        property_objects(sh_qualified_value_shape().clone()).then(|qvs| { parse_qualified_value_shape::<RDF>(qvs) })
    )
}

fn term_to_node_kind<RDF>(term: &RDF::Term) -> Result<NodeKind>
where
    RDF: Rdf,
{
    let term_name = term.to_string();
    let result_iri: Result<RDF::IRI> =
        <RDF::Term as TryInto<RDF::IRI>>::try_into(term.clone()).map_err(|_| ShaclParserError::ExpectedNodeKind {
            term: term_name.to_string(),
        });
    let iri = result_iri?;
    match iri.as_str() {
        SH_IRI_STR => Ok(NodeKind::Iri),
        SH_LITERAL_STR => Ok(NodeKind::Literal),
        SH_BLANKNODE_STR => Ok(NodeKind::BlankNode),
        SH_BLANK_NODE_OR_IRI_STR => Ok(NodeKind::BlankNodeOrIri),
        SH_BLANK_NODE_OR_LITERAL_STR => Ok(NodeKind::BlankNodeOrLiteral),
        SH_IRI_OR_LITERAL_STR => Ok(NodeKind::IRIOrLiteral),
        _ => Err(ShaclParserError::UnknownNodeKind {
            term: format!("{term}"),
        }),
    }
}

fn targets_class<RDF>() -> FnOpaque<RDF, Vec<Target<RDF>>>
where
    RDF: FocusRDF,
{
    opaque!(property_iris(sh_target_class().clone()).flat_map(move |ts| {
        let result = ts.into_iter().map(|iri| Target::Class(RDFNode::iri(iri))).collect();
        Ok(result)
    }))
}

fn targets_node<RDF>() -> impl RDFNodeParse<RDF, Output = Vec<Target<RDF>>>
where
    RDF: FocusRDF,
{
    property_objects(sh_target_node().clone()).flat_map(move |ts| {
        let result = ts.into_iter().map(Target::Node).collect();
        Ok(result)
    })
}

fn targets_implicit_class<R: FocusRDF>() -> impl RDFNodeParse<R, Output = Vec<Target<R>>> {
    instances_of(rdfs_class().clone())
        .and(instances_of(sh_property_shape().clone()))
        .and(instances_of(sh_node_shape().clone()))
        .and(get_focus())
        .flat_map(move |(((class, property_shapes), node_shapes), focus): (_, R::Term)| {
            let result: std::result::Result<Vec<Target<R>>, RDFParseError> = class
                .into_iter()
                .filter(|t: &R::Subject| property_shapes.contains(t) || node_shapes.contains(t))
                .map(Into::into)
                .filter(|t: &R::Term| t.clone() == focus)
                .map(|t: R::Term| {
                    let t_name = t.to_string();
                    let obj = t
                        .clone()
                        .try_into()
                        .map_err(|_| RDFParseError::TermToRDFNodeFailed { term: t_name })?;
                    Ok(Target::ImplicitClass(obj))
                })
                .collect();
            let ts = result?;
            Ok(ts)
        })
}

fn targets_objects_of<R: FocusRDF>() -> impl RDFNodeParse<R, Output = Vec<Target<R>>> {
    property_values_iri(sh_target_objects_of().clone()).flat_map(move |ts| {
        let result = ts.into_iter().map(|t: IriS| Target::ObjectsOf(t.into())).collect();
        Ok(result)
    })
}

fn targets_subjects_of<R: FocusRDF>() -> impl RDFNodeParse<R, Output = Vec<Target<R>>> {
    property_values_iri(sh_target_subjects_of().clone()).flat_map(move |ts| {
        let result = ts.into_iter().map(|t: IriS| Target::SubjectsOf(t.into())).collect();
        Ok(result)
    })
}

fn unique_lang<RDF>() -> FnOpaque<RDF, Vec<Component>>
where
    RDF: FocusRDF,
{
    opaque!(
        property_values_bool(sh_unique_lang().clone()).map(|ns| ns.iter().map(|n| Component::UniqueLang(*n)).collect())
    )
}

fn into_iri<RDF: Rdf>(iri: &IriS) -> RDF::IRI {
    iri.clone().into()
}

#[cfg(test)]
mod tests {
    use super::ShaclParser;
    use iri_s::IriS;
    use shacl_ast::shape::Shape;
    use srdf::Object;
    use srdf::RDFFormat;
    use srdf::ReaderMode;
    use srdf::SRDFGraph;
    use srdf::lang::Lang;

    #[test]
    fn test_language_in() {
        let shape = r#"
            @prefix :    <http://example.org/> .
            @prefix sh:  <http://www.w3.org/ns/shacl#> .
            @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

            :TestShape a sh:NodeShape ;
                sh:targetNode "Hello"@en ;
                sh:languageIn ( "en" "fr" ) .
        "#;

        let rdf_format = RDFFormat::Turtle;
        let reader_mode = ReaderMode::default();
        let shape_id: Object = IriS::new_unchecked("http://example.org/TestShape").into();

        let graph = SRDFGraph::from_str(shape, &rdf_format, None, &reader_mode).unwrap();
        let schema = ShaclParser::new(graph).parse().unwrap();
        let shape = match schema.get_shape(&shape_id).unwrap() {
            Shape::NodeShape(ns) => ns,
            _ => panic!("Shape is not a NodeShape"),
        };

        match shape.components().first().unwrap() {
            crate::rdf_to_shacl::shacl_parser::Component::LanguageIn { langs } => {
                assert_eq!(langs.len(), 2);
                assert_eq!(langs[0], Lang::new("en").unwrap());
                assert_eq!(langs[1], Lang::new("fr").unwrap());
            },
            _ => panic!("Shape has not a LanguageIn component"),
        }
    }
}
