from __future__ import annotations

import json
from pathlib import Path

from rdflib import Dataset

from cf_ontology.namespaces import CF_BASE
from cf_ontology.ontology_manager import OntologyManager
from cf_ontology.package_discovery import _extract_package_info
from cf_ontology.pipeline_id import derive_pipeline_id_from_document

_STRUCTURED_FORMAT = "json" + "-ld"


def _manager_with_document(tmp_path: Path, document: dict) -> OntologyManager:
    manager = OntologyManager.__new__(OntologyManager)
    manager._ontology_store = None
    manager._steps_store = None
    manager._pipelines_store = None
    manager._pipeline_states_store = None
    manager._pipeline_state_store = None
    manager._ontology_db_path = tmp_path / "missing.duckdb"
    manager._document_records = [
        {
            "graph_id": "test",
            "graph_type": "step",
            "package": "custom",
            "document": document,
            "path": None,
        }
    ]
    return manager


def _canonical_step_document() -> dict:
    return {
        "@context": {"cf": CF_BASE, "schema": "https://schema.org/"},
        "@graph": [
            {
                "@id": "urn:pkg:demo",
                "@type": f"{CF_BASE}StepPackage",
                f"{CF_BASE}packageId": "demo.pkg",
                f"{CF_BASE}packageVersion": "1.2.3",
            },
            {
                "@id": "urn:step:demo",
                "@type": f"{CF_BASE}ProcessingStep",
                f"{CF_BASE}belongsToStepPackage": {"@id": "urn:pkg:demo"},
                f"{CF_BASE}hasImplementation": {"@id": "urn:impl:demo"},
            },
            {
                "@id": "urn:impl:demo",
                f"{CF_BASE}implementationEntrypoint": "demo.module:run",
                f"{CF_BASE}implementationArtifact": {"@id": "urn:artifact:demo"},
                f"{CF_BASE}operationName": "demo_op",
                f"{CF_BASE}operationContract": {"kind": "example"},
            },
            {
                "@id": "urn:artifact:demo",
                f"{CF_BASE}artifactPath": "bin/demo.dll",
                f"{CF_BASE}pluginName": "demo-plugin",
                f"{CF_BASE}pluginVersion": "9.9.9",
                f"{CF_BASE}pluginApiVersion": "v2",
                f"{CF_BASE}pluginDataAbi": "abi-1",
                f"{CF_BASE}pluginListSymbol": "cf_list_steps",
                f"{CF_BASE}pluginResolveSymbol": "cf_resolve_step",
            },
            {
                "@id": "urn:pipeline:demo",
                "@type": f"{CF_BASE}ProcessingPipeline",
            },
        ],
    }


def test_ontology_manager_queries_accept_canonical_full_iri_document(tmp_path: Path) -> None:
    manager = _manager_with_document(tmp_path, _canonical_step_document())

    steps = manager.get_processing_steps()
    packages = manager.get_step_packages()
    package_steps = manager.get_steps_for_package("demo.pkg@1.2.3")
    bindings = manager.resolve_step_implementations("urn:step:demo", resolve_paths=False)
    pipelines = manager.get_pipelines()

    assert [node["@id"] for node in steps] == ["urn:step:demo"]
    assert [node["@id"] for node in packages] == ["urn:pkg:demo"]
    assert [node["@id"] for node in package_steps] == ["urn:step:demo"]
    assert [node["@id"] for node in pipelines] == ["urn:pipeline:demo"]
    assert bindings[0]["entrypoint"] == "demo.module:run"
    assert bindings[0]["artifact"]["plugin_name"] == "demo-plugin"
    assert bindings[0]["artifact"]["plugin_version"] == "9.9.9"
    assert bindings[0]["operation_name"] == "demo_op"


def test_extract_package_info_accepts_canonical_step_package_document() -> None:
    package_id, package_version = _extract_package_info(_canonical_step_document())

    assert package_id == "demo.pkg"
    assert package_version == "1.2.3"


def test_derive_pipeline_id_accepts_canonical_namespace(tmp_path: Path) -> None:
    document = {
        "@context": {"cf": CF_BASE},
        "@graph": [
            {
                "@id": "http://example.org/pipelines/pipe",
                "@type": f"{CF_BASE}ProcessingPipeline",
            }
        ],
    }

    dataset = Dataset()
    dataset.parse(data=json.dumps(document), format=_STRUCTURED_FORMAT)
    payload = dataset.serialize(format="nquads")
    if isinstance(payload, bytes):
        payload = payload.decode("utf-8")
    path = tmp_path / "canonical-pipeline.nq"
    path.write_text(payload, encoding="utf-8")

    assert derive_pipeline_id_from_document(path) == "pipe"