from __future__ import annotations

import pytest

from psycopg.crdb import CrdbConnection

from .utils import VersionCheck


def pytest_configure(config):
    # register libpq marker
    config.addinivalue_line(
        "markers",
        "crdb(version_expr, reason=detail): run/skip the test with matching CockroachDB"
        " (e.g. '>= 21.2.10', '< 22.1', 'skip < 22')",
    )
    config.addinivalue_line(
        "markers",
        "crdb_skip(reason): skip the test for known CockroachDB reasons",
    )


def check_crdb_version(got, mark):
    if mark.name == "crdb":
        assert len(mark.args) <= 1
        assert not (set(mark.kwargs) - {"reason"})
        spec = mark.args[0] if mark.args else "only"
        reason = mark.kwargs.get("reason")
    elif mark.name == "crdb_skip":
        assert len(mark.args) == 1
        assert not mark.kwargs
        reason = mark.args[0]
        assert reason in _crdb_reasons, reason
        spec = _crdb_reason_version.get(reason, "skip")
    else:
        assert False, mark.name

    pred = VersionCheck.parse(spec)
    pred.whose = "CockroachDB"

    if not (msg := pred.get_skip_message(got)):
        return None

    if reason := crdb_skip_message(reason):
        msg = f"{msg}: {reason}"

    return msg


# Utility functions which can be imported in the test suite

is_crdb = CrdbConnection.is_crdb


def crdb_skip_message(reason: str | None) -> str:
    msg = ""
    if reason:
        msg = reason
        if _crdb_reasons.get(reason):
            url = (
                "https://github.com/cockroachdb/cockroach/"
                f"issues/{_crdb_reasons[reason]}"
            )
            msg = f"{msg} ({url})"

    return msg


def skip_crdb(*args, reason=None):
    return pytest.param(*args, marks=pytest.mark.crdb("skip", reason=reason))


def crdb_encoding(*args):
    """Mark tests that fail on CockroachDB because of missing encodings"""
    return skip_crdb(*args, reason="encoding")


def crdb_scs_off(*args):
    return skip_crdb(*args, reason="standard_conforming_strings=off")


# mapping from reason description to ticket number
_crdb_reasons = {
    "2-phase commit": 22329,
    "backend pid": 35897,
    "batch statements": 44803,
    "begin_read_only": 87012,
    "binary decimal": 82492,
    "cancel": 41335,
    "cast adds tz": 51692,
    "cidr": 18846,
    "close portal": None,
    "composite": 27792,
    "copy array": 82792,
    "copy canceled": 81559,
    "copy": 41608,
    "cursor invalid name": 84261,
    "cursor with hold": 77101,
    "deferrable": 48307,
    "do": 17511,
    "encoding": 35882,
    "geometric types": 21286,
    "hstore": 41284,
    "inet": 94192,
    "infinity date": 41564,
    "interval style": 35807,
    "json array": 23468,
    "large objects": 243,
    "negative interval": 81577,
    "nested array": 32552,
    "no col query": None,
    "notify": 41522,
    "password_encryption": 42519,
    "pg_terminate_backend": 35897,
    "range": 41282,
    "scroll cursor": 77102,
    "server-side cursor": 41412,
    "severity_nonlocalized": 81794,
    "stored procedure": 1751,
    "to_regtype": None,
}

_crdb_reason_version = {
    "backend pid": "skip < 22",
    "inet": "skip == 22.2.1",
    "cancel": "skip < 22",
    "server-side cursor": "skip < 22.1.3",
    "severity_nonlocalized": "skip < 22.1.3",
    "to_regtype": "skip < 22.2",
}
