from __future__ import annotations

import json
from pathlib import Path

import pytest
from rdflib import Dataset

from cf_ontology import OntologyManager, ingest_rdf_documents
from cf_ontology.namespaces import CF_BASE


_RDF_FORMAT = "json" + "-ld"
_PACKAGE_IRI = "https://example.org/packages/DemoPackage"
_STEP_IRI = "https://example.org/steps/DemoStep"
_PIPELINE_IRI = "https://example.org/pipelines/DemoPipeline"


def _write_nquads(path: Path, document: dict) -> Path:
    dataset = Dataset()
    dataset.parse(data=json.dumps(document), format=_RDF_FORMAT)
    payload = dataset.serialize(format="nquads")
    if isinstance(payload, bytes):
        payload = payload.decode("utf-8")
    path.write_text(payload, encoding="utf-8")
    return path


def _step_document() -> dict:
    return {
        "@context": {
            "cf": CF_BASE,
            "ex": "https://example.org/",
        },
        "@graph": [
            {
                "@id": _PACKAGE_IRI,
                "@type": "cf:StepPackage",
                "cf:packageId": "demo.pkg",
                "cf:packageVersion": "1.2.3",
            },
            {
                "@id": _STEP_IRI,
                "@type": "cf:ProcessingStep",
                "cf:belongsToStepPackage": {"@id": _PACKAGE_IRI},
            },
        ],
    }


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


def _seed_manager(
    tmp_path: Path,
    semantics_dir: Path,
    monkeypatch: pytest.MonkeyPatch,
    *,
    include_pipeline: bool,
) -> OntologyManager:
    monkeypatch.setenv("CF_ENABLE_STEP_PACKAGES", "0")
    step_path = _write_nquads(tmp_path / "steps.nq", _step_document())
    ingest_rdf_documents(
        [step_path],
        semantics_dir=semantics_dir,
        graph_type="step",
        package="tests",
    )
    manager = OntologyManager()
    if include_pipeline:
        manager.commit_pipeline(
            "demo-pipeline",
            _pipeline_document(),
            user="tester",
            message="seed contract pipeline",
        )
    return manager


def should_return_expected_step_keys_when_querying_processing_steps(
    tmp_path: Path,
    monkeypatch: pytest.MonkeyPatch,
    _isolate_semantics: Path,
) -> None:
    manager = _seed_manager(tmp_path, _isolate_semantics, monkeypatch, include_pipeline=False)

    steps = manager.get_processing_steps()
    step = next(node for node in steps if node.get("@id") == _STEP_IRI)

    assert step["@type"] == "cf:ProcessingStep"
    assert step["cf:belongsToStepPackage"] == {"@id": _PACKAGE_IRI}


def should_return_expected_package_keys_when_querying_step_packages(
    tmp_path: Path,
    monkeypatch: pytest.MonkeyPatch,
    _isolate_semantics: Path,
) -> None:
    manager = _seed_manager(tmp_path, _isolate_semantics, monkeypatch, include_pipeline=False)

    packages = manager.get_step_packages()
    package = next(node for node in packages if node.get("@id") == _PACKAGE_IRI)

    assert package["cf:packageId"] == "demo.pkg"
    assert package["cf:packageVersion"] == "1.2.3"


def should_return_expected_pipeline_keys_when_querying_pipelines(
    tmp_path: Path,
    monkeypatch: pytest.MonkeyPatch,
    _isolate_semantics: Path,
) -> None:
    manager = _seed_manager(tmp_path, _isolate_semantics, monkeypatch, include_pipeline=True)

    pipelines = manager.get_pipelines()
    pipeline = next(node for node in pipelines if node.get("@id") == _PIPELINE_IRI)

    assert pipeline["@type"] == "cf:ProcessingPipeline"


def should_return_boolean_and_report_text_when_validating_pipeline_document(
    tmp_path: Path,
    monkeypatch: pytest.MonkeyPatch,
    _isolate_semantics: Path,
) -> None:
    manager = _seed_manager(tmp_path, _isolate_semantics, monkeypatch, include_pipeline=False)

    result = manager.validate_pipeline_document(_pipeline_document())

    assert isinstance(result.get("conforms"), bool)
    assert isinstance(result.get("report_text"), str)
