from __future__ import annotations

import json
from pathlib import Path

from rdflib import Dataset
from rdflib import Graph
from pyshacl import validate as shacl_validate

from cf_ontology import OntologyManager
from cf_ontology.namespaces import CF_BASE


def _project_root() -> Path:
    return Path(__file__).resolve().parents[1]


def _statement_count(dataset: Dataset) -> int:
    return sum(len(graph) for graph in dataset.graphs())


def _valid_pipeline_document() -> dict:
    return {
        "@context": {
            "cf": CF_BASE,
            "ex": "https://example.org/",
        },
        "@graph": [
            {
                "@id": "https://example.org/pipelines/demo",
                "@type": "cf:ProcessingPipeline",
                "cf:hasStepsHeader": {"@id": "https://example.org/pipelines/demo/steps"},
                "cf:hasPluginsHeader": {"@id": "https://example.org/pipelines/demo/plugins"},
            },
            {
                "@id": "https://example.org/pipelines/demo/steps",
                "@type": "cf:StepsHeader",
                "cf:stepsPath": "./steps.nq",
            },
            {
                "@id": "https://example.org/pipelines/demo/plugins",
                "@type": "cf:PluginsHeader",
                "cf:pluginPath": "./bin",
            },
        ],
    }


def _invalid_pipeline_document() -> dict:
    return {
        "@context": {
            "cf": CF_BASE,
            "ex": "https://example.org/",
        },
        "@graph": [
            {
                "@id": "https://example.org/pipelines/demo",
                "@type": "cf:ProcessingPipeline",
                "cf:hasStepsHeader": {"@id": "https://example.org/pipelines/demo/steps"},
            },
            {
                "@id": "https://example.org/pipelines/demo/steps",
                "@type": "cf:StepsHeader",
                "cf:stepsPath": "./steps.nq",
            },
        ],
    }


def should_conform_when_pipeline_document_has_required_headers(
    _isolate_semantics: Path,
) -> None:
    manager = OntologyManager()

    result = manager.validate_pipeline_document(_valid_pipeline_document())

    assert result["conforms"] is True


def should_report_violations_when_pipeline_document_is_missing_a_required_header(
    _isolate_semantics: Path,
) -> None:
    manager = OntologyManager()

    result = manager.validate_pipeline_document(_invalid_pipeline_document())

    assert result["conforms"] is False
    assert "Constraint Violation" in str(result["report_text"])


def should_validate_packaged_ontology_when_running_ontology_manager_validation(
    _isolate_semantics: Path,
) -> None:
    manager = OntologyManager()

    result = manager.validate_ontology()

    assert result["conforms"] is True


def should_parse_packaged_shape_nquads_when_loading_shape_resources() -> None:
    shapes_root = _project_root() / "src" / "cf_ontology" / "ontology" / "shapes"
    failures: list[str] = []
    shape_paths = sorted(shapes_root.glob("*.nq"))

    for path in shape_paths:
        dataset = Dataset()
        dataset.parse(str(path), format="nquads")
        if _statement_count(dataset) == 0:
            failures.append(str(path))

    assert shape_paths
    assert not failures, f"Packaged shape files were empty after parsing: {failures}"


def should_reject_missing_scope_note_on_documented_step_resources(
    _isolate_semantics: Path,
) -> None:
    shapes_path = _project_root() / "src" / "cf_ontology" / "ontology" / "shapes" / "step_packages.shapes.nq"
    shapes_graph = Graph().parse(str(shapes_path), format="nquads")
    data_graph = Graph().parse(
        data=json.dumps(
            {
                "@context": {
                    "cf": CF_BASE,
                    "skos": "http://www.w3.org/2004/02/skos/core#",
                    "ex": "https://example.org/",
                },
                "@graph": [
                    {
                        "@id": "https://example.org/step",
                        "@type": "cf:ProcessingStep",
                        "skos:prefLabel": "Example Step",
                        "skos:definition": "Example definition.",
                        "skos:example": "Example usage.",
                        "cf:belongsToStepPackage": {"@id": "https://example.org/package"},
                        "cf:hasImplementation": {"@id": "https://example.org/impl"},
                    },
                    {
                        "@id": "https://example.org/package",
                        "@type": "cf:StepPackage",
                        "cf:packageId": "example.package",
                        "cf:packageVersion": "1.0.0",
                        "cf:pythonModule": "example_package",
                        "skos:prefLabel": "Example Package",
                        "skos:definition": "Package definition.",
                        "skos:scopeNote": "Package scope.",
                        "skos:example": "Package example.",
                    },
                    {
                        "@id": "https://example.org/impl",
                        "@type": "cf:StepImplementation",
                        "cf:implementationEntrypoint": "example:run",
                        "skos:prefLabel": "Example Implementation",
                        "skos:definition": "Implementation definition.",
                        "skos:scopeNote": "Implementation scope.",
                        "skos:example": "Implementation example.",
                    },
                ],
            }
        ),
        format="json-ld",
    )

    conforms, _, _ = shacl_validate(
        data_graph,
        shacl_graph=shapes_graph,
        inference="rdfs",
        abort_on_first=False,
        advanced=True,
    )

    assert bool(conforms) is False
