use std::io;

use iri_s::IriS;
use shacl_ast::Schema;
use shacl_ir::compiled_shacl_error::CompiledShaclError;
use sparql_service::RdfData;
use srdf::SRDFSparql;
use thiserror::Error;

use crate::data_format::DataFormatError;

#[derive(Error, Debug)]
pub enum RudofError {
    #[error("Data format error: {error}")]
    DataFormatError { error: DataFormatError },
    #[error("Error qualifying object {object}: {error}")]
    QualifyObject { object: String, error: String },
    #[error("Error qualifying subject {subject}: {error}")]
    QualifySubject { subject: String, error: String },
    #[error("Error resolving node {node}: {error}")]
    NodeResolveError { node: String, error: String },
    #[error("Error parsing IRI reference {iri}: {error}")]
    IriRefParseError { iri: String, error: String },
    #[error("Error resolving predicate {predicate}: {error}")]
    PredicateResolveError { predicate: String, error: String },
    #[error("Node {node} is a literal, expected an IRI or a blank node")]
    LiteralNotSubject { node: String },
    #[error("Node {node} is not a subject in the RDF data")]
    NodeNotSubject { node: String },
    #[error("Node {node} is not in the RDF data")]
    NodeNotFound { node: String },
    #[error("Error obtaining incoming arcs for object {object}: {error}")]
    IncomingArcs { object: String, error: String },
    #[error("Error obtaining outgoing arcs for subject {subject}: {error}")]
    OutgoingArcs { subject: String, error: String },
    #[error("Invalid mode: {mode}. Expected one of: {expected}")]
    InvalidMode { mode: String, expected: String },
    #[error("Error parsing node selector {node_selector}: {error}")]
    NodeSelectorParseError { node_selector: String, error: String },
    #[error("Error parsing shape selector {shape_selector}: {error}")]
    ShapeSelectorParseError { shape_selector: String, error: String },
    #[error("Error formatting node info: {error}")]
    NodeInfoFormatError { error: String },

    #[error("Reading Rudof config: {error}\nContent:\n{str}")]
    RudofConfigFromStrError { str: String, error: String },

    #[error("Error configuring RDF data: {error}")]
    RdfDataConfigError { error: String },

    #[error(transparent)]
    IOErro(#[from] io::Error),

    #[error("Invalid shape label {label}: {error}")]
    InvalidShapeLabel { label: String, error: String },

    #[error("Error obtaining current directory: {error}")]
    CurrentDirError { error: String },

    #[error("Error converting current dir {current_dir} to URL")]
    ConvertingCurrentFolderUrl { current_dir: String },

    #[error("Parsing IRI from {source_name}: {error}")]
    SourceNameIriError { source_name: String, error: String },
    #[error("SPARQL syntax error reading {source_name}: {error}")]
    SparqlSyntaxError { error: String, source_name: String },

    #[error("Invalid endpoint IRI {endpoint}: {error}")]
    InvalidEndpointIri { endpoint: String, error: String },

    #[error("Invalid endpoint {endpoint}: {error}")]
    InvalidEndpoint { endpoint: String, error: String },

    #[error("Error running query against endpoint {endpoint}.\nQuery:\n{query}\nError: {error}")]
    QueryEndpointError {
        endpoint: String,
        error: String,
        query: String,
    },

    #[error("Parsing URL {url} reading service description: {error}")]
    ParsingUrlReadingServiceDescriptionUrl { url: String, error: String },

    #[error("Obtaining content from URL {url} reading service description: {error}")]
    ReadingServiceDescriptionUrl { url: String, error: String },

    #[error("{error}")]
    Generic { error: String },

    #[error("Common Shapes Model conversion error from ShEx: {error}")]
    CoShaMoFromShExError { schema: String, error: String },

    #[error("Invalid compare schema format: {format}: {error}")]
    InvalidCompareSchemaFormat { format: String, error: String },

    #[error("RDF Config read error: {error}")]
    RdfConfigReadError { error: String },

    #[error("Compiling SHACL: {error}")]
    ShaclCompilation { error: Box<CompiledShaclError> },

    #[error("Error reading config file from path {path}: {error}")]
    RudofConfigFromPathError { path: String, error: io::Error },

    #[error("Error reading config file from path {path}: {error}")]
    RudofConfigTomlError { path: String, error: toml::de::Error },

    #[error("Error running query {str}: {error}")]
    QueryError { str: String, error: String },

    #[error("Storage error: {error}")]
    StorageError { error: String },

    #[error("Error parsing IRI from {str}: {error}")]
    BaseIriError { str: String, error: String },

    #[error("ShEx compact parser error from {source_name}: {error}")]
    ShExCParserError { error: String, source_name: String },

    #[error("ShEx JSON parser error from {source_name}: {error}")]
    ShExJParserError { error: String, source_name: String },

    #[error("Compiling schema error: {error}")]
    CompilingSchemaError { error: String },

    #[error(
        "ShEx Validator undefined. Before trying to validate with ShEx, a ShEx validator must be initialized in rudof"
    )]
    ShExValidatorUndefined {},

    #[error("Error creating schema for ShEx validation. Schema:\n{schema}\nError: {error} ")]
    ShExValidatorCreationError { schema: String, error: String },

    #[error("ShEx validation error: {error} ")]
    ShExValidatorError {
        schema: String,
        rdf_data: String,
        query_map: String,
        error: String,
    },

    #[error("ShEx validation error. Error: {error} ")]
    ShExValidatorObtainingResultMapError {
        schema: String,
        rdf_data: String,
        shapemap: String,
        error: String,
    },

    #[error("Obtaining nodes from node_selector: {node_selector}: {error}")]
    NodeSelectorError { node_selector: String, error: String },

    #[error("Converting term {term} to subject: {error}")]
    Term2Subject { term: String, error: String },

    #[error(
        "Error obtaining RDF data from {source_name}, format: {format}, base: {base}, reader_mode: {reader_mode}: {error} "
    )]
    MergeRDFDataFromReader {
        source_name: String,
        format: String,
        base: String,
        reader_mode: String,
        error: String,
    },

    #[error("Utf8 error: {error} ")]
    Utf8Error { error: String },

    #[error("Shapemap parse error from {source_name}\nSource map read:\n{str}\nError: {error}")]
    ShapeMapParseError {
        source_name: String,
        str: String,
        error: String,
    },

    #[error("Read error: {error} ")]
    ReadError { error: String },

    #[error("Error reading SHACL from {reader} with format {format}: {error} ")]
    ReadingSHACLError {
        reader: String,
        error: String,
        format: String,
    },

    #[error("Error reading SHACL from graph obtained from {reader_name} with format {format}: {error} ")]
    ReadingSHACLFromGraphError {
        error: String,
        format: String,
        reader_name: String,
    },

    #[error("AddingEndpoint: {iri} ")]
    AddingEndpointError { iri: IriS, error: String },

    #[error("Validating shex requires to initialize a shapemap or a node/shape pair")]
    NoShapeMap { schema: String },

    #[error("Resolving imports in ShEx schema: {error}")]
    ResolvingImportsShExSchema { error: String },

    #[error("Attempt to resolve import declarations without defining ShEx schema")]
    NoShExSchemaForResolvingImports,

    #[error("Internal SHACL Format is not readable. Only for output")]
    InternalSHACLFormatNonReadable,

    #[error("SHACL Parser error: {error}")]
    SHACLParseError { error: String },

    #[error("SHACL Compilation from schema {schema} error: {error}")]
    SHACLCompilationError {
        error: String,
        schema: Box<Schema<RdfData>>,
    },

    #[error("SHACL Validation from schema {schema} error: {error}")]
    SHACLValidationError {
        error: String,
        schema: Box<Schema<RdfData>>,
    },

    #[error("Creating Endpoint validation for SHACL from endpoint {endpoint:?}. error: {error}")]
    SHACLEndpointValidationCreation { error: String, endpoint: Box<SRDFSparql> },

    #[error("Parsing RDF data error: {error}")]
    ParsingRDFDataReader { error: String },

    #[error("No graph and no first endpoint to validate SHACL")]
    NoGraphNoFirstEndpoint,

    #[error("No SHACL schema defined")]
    NoShaclSchema,

    #[error("Cannot serialize current ShEx schema because it has not been defined")]
    NoShExSchemaToSerialize,

    #[error("No DCTAP defined")]
    NoDCTAP,

    #[error("ShEx2UML: No ShEx schema")]
    ShEx2UmlWithoutShEx,

    #[error("ShEx2PlantUML Error: {error}")]
    ShEx2PlantUmlError { error: String },

    #[error("RDF2PlantUML Error: {error}")]
    RDF2PlantUmlError { error: String },

    #[error("ShEx2PlantUML Error when generating PlantUML: {error}")]
    ShEx2PlantUmlErrorAsPlantUML { error: String },

    #[error("RDF2PlantUML Error when generating PlantUML: {error}")]
    RDF2PlantUmlErrorAsPlantUML { error: String },

    #[error("Reading ShEx Schema from path: {path}: {error}")]
    ReadingShExPath { path: String, error: String },

    #[error("Reading {context} from {url}: {error}")]
    ReadingUrlContext {
        url: String,
        error: String,
        context: String,
    },

    #[error("Obtaining {context} from input {input}: {error}")]
    ParsingInputSpecContext {
        input: String,
        error: String,
        context: String,
    },

    #[error("No SPARQL query has been defined")]
    NoCurrentSPARQLQuery,

    #[error("The current SPARQL query is not a SELECT query, it is:\n{query}")]
    NotSelectQuery { query: String },

    #[error("The current SPARQL query is not a SELECT or CONSTRUCT query, it is:\n{query}")]
    NotConstructQuery { query: String },

    #[error("Reading {context} from input {input}: {error}")]
    ReadingInputSpecContext {
        input: String,
        error: String,
        context: String,
    },

    #[error("Reading {context}. Parsing {url}: {error}")]
    ParsingUrlContext {
        url: String,
        error: String,
        context: String,
    },

    #[error("Reading {context} from path: {path}: {error}")]
    ReadingPathContext {
        path: String,
        error: String,
        context: String,
    },

    #[error("Error formatting schema {schema}: {error}")]
    ErrorFormattingSchema { schema: String, error: String },

    #[error("Error formatting shapemap {shapemap}: {error}")]
    ErrorFormattingShapeMap { shapemap: String, error: String },

    #[error("Error formatting schema: {error}")]
    ErrorWritingShExJson { schema: String, error: String },

    #[error("Not implemented yet: {msg}")]
    NotImplemented { msg: String },

    #[error("Unsupported SHACL result format: {format}")]
    UnsupportedShaclResultFormat { format: String },

    #[error("Unsupported sort mode for SHACL validation report: {mode}")]
    UnsupportedShaclSortMode { mode: String },

    #[error("Unsupported SHACL format: {format}")]
    UnsupportedShaclFormat { format: String },

    #[error("Unsupported ShapeMap format: {format}")]
    UnsupportedShapeMapFormat { format: String },

    #[error("Unsupported ShapeMap sort mode: {mode}")]
    UnsupportedShapeMapSortMode { mode: String },

    #[error("Unsupported ShEx result format: {format}")]
    UnsupportedShExResultFormat { format: String },

    #[error("Conversion of result ShEx validation format to ShapeMapFormat not implemented: {format}")]
    UnsupportedShExToShapeMapConversion { format: String },

    #[error("Unsupported ShEx format: {format}")]
    UnsupportedShExFormat { format: String },

    #[error("Cannot serialize current ShapeMap because it has not been defined")]
    NoShapeMapToSerialize,

    #[error("Cannot serialize current SHACL because it has not been defined")]
    NoShaclToSerialize,

    #[error("Converting SHACLFormat with value Internal to RDFFormat")]
    NoInternalFormatForRDF,

    #[error("Serializing SHACL to internal representation: {error}")]
    SerializingSHACLInternal { error: String },

    #[error("PrefixMap error: {error}")]
    PrefixMapError { error: String },

    #[error("Writing SHACL {shacl}: {error}")]
    WritingSHACL { shacl: String, error: String },

    #[error("Serializing SHACL {shacl}: {error}")]
    SerializingSHACL { shacl: String, error: String },

    #[error("Serializing ShEx: {error}")]
    SerializingShEx { error: String },

    #[error("Serializing RDF data: {error}")]
    SerializingData { error: String },

    #[error("Serializing ShEx: {error}")]
    SerializingShacl { error: String },

    #[error("DCTAP reader error. Path {path}, format {format}: {error}")]
    DCTAPReader {
        error: String,
        format: String,
        path: String,
    },

    #[error("DCTAP reader from path {path}: {error}")]
    ReadingDCTAPPath { error: String, path: String },

    #[error("DCTAP reader in CSV format: {error}")]
    DCTAPReaderCSVReader { error: String },

    #[error("DCTAP reader from path {path} in format {format}: {error}")]
    DCTAPReaderPathXLS {
        error: String,
        path: String,
        format: String,
    },

    #[error("Reading DCTAP from XLS format requires a Path, use read_dctap_path")]
    DCTAPReadXLSNoPath,

    #[error("Error converting DCTAP to ShEx")]
    DCTap2ShEx { error: String },

    #[error("Serializing Service Description: {error}")]
    SerializingServiceDescription { error: String },

    #[error("Cannot serialize current Service Description because it has not been defined")]
    NoServiceDescriptionToSerialize,

    #[error("Reading Service Description: {error}")]
    ReadingServiceDescription { error: String },

    #[error("Reading Service Description from path {path}: {error}")]
    ReadingServiceDescriptionPath { path: String, error: String },

    #[error("None of `data` or `endpoint` parameters have been specified for validation")]
    MissingDataAndEndpoint,

    #[error("Only one of 'data' or 'endpoint' supported at the same time")]
    BothDataAndEndpointSpecified,

    #[error("Invalid image format: {format}. Must be 'SVG' or 'PNG'")]
    InvalidImageFormat { format: String },

    #[error("Error reading RDF data from {source_name} using mime_type {mime_type}: {error}")]
    RDFDataReadError {
        source_name: String,
        error: String,
        mime_type: String,
    },

    #[error("Error using endpoint {endpoint}: {error}")]
    EndpointUseError { endpoint: String, error: String },

    #[error("Error parsing query result format {format}: {error}")]
    QueryResultFormatParseError { format: String, error: String },

    #[error("RDF error: {error}")]
    RdfError { error: String },

    #[error("WASM error: {0}")]
    WASMError(String),
}
