"""The CLI reference the site publishes, and the guarantee that it is current.

`docs/generated/hsql-reference.md` is a build artifact: nothing reads it at run
time, and the release workflow is what carries it to harlequin.sh. So this is
the only thing that notices when hsql grows an option, renames one, or changes
what it takes, and the committed page goes on saying what used to be true.

It also checks the two things a generated page can still get wrong: a table
cell that breaks its table, and a document built from what happens to be
installed rather than from the command.
"""

from __future__ import annotations

import importlib.util
import re
from pathlib import Path
from types import ModuleType

import pytest

from harlequin.hsql import output
from harlequin.hsql.diagnostics import ExitCode

REPO_ROOT = Path(__file__).resolve().parents[2]
GENERATOR_PATH = REPO_ROOT / "scripts" / "write_cli_reference.py"
REFERENCE_PATH = REPO_ROOT / "docs" / "generated" / "hsql-reference.md"

FIX = "uv run python scripts/write_cli_reference.py"
"""What to run when the committed reference and the generator disagree."""


@pytest.fixture(scope="module")
def generator() -> ModuleType:
    """The generator, loaded from `scripts/`, which is not an importable package."""
    spec = importlib.util.spec_from_file_location("write_cli_reference", GENERATOR_PATH)
    assert spec is not None and spec.loader is not None
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module


@pytest.fixture(scope="module")
def generated(generator: ModuleType) -> str:
    text: str = generator.build_reference()
    return text


@pytest.fixture(scope="module")
def committed() -> str:
    return REFERENCE_PATH.read_text(encoding="utf-8")


def test_the_committed_reference_is_what_the_generator_writes(
    generated: str, committed: str
) -> None:
    assert committed == generated, f"the CLI reference is out of date: {FIX}"


def test_every_option_hsql_has_is_a_row(generated: str) -> None:
    """The point of generating it: nothing hand-written can be left behind."""
    from harlequin.hsql.cli import bare_command
    from harlequin.hsql.modes.spec import command_document

    for option in command_document(bare_command())["options"]:
        for decl in option["decls"]:
            assert f"`{decl}`" in generated, f"{decl} is missing from the reference"


def test_the_adapters_installed_here_are_not_written_down(generated: str) -> None:
    """A published page describes the command, not the machine that built it.

    `-a` takes an enum of what is installed, so a reference generated on a box
    with postgres on it would tell every reader that postgres is an option.
    """
    from harlequin.plugins import adapter_names

    values = _row(generated, "`-a`, `--adapter`")[2]
    assert values == "`NAME`"
    assert not [name for name in adapter_names() if name in values]


def test_the_choices_an_option_does_have_are_written_down(generated: str) -> None:
    """The other half of the one above: dropping the enum is one option's rule."""
    assert _row(generated, "`--color`")[2] == "`auto`, `always`, `never`"


def test_every_format_is_a_row_with_the_suffix_it_writes(generated: str) -> None:
    """`-o DIRECTORY` names files by these, so a wrong one is a wrong filename."""
    for name in output.format_names():
        suffix = output.suffix(name)
        assert _row(generated, f"`{name}`")[2] == (f"`{suffix}`" if suffix else "")


def test_every_exit_code_is_described(generated: str, generator: ModuleType) -> None:
    """A code with no meaning beside it fails the generator, so this is what
    catches the one that was added without one."""
    described = generator.EXIT_CODES
    assert set(described) == set(ExitCode)
    for code in ExitCode:
        assert f"| `{code.value}` | {described[code]} |" in generated


def test_no_cell_breaks_the_table_it_is_in(generated: str) -> None:
    """An unescaped pipe silently splits a row into one column too many.

    `--result all|last|N` is the cell that would, so this counts the columns
    every row actually has rather than trusting the cells that produced them.
    """
    for table in _tables(generated):
        widths = {len(row) for row in table}
        assert len(widths) == 1, f"a row of {table[0]} has the wrong width: {widths}"


def test_the_page_is_markdown_a_reader_can_land_in(generated: str) -> None:
    """It is published as a page of its own, so it carries its own title."""
    assert generated.startswith("<!-- Generated by scripts/write_cli_reference.py")
    assert "\n# hsql\n" in generated
    assert generated.endswith("\n")
    assert "\r" not in generated


def _tables(page: str) -> list[list[list[str]]]:
    """Every markdown table on the page, as rows of cells. A pipe a cell
    escaped is part of that cell, which is the whole point of escaping it."""
    tables: list[list[list[str]]] = []
    for line in page.splitlines():
        if not line.startswith("|"):
            tables.append([])
            continue
        if not tables or not tables[-1]:
            tables.append([])
        tables[-1].append([cell.strip() for cell in re.split(r"(?<!\\)\|", line)[1:-1]])
    return [table for table in tables if table]


def _row(page: str, first_cell: str) -> list[str]:
    """The one row of one table whose first cell is this."""
    rows = [row for table in _tables(page) for row in table if row[0] == first_cell]
    assert len(rows) == 1, f"expected one row for {first_cell}, found {len(rows)}"
    return rows[0]
