from __future__ import annotations

from pathlib import Path

import pytest
from typer.testing import CliRunner

from worai import cli as root_cli
from worai.commands import graph as graph_cmd
from worai.core.graph import export as export_core
from worai.errors import UsageError


def test_graph_export_command_calls_core_with_explicit_output(monkeypatch, tmp_path: Path) -> None:
    seen = {}

    def _stub_run(options):
        seen["options"] = options
        return options.output_path

    monkeypatch.setattr(graph_cmd, "run_graph_export", _stub_run)
    monkeypatch.setattr(
        graph_cmd,
        "resolve_profile_api_key",
        lambda _ctx, explicit_profile=None: ("wl_default", explicit_profile or "default", Path("worai.toml")),
    )

    runner = CliRunner()
    output = tmp_path / "kg.nt"
    result = runner.invoke(graph_cmd.app, ["export", str(output)])

    assert result.exit_code == 0
    assert seen["options"].api_key == "wl_default"
    assert seen["options"].output_path == output
    assert seen["options"].endpoint == "https://api.wordlift.io/dataset/export"


def test_graph_export_uses_profile_and_root_config(monkeypatch, tmp_path: Path) -> None:
    seen = {}

    def _stub_run(options):
        seen["options"] = options
        return options.output_path

    monkeypatch.setattr(graph_cmd, "run_graph_export", _stub_run)
    monkeypatch.setattr(
        graph_cmd,
        "resolve_profile_api_key",
        lambda _ctx, explicit_profile=None: ("wl_acme", explicit_profile or "default", Path("custom.toml")),
    )

    runner = CliRunner()
    result = runner.invoke(
        root_cli.app,
        ["--config", str(tmp_path / "custom.toml"), "graph", "export", "--profile", "acme", str(tmp_path / "out.ttl")],
    )

    assert result.exit_code == 0
    assert seen["options"].api_key == "wl_acme"


def test_graph_export_generates_default_filename_with_sequence(monkeypatch, tmp_path: Path) -> None:
    first = tmp_path / "export_default_20260224_1.ttl"
    first.write_text("exists", encoding="utf-8")
    monkeypatch.chdir(tmp_path)

    class _FakeDateTime:
        @classmethod
        def now(cls):  # noqa: D401
            class _Now:
                @staticmethod
                def strftime(fmt: str) -> str:
                    assert fmt == "%Y%m%d"
                    return "20260224"

            return _Now()

    seen = {}

    def _stub_run(options):
        seen["output"] = options.output_path
        return options.output_path

    monkeypatch.setattr(graph_cmd, "datetime", _FakeDateTime)
    monkeypatch.setattr(graph_cmd, "run_graph_export", _stub_run)
    monkeypatch.setattr(
        graph_cmd,
        "resolve_profile_api_key",
        lambda _ctx, explicit_profile=None: ("wl_default", explicit_profile or "default", Path("worai.toml")),
    )
    result = CliRunner().invoke(graph_cmd.app, ["export"])

    assert result.exit_code == 0
    assert seen["output"] == Path("export_default_20260224_2.ttl")


def test_graph_export_requires_api_key(monkeypatch, tmp_path: Path) -> None:
    monkeypatch.setattr(
        graph_cmd,
        "resolve_profile_api_key",
        lambda _ctx, explicit_profile=None: (None, explicit_profile or "default", Path("worai.toml")),
    )

    result = CliRunner().invoke(graph_cmd.app, ["export"])
    assert result.exit_code == 1
    assert isinstance(result.exception, UsageError)
    assert "WORDLIFT_API_KEY is required" in str(result.exception)


def test_graph_export_surfaces_validation_errors(monkeypatch, tmp_path: Path) -> None:
    monkeypatch.setattr(
        graph_cmd,
        "resolve_profile_api_key",
        lambda _ctx, explicit_profile=None: ("wl_default", explicit_profile or "default", Path("worai.toml")),
    )
    monkeypatch.setattr(
        graph_cmd,
        "run_graph_export",
        lambda _options: (_ for _ in ()).throw(ValueError("Unsupported output extension '.bad'.")),
    )

    result = CliRunner().invoke(graph_cmd.app, ["export", "bad.bad"])
    assert result.exit_code == 1
    assert isinstance(result.exception, UsageError)
    assert "Unsupported output extension" in str(result.exception)


def test_graph_export_validate_option_runs_validation(monkeypatch, tmp_path: Path) -> None:
    seen = {"validated": False}

    monkeypatch.setattr(
        graph_cmd,
        "resolve_profile_api_key",
        lambda _ctx, explicit_profile=None: ("wl_default", explicit_profile or "default", Path("worai.toml")),
    )
    monkeypatch.setattr(
        graph_cmd,
        "run_graph_export",
        lambda options: options.output_path,
    )
    monkeypatch.setattr(
        graph_cmd,
        "validate_exported_graph",
        lambda _path: seen.update({"validated": True}),
    )

    output = tmp_path / "out.ttl"
    result = CliRunner().invoke(graph_cmd.app, ["export", str(output), "--validate"])

    assert result.exit_code == 0
    assert seen["validated"] is True
    assert "Validated graph export" in result.output


def test_graph_export_validate_option_surfaces_validation_failure(monkeypatch, tmp_path: Path) -> None:
    monkeypatch.setattr(
        graph_cmd,
        "resolve_profile_api_key",
        lambda _ctx, explicit_profile=None: ("wl_default", explicit_profile or "default", Path("worai.toml")),
    )
    monkeypatch.setattr(
        graph_cmd,
        "run_graph_export",
        lambda options: options.output_path,
    )
    monkeypatch.setattr(
        graph_cmd,
        "validate_exported_graph",
        lambda _path: (_ for _ in ()).throw(RuntimeError("Exported graph failed SHACL validation.")),
    )

    output = tmp_path / "out.ttl"
    result = CliRunner().invoke(graph_cmd.app, ["export", str(output), "--validate"])

    assert result.exit_code == 1
    assert isinstance(result.exception, UsageError)
    assert "failed SHACL validation" in str(result.exception)


def test_resolve_accept_header_supports_known_extensions() -> None:
    assert export_core.resolve_accept_header(Path("x.ttl")) == "text/turtle"
    assert export_core.resolve_accept_header(Path("x.nt")) == "application/n-triples"
    assert export_core.resolve_accept_header(Path("x.nq")) == "application/n-quads"
    assert export_core.resolve_accept_header(Path("x.rdf")) == "application/rdf+xml"
    assert export_core.resolve_accept_header(Path("x.xml")) == "application/rdf+xml"
    assert export_core.resolve_accept_header(Path("x.jsonld")) == "application/ld+json"
    assert export_core.resolve_accept_header(Path("x.json")) == "application/ld+json"


def test_validate_exported_graph_runtime_paths(monkeypatch, tmp_path: Path) -> None:
    path = tmp_path / "graph.ttl"
    path.write_text("@prefix schema: <http://schema.org/> .", encoding="utf-8")

    class _Result:
        conforms = True
        warning_count = 0

    monkeypatch.setattr(export_core, "validate_file", lambda *_a, **_k: _Result())
    export_core.validate_exported_graph(path)

    _Result.warning_count = 1
    with pytest.raises(RuntimeError, match="contains SHACL warnings"):
        export_core.validate_exported_graph(path)

    _Result.warning_count = 0
    _Result.conforms = False
    with pytest.raises(RuntimeError, match="failed SHACL validation"):
        export_core.validate_exported_graph(path)

    monkeypatch.setattr(
        export_core,
        "validate_file",
        lambda *_a, **_k: (_ for _ in ()).throw(RuntimeError("sdk failure")),
    )
    with pytest.raises(RuntimeError, match="Failed to run SHACL validation"):
        export_core.validate_exported_graph(path)


def test_run_graph_export_writes_response_content(monkeypatch, tmp_path: Path) -> None:
    output = tmp_path / "graph.ttl"
    seen = {}

    class _FakeResponse:
        status_code = 200
        content = b"@prefix schema: <http://schema.org/> ."
        text = ""

    def _stub_get(url, headers=None, timeout=120):
        seen["url"] = url
        seen["accept"] = (headers or {}).get("Accept")
        seen["auth"] = (headers or {}).get("Authorization")
        seen["timeout"] = timeout
        return _FakeResponse()

    monkeypatch.setattr(export_core.requests, "get", _stub_get)
    path = export_core.run_graph_export(
        export_core.GraphExportOptions(
            api_key="wl_123",
            output_path=output,
            endpoint="https://api.wordlift.io/dataset/export",
            timeout=90,
        )
    )

    assert path == output
    assert output.read_bytes() == _FakeResponse.content
    assert seen["url"] == "https://api.wordlift.io/dataset/export"
    assert seen["accept"] == "text/turtle"
    assert seen["auth"] == "Key wl_123"
    assert seen["timeout"] == 90
