use crate::ObjectValue;
use crate::SchemaJsonError;
use prefixmap::IriRef;
use rbe::Value;
use rudof_iri::IriS;
use rudof_rdf::rdf_core::{
    RDFError,
    term::{
        Object,
        literal::{ConcreteLiteral, NumericLiteral},
    },
};
use serde::{Deserialize, Serialize};
use std::fmt::Display;

impl Value for Node {}

#[derive(PartialEq, Eq, Hash, Debug, Default, Serialize, Deserialize, Clone, PartialOrd, Ord)]
#[serde(transparent)]
pub struct Node {
    node: Object,
}

impl Node {
    pub fn new(object: Object) -> Self {
        Self { node: object }
    }
    /// Creates a node from an [`ÌriS`]
    pub fn iri(iri: IriS) -> Node {
        Node { node: Object::iri(iri) }
    }

    /// Returns the length of the RDF Node
    pub fn length(&self) -> usize {
        self.node.length()
    }

    /// Returns the numeric value of a node if it is a numeric literal
    pub fn numeric_value(&self) -> Option<NumericLiteral> {
        self.node.numeric_value()
    }

    pub fn as_object(&self) -> &Object {
        &self.node
    }

    /// Returns the inner Object but checks if it is well-formed
    /// This is important in the case of literals like `"hi"^^xsd:integer` which can declare that they are of type integers
    /// but have a lexical form that is not an integer
    /// In that case, this function will return a WrongDatatypeLiteral
    pub fn as_checked_object(&self) -> Result<Object, SchemaJsonError> {
        match &self.node {
            Object::Literal(sliteral) => {
                let checked_literal = sliteral
                    .clone()
                    .into_checked_literal()
                    .map_err(|e| SchemaJsonError::LiteralError { error: e.to_string() })?;
                Ok(Object::Literal(checked_literal))
            },
            _ => Ok(self.node.clone()),
        }
    }

    pub fn literal(lit: ConcreteLiteral) -> Node {
        Node {
            node: Object::literal(lit),
        }
    }

    pub fn datatype(&self) -> Option<IriRef> {
        self.node.datatype()
    }

    pub fn parse(str: &str, base: Option<&str>) -> Result<Node, RDFError> {
        let obj = Object::parse(str, base)?;
        Ok(Node { node: obj })
    }

    pub fn show_qualified(&self, prefixmap: &prefixmap::PrefixMap) -> String {
        self.node.show_qualified(prefixmap)
    }
}

impl Display for Node {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.node)
    }
}

impl From<Object> for Node {
    fn from(node: Object) -> Self {
        Node { node }
    }
}

impl From<IriS> for Node {
    fn from(iri: IriS) -> Self {
        Node { node: iri.into() }
    }
}

impl TryFrom<&Node> for ObjectValue {
    type Error = crate::SchemaJsonError;

    fn try_from(node: &Node) -> Result<Self, Self::Error> {
        match &node.node {
            Object::Iri(iri) => Ok(ObjectValue::IriRef(IriRef::iri(iri.clone()))),
            Object::Literal(lit) => Ok(ObjectValue::Literal(lit.clone())),
            Object::BlankNode(bnode_id) => Err(crate::SchemaJsonError::InvalidNodeInObjectValue {
                node: node.to_string(),
                error: format!("Blank node _:{bnode_id}"),
            }),
            Object::Triple { .. } => Err(SchemaJsonError::InvalidNodeInObjectValue {
                node: node.to_string(),
                error: "RDF triples are not supported in ObjectValue".to_string(),
            }),
        }
    }
}
