"""Tests for docs example generation."""

from __future__ import annotations

import subprocess
import sys
from pathlib import Path
from typing import TYPE_CHECKING

from scripts import build_docs_examples
from tests.conftest import assert_output

if TYPE_CHECKING:
    import pytest

ROOT = Path(__file__).resolve().parents[1]
SCRIPT = ROOT / "scripts" / "build_docs_examples.py"
EXPECTED_DOCS_EXAMPLES_PATH = ROOT / "tests" / "data" / "expected" / "docs_examples"


def test_build_docs_examples_check_is_up_to_date() -> None:
    """Generated docs examples are committed."""
    subprocess.run([sys.executable, str(SCRIPT), "--check"], check=True)


def test_docs_examples_registry() -> None:
    """Docs example registry exposes stable generated section IDs."""
    output = "".join(f"{example.example_id}: {example.path.name}\n" for example in build_docs_examples.docs_examples())

    assert_output(output, EXPECTED_DOCS_EXAMPLES_PATH / "docs_examples_registry.txt")


def test_reuse_scope_tree_example() -> None:
    """Directory examples render a stable tree and representative files."""
    assert_output(
        build_docs_examples.render_reuse_scope_tree_example(),
        EXPECTED_DOCS_EXAMPLES_PATH / "reuse_scope_tree_example.txt",
    )


def test_docs_example_render_helpers(tmp_path: Path) -> None:
    """File and details render helpers produce stable Markdown blocks."""
    yaml_path = tmp_path / "schema.yaml"
    python_path = tmp_path / "model.py"
    yaml_path.write_text("name: User\n", encoding="utf-8")
    python_path.write_text(
        "# generated by datamodel-codegen:\n"
        "#   filename:  model.py\n"
        "#   timestamp: 2019-07-26T00:00:00+00:00\n\n"
        "class User:\n"
        "    pass\n",
        encoding="utf-8",
    )
    output = "".join((
        build_docs_examples.details("schema.yaml", "yaml", build_docs_examples.read_text(yaml_path)),
        build_docs_examples.render_file_example(language="yaml", path=yaml_path),
        build_docs_examples._file_example(
            "example.output",
            "example.md",
            language="python",
            path=python_path,
            strip_python_header=True,
        ).render(),
        build_docs_examples.render_cli_example(
            build_docs_examples.MarkdownCodeBlock(
                "Command",
                "bash",
                content="datamodel-codegen --input schema.yaml --output model.py",
            ),
            build_docs_examples.MarkdownCodeBlock("Schema", "yaml", path=yaml_path),
            build_docs_examples.MarkdownCodeBlock(
                "Model",
                "python",
                path=python_path,
                strip_python_header=True,
            ),
        ),
    ))

    assert_output(output, EXPECTED_DOCS_EXAMPLES_PATH / "docs_example_render_helpers.txt")


def test_main_check_reports_up_to_date(
    tmp_path: Path,
    monkeypatch: pytest.MonkeyPatch,
    capsys: pytest.CaptureFixture[str],
) -> None:
    """CLI check mode reports success when generated sections already match."""
    docs_path = tmp_path / "doc.md"
    docs_path.write_text(_marked_example("example.current", "current"), encoding="utf-8")
    monkeypatch.setattr(
        build_docs_examples,
        "docs_examples",
        lambda: (
            build_docs_examples.DocsExample(
                example_id="example.current",
                path=docs_path,
                render=lambda: "\ncurrent\n",
            ),
        ),
    )
    monkeypatch.setattr(sys, "argv", [str(SCRIPT), "--check"])

    result = build_docs_examples.main()
    captured = capsys.readouterr()

    assert_output(
        _normalize_tmp_path(
            f"return={result}\nstdout:\n{captured.out}stderr:\n{captured.err}",
            tmp_path,
        ),
        EXPECTED_DOCS_EXAMPLES_PATH / "main_check_up_to_date.txt",
    )


def test_update_docs_examples_write_mode(
    tmp_path: Path,
    monkeypatch: pytest.MonkeyPatch,
    capsys: pytest.CaptureFixture[str],
) -> None:
    """Write mode updates generated sections after all markers are found."""
    docs_path = tmp_path / "doc.md"
    docs_path.write_text(
        "Before\n"
        "<!-- BEGIN AUTO-GENERATED DOC EXAMPLE: example.write -->\n"
        "stale\n"
        "<!-- END AUTO-GENERATED DOC EXAMPLE: example.write -->\n"
        "After\n",
        encoding="utf-8",
    )
    monkeypatch.setattr(
        build_docs_examples,
        "docs_examples",
        lambda: (
            build_docs_examples.DocsExample(
                example_id="example.write",
                path=docs_path,
                render=lambda: "\nnew\n",
            ),
        ),
    )

    result = build_docs_examples.update_docs_examples(check=False)
    captured = capsys.readouterr()

    assert_output(
        _normalize_tmp_path(
            f"return={result}\n"
            f"stdout:\n{captured.out}"
            f"stderr:\n{captured.err}"
            f"content:\n{docs_path.read_text(encoding='utf-8')}",
            tmp_path,
        ),
        EXPECTED_DOCS_EXAMPLES_PATH / "update_docs_examples_write_mode.txt",
    )


def test_update_docs_examples_check_reports_all_problems(
    tmp_path: Path,
    monkeypatch: pytest.MonkeyPatch,
    capsys: pytest.CaptureFixture[str],
) -> None:
    """Check mode reports every out-of-date path and missing marker."""
    outdated_one_path = tmp_path / "outdated-one.md"
    missing_path = tmp_path / "missing.md"
    outdated_two_path = tmp_path / "outdated-two.md"
    outdated_one_path.write_text(_marked_example("example.one", "stale"), encoding="utf-8")
    missing_path.write_text("No generated markers here.\n", encoding="utf-8")
    outdated_two_path.write_text(_marked_example("example.two", "stale"), encoding="utf-8")
    monkeypatch.setattr(
        build_docs_examples,
        "docs_examples",
        lambda: (
            build_docs_examples.DocsExample("example.one", outdated_one_path, lambda: "\none\n"),
            build_docs_examples.DocsExample("example.missing", missing_path, lambda: "\nmissing\n"),
            build_docs_examples.DocsExample("example.two", outdated_two_path, lambda: "\ntwo\n"),
        ),
    )

    result = build_docs_examples.update_docs_examples(check=True)
    captured = capsys.readouterr()

    assert_output(
        _normalize_tmp_path(
            f"return={result}\nstdout:\n{captured.out}stderr:\n{captured.err}",
            tmp_path,
        ),
        EXPECTED_DOCS_EXAMPLES_PATH / "update_docs_examples_check_problems.txt",
    )


def _marked_example(example_id: str, content: str) -> str:
    return (
        f"<!-- BEGIN AUTO-GENERATED DOC EXAMPLE: {example_id} -->\n"
        f"{content}\n"
        f"<!-- END AUTO-GENERATED DOC EXAMPLE: {example_id} -->\n"
    )


def _normalize_tmp_path(output: str, tmp_path: Path) -> str:
    return output.replace(str(tmp_path), "<tmp>").replace("\\", "/")
