from __future__ import annotations

from pathlib import Path

from rdflib import Literal, RDF, URIRef
from rdflib.namespace import XSD

from cf_ontology.namespaces import CF_BASE
from cf_ontology.quad_store import QuadStore


def _store(tmp_path: Path) -> QuadStore:
    semantics_dir = tmp_path / "semantics"
    store = QuadStore(
        db_path=semantics_dir / "cf-test.duckdb",
        semantics_dir=semantics_dir,
    )
    store.init_schema(create_pipeline_tables=False)
    return store


def _named_document(node_id: str, label: str) -> dict:
    return {
        "@context": {
            "cf": CF_BASE,
            "schema": "https://schema.org/",
        },
        "@graph": [
            {
                "@id": node_id,
                "@type": "cf:ProcessingPipeline",
                "schema:name": label,
            }
        ],
    }


def should_preserve_rdflib_term_types_when_loading_graph_after_ingest(
    tmp_path: Path,
) -> None:
    store = _store(tmp_path)
    subject = URIRef("https://example.org/record")
    ref = URIRef("https://example.org/target")
    document = {
        "@context": {
            "ex": "https://example.org/",
            "xsd": "http://www.w3.org/2001/XMLSchema#",
        },
        "@graph": [
            {
                "@id": str(subject),
                "ex:ref": {"@id": str(ref)},
                "ex:plain": "plain",
                "ex:count": {"@value": 7, "@type": "xsd:integer"},
                "ex:ratio": {"@value": 1.5, "@type": "xsd:double"},
                "ex:greeting": {"@value": "hallo", "@language": "de"},
            }
        ],
    }
    store.ingest_document(
        document,
        graph_id="terms",
        graph_type="custom",
        package="tests",
        source_path=None,
    )

    graph = store.load_graph(["terms"])

    assert (subject, URIRef("https://example.org/ref"), ref) in graph
    assert (subject, URIRef("https://example.org/plain"), Literal("plain")) in graph
    assert (
        subject,
        URIRef("https://example.org/count"),
        Literal("7", datatype=XSD.integer),
    ) in graph
    assert (
        subject,
        URIRef("https://example.org/ratio"),
        Literal("1.5", datatype=XSD.double),
    ) in graph
    assert (
        subject,
        URIRef("https://example.org/greeting"),
        Literal("hallo", lang="de"),
    ) in graph


def should_keep_named_graphs_isolated_when_loading_dataset_from_multiple_graphs(
    tmp_path: Path,
) -> None:
    store = _store(tmp_path)
    alpha = URIRef("https://example.org/pipeline/alpha")
    beta = URIRef("https://example.org/pipeline/beta")
    name_predicate = URIRef("https://schema.org/name")
    store.ingest_document(
        _named_document(str(alpha), "alpha"),
        graph_id="g1",
        graph_type="custom",
        package="tests",
        source_path=None,
    )
    store.ingest_document(
        _named_document(str(beta), "beta"),
        graph_id="g2",
        graph_type="custom",
        package="tests",
        source_path=None,
    )

    dataset = store.load_dataset(["g1", "g2"])
    graph_one = dataset.graph(URIRef("g1"))
    graph_two = dataset.graph(URIRef("g2"))

    assert (alpha, name_predicate, Literal("alpha")) in graph_one
    assert (beta, name_predicate, Literal("beta")) not in graph_one
    assert (beta, name_predicate, Literal("beta")) in graph_two
    assert (alpha, name_predicate, Literal("alpha")) not in graph_two


def should_overwrite_existing_quads_when_reingesting_the_same_graph_id(
    tmp_path: Path,
) -> None:
    store = _store(tmp_path)
    subject = URIRef("https://example.org/pipeline/demo")
    name_predicate = URIRef("https://schema.org/name")
    store.ingest_document(
        _named_document(str(subject), "alpha"),
        graph_id="g1",
        graph_type="custom",
        package="tests",
        source_path=None,
    )
    store.ingest_document(
        _named_document(str(subject), "beta"),
        graph_id="g1",
        graph_type="custom",
        package="tests",
        source_path=None,
    )

    graph = store.load_graph(["g1"])

    assert (subject, name_predicate, Literal("alpha")) not in graph
    assert (subject, name_predicate, Literal("beta")) in graph


def should_allow_repeated_schema_initialization_when_database_already_exists(
    tmp_path: Path,
) -> None:
    store = _store(tmp_path)

    store.init_schema(create_pipeline_tables=False)
    store.ingest_document(
        {
            "@context": {"cf": CF_BASE},
            "@graph": [
                {
                    "@id": "https://example.org/pipeline/schema-check",
                    "@type": "cf:ProcessingPipeline",
                }
            ],
        },
        graph_id="schema-check",
        graph_type="custom",
        package="tests",
        source_path=None,
    )

    graph = store.load_graph(["schema-check"])

    assert (
        URIRef("https://example.org/pipeline/schema-check"),
        RDF.type,
        URIRef(f"{CF_BASE}ProcessingPipeline"),
    ) in graph
