from __future__ import annotations

from pathlib import Path

import pytest
from rdflib import Dataset, Graph, Literal, URIRef

from ontoenv import OntoEnv, OntoEnvStore, refresh_dataset_from_env


FIXTURES = Path(__file__).parent / "fixtures" / "rdflib_store"
DEMO_TTL = FIXTURES / "demo.ttl"
FIRST_TTL = FIXTURES / "first.ttl"
SECOND_TTL = FIXTURES / "second.ttl"


class DictGraphStore:
    def __init__(self) -> None:
        self.graphs: dict[str, Graph] = {}

    def add_graph(self, iri: str, graph: Graph, overwrite: bool = False) -> None:
        if not overwrite and iri in self.graphs:
            return
        self.graphs[iri] = graph

    def get_graph(self, iri: str) -> Graph:
        return self.graphs[iri]

    def graph_ids(self) -> list[str]:
        return list(self.graphs.keys())


@pytest.fixture
def persistent_env(tmp_path: Path):
    env = OntoEnv(path=tmp_path, recreate=True, offline=True)
    try:
        yield env
    finally:
        env.close()


@pytest.fixture
def temporary_env():
    env = OntoEnv(temporary=True, offline=True)
    try:
        yield env
    finally:
        env.close()


def test_standalone_store_is_empty_read_only() -> None:
    store = OntoEnvStore()
    graph = Graph(store=store, identifier=URIRef("urn:g"))
    s = URIRef("urn:s")
    p = URIRef("urn:p")
    o = Literal("value")
    with pytest.raises(ValueError, match="read-only snapshot"):
        graph.add((s, p, o))
    assert len(list(graph.query("SELECT ?o WHERE { <urn:s> <urn:p> ?o }"))) == 0
    assert len(list(store.contexts())) == 0


def test_dataset_from_env_auto_uses_rdf5d_for_persistent_env(persistent_env: OntoEnv) -> None:
    persistent_env.add(str(DEMO_TTL))
    persistent_env.flush()

    dataset = persistent_env.snapshot_as_dataset()
    assert dataset.store._backend.backend_kind() == "rdf5d"

    rows = list(
        dataset.query(
            "SELECT ?label WHERE { GRAPH <urn:example:demo> { <urn:example:ahu1> <urn:example:hasLabel> ?label } }"
        )
    )
    assert [row.label for row in rows] == [Literal("AHU-1")]
    assert len(dataset.graph(URIRef("urn:example:demo"))) == 2

    with pytest.raises(ValueError, match="read-only snapshot"):
        dataset.graph(URIRef("urn:example:demo")).add(
            (URIRef("urn:example:ahu2"), URIRef("urn:example:hasLabel"), Literal("AHU-2"))
        )


def test_dataset_from_env_auto_falls_back_to_copy_for_temporary_env(temporary_env: OntoEnv) -> None:
    temporary_env.add(str(DEMO_TTL))

    dataset = temporary_env.snapshot_as_dataset()
    assert dataset.store._backend.backend_kind() == "copy"
    rows = list(
        dataset.query(
            "SELECT ?label WHERE { GRAPH <urn:example:demo> { <urn:example:ahu1> <urn:example:hasLabel> ?label } }"
        )
    )
    assert [row.label for row in rows] == [Literal("AHU-1")]


def test_backend_rdf5d_rejects_temporary_and_graph_store_envs(temporary_env: OntoEnv) -> None:
    temporary_env.add(str(DEMO_TTL))
    with pytest.raises(ValueError, match="backend='rdf5d'"):
        temporary_env.snapshot_as_dataset(backend="rdf5d")

    store = DictGraphStore()
    external_env = OntoEnv(graph_store=store, temporary=True, init_from_store=True)
    try:
        with pytest.raises(ValueError, match="backend='rdf5d'"):
            external_env.snapshot_as_dataset(backend="rdf5d")
    finally:
        external_env.close()


def test_refresh_dataset_from_env_is_explicit(persistent_env: OntoEnv) -> None:
    persistent_env.add(str(FIRST_TTL))
    persistent_env.flush()

    dataset = persistent_env.snapshot_as_dataset()
    assert dataset.store._backend.backend_kind() == "rdf5d"
    assert list(
        dataset.query(
            "SELECT ?label WHERE { GRAPH <urn:example:second> { <urn:example:second> <urn:example:hasLabel> ?label } }"
        )
    ) == []

    persistent_env.add(str(SECOND_TTL))
    persistent_env.flush()

    assert list(
        dataset.query(
            "SELECT ?label WHERE { GRAPH <urn:example:second> { <urn:example:second> <urn:example:hasLabel> ?label } }"
        )
    ) == []

    refresh_dataset_from_env(dataset, persistent_env)
    rows = list(
        dataset.query(
            "SELECT ?label WHERE { GRAPH <urn:example:second> { <urn:example:second> <urn:example:hasLabel> ?label } }"
        )
    )
    assert [row.label for row in rows] == [Literal("Second")]


def test_dataset_from_env_with_other_store_forces_copy(persistent_env: OntoEnv) -> None:
    persistent_env.add(str(DEMO_TTL))
    persistent_env.flush()

    with pytest.raises(ValueError, match="requires an OntoEnvStore"):
        persistent_env.snapshot_as_dataset(backend="rdf5d", store=Graph().store)

    dataset = persistent_env.snapshot_as_dataset(store=Graph().store)
    rows = list(
        dataset.query(
            "SELECT ?label WHERE { GRAPH <urn:example:demo> { <urn:example:ahu1> <urn:example:hasLabel> ?label } }"
        )
    )
    assert [row.label for row in rows] == [Literal("AHU-1")]
