from __future__ import annotations

import json
import sys
from pathlib import Path
from typing import Any

import pytest


ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))

import scripts.audit_1c_adapter_coverage as coverage  # noqa: E402


def test_record_ref_from_public_reference_value() -> None:
    ref = "0123456789ABCDEF0123456789ABCDEF"
    assert coverage.record_ref_from_row({"ref": ref}) == ref
    assert coverage.record_ref_from_row({"ref": {"type": "reference", "hex": ref}}) == ref
    assert coverage.record_ref_from_row({"period": "2026-01-01"}) is None


def test_audit_data_kind_runs_complete_read_chain() -> None:
    calls: list[tuple[str, dict[str, Any]]] = []
    ref = "0123456789ABCDEF0123456789ABCDEF"

    def fake_rpc(_url: str, _token: str, method: str, payload: dict[str, Any], _timeout: float) -> dict[str, Any]:
        calls.append((method, payload))
        responses = {
            "metadata.objects.list": {"status": "ok", "objects": [{"kind": "Catalog", "name": "Товары", "ref": "Catalog.Товары"}]},
            "data.schema": {"status": "ok", "table": {"name": "_Reference1"}, "fields": [{"name": "ref"}]},
            "data.list": {"status": "ok", "rows": [{"ref": {"type": "reference", "hex": ref}}]},
            "data.count": {"status": "ok", "count": 7},
            "data.get": {"status": "ok", "rows": [{"ref": ref}]},
        }
        return responses[method]

    result = coverage.audit_data_kind(
        "http://adapter",
        "upo_test",
        "secret",
        "Catalog",
        10,
        True,
        fake_rpc,
    )

    assert result["status"] == "ok"
    assert [method for method, _ in calls] == [
        "metadata.objects.list",
        "data.schema",
        "data.list",
        "data.count",
        "data.get",
    ]
    assert calls[-1][1]["record_ref"] == ref
    assert result["operations"]["data.count"]["count"] == 7


def test_audit_data_kind_marks_get_not_applicable_for_register_row() -> None:
    def fake_rpc(_url: str, _token: str, method: str, _payload: dict[str, Any], _timeout: float) -> dict[str, Any]:
        responses = {
            "metadata.objects.list": {"status": "ok", "objects": [{"kind": "InformationRegister", "name": "Курсы"}]},
            "data.schema": {"status": "ok", "table": {"name": "_InfoRg1"}, "fields": [{"name": "period"}]},
            "data.list": {"status": "ok", "rows": [{"period": "2026-01-01"}]},
            "data.count": {"status": "ok", "count": 1},
        }
        return responses[method]

    result = coverage.audit_data_kind(
        "http://adapter", "upo_test", "secret", "InformationRegister", 10, True, fake_rpc
    )

    assert result["status"] == "ok"
    assert result["operations"]["data.get"] == {
        "status": "not_applicable",
        "reason": "object_has_no_reference_key",
        "duration_ms": 0,
    }


def test_audit_data_kind_does_not_mark_get_not_applicable_when_list_failed() -> None:
    def fake_rpc(_url: str, _token: str, method: str, _payload: dict[str, Any], _timeout: float) -> dict[str, Any]:
        responses = {
            "metadata.objects.list": {"status": "ok", "objects": [{"kind": "Document", "name": "Продажа"}]},
            "data.schema": {"status": "ok", "table": {"name": "_Document1"}, "fields": [{"name": "ref"}]},
            "data.list": {"status": "transport_error", "diagnostics": {"message": "timed out"}},
            "data.count": {"status": "ok", "count": 3},
        }
        return responses[method]

    result = coverage.audit_data_kind(
        "http://adapter", "upo_test", "secret", "Document", 10, True, fake_rpc
    )

    assert result["status"] == "degraded"
    assert result["operations"]["data.get"]["status"] == "blocked"
    assert result["operations"]["data.get"]["reason"] == "data_list_failed"


def test_run_data_checks_saves_and_resumes_checkpoint(tmp_path: Path) -> None:
    checkpoint = tmp_path / "coverage.checkpoint.json"
    calls: list[str] = []

    def fake_rpc(_url: str, _token: str, method: str, payload: dict[str, Any], _timeout: float) -> dict[str, Any]:
        calls.append(f"{method}:{payload.get('kind', '')}")
        if method == "metadata.objects.list":
            return {"status": "ok", "objects": [{"kind": payload["kind"], "name": payload["kind"]}]}
        if method == "data.schema":
            return {"status": "ok", "table": {"name": "_Data1"}, "fields": []}
        raise AssertionError(method)

    first, resumed = coverage.run_data_checks(
        "http://adapter", "upo_test", "secret", ["Catalog", "Document"], 10, False, 2, checkpoint, False, False, fake_rpc
    )
    assert resumed == 0
    assert set(first) == {"Catalog", "Document"}
    saved = json.loads(checkpoint.read_text(encoding="utf-8"))
    assert saved["include_reads"] is False
    assert set(saved["checks"]) == {"Catalog", "Document"}

    calls.clear()
    second, resumed = coverage.run_data_checks(
        "http://adapter", "upo_test", "secret", ["Catalog", "Document"], 10, False, 2, checkpoint, True, False, fake_rpc
    )
    assert resumed == 2
    assert second == first
    assert calls == []


def test_run_data_checks_can_retry_only_degraded_entries(tmp_path: Path) -> None:
    checkpoint = tmp_path / "coverage.checkpoint.json"
    checkpoint.write_text(
        json.dumps(
            {
                "schema": "onec_adapter_data_audit_checkpoint.v1",
                "base_url": "http://adapter",
                "base_id": "upo_test",
                "include_reads": False,
                "checks": {
                    "Catalog": {"kind": "Catalog", "status": "ok"},
                    "Document": {"kind": "Document", "status": "degraded"},
                },
            }
        ),
        encoding="utf-8",
    )
    calls: list[str] = []

    def fake_rpc(_url: str, _token: str, method: str, payload: dict[str, Any], _timeout: float) -> dict[str, Any]:
        calls.append(method)
        if method == "metadata.objects.list":
            return {"status": "ok", "objects": [{"kind": payload["kind"], "name": "Продажа"}]}
        if method == "data.schema":
            return {"status": "ok", "table": {"name": "_Document1"}, "fields": []}
        raise AssertionError(method)

    checks, resumed = coverage.run_data_checks(
        "http://adapter", "upo_test", "secret", ["Catalog", "Document"], 10, False, 1, checkpoint, True, True, fake_rpc
    )

    assert resumed == 1
    assert checks["Catalog"]["status"] == "ok"
    assert checks["Document"]["status"] == "ok"
    assert calls == ["metadata.objects.list", "data.schema"]


def test_checkpoint_rejects_schema_only_resume_for_full_reads(tmp_path: Path) -> None:
    checkpoint = tmp_path / "coverage.checkpoint.json"
    checkpoint.write_text(
        json.dumps(
            {
                "schema": "onec_adapter_data_audit_checkpoint.v1",
                "base_url": "http://adapter",
                "base_id": "upo_test",
                "include_reads": False,
                "checks": {},
            }
        ),
        encoding="utf-8",
    )
    with pytest.raises(ValueError, match="another data audit mode"):
        coverage.load_checkpoint(checkpoint, "http://adapter", "upo_test", True, True)


def test_build_report_does_not_treat_unchecked_non_data_kind_as_list_failure() -> None:
    def fake_rpc(_url: str, _token: str, method: str, payload: dict[str, Any], _timeout: float) -> dict[str, Any]:
        if method == "metadata.adapter.audit":
            return {
                "status": "ok",
                "optional_deep_reads": [
                    {"kind": "DocumentJournal", "property": "column_types", "flag": "include_column_types=true"}
                ],
                "metadata_kinds": [
                    {"kind": "Catalog", "kind_ru": "Справочник", "count": 1, "capabilities": ["list"]},
                    {"kind": "CommonModule", "kind_ru": "ОбщийМодуль", "count": 1, "capabilities": ["list"]},
                ],
            }
        if method == "metadata.objects.list":
            return {"status": "ok", "objects": [{"kind": payload["kind"], "name": "Товары"}]}
        if method == "data.schema":
            return {"status": "ok", "table": {"name": "_Reference1"}, "fields": []}
        raise AssertionError(method)

    report = coverage.build_report(
        "http://adapter",
        "upo_test",
        "secret",
        10,
        True,
        True,
        workers=1,
        rpc_call=fake_rpc,
    )

    assert report["status"] == "ok"
    assert report["list_failures"] == []
    assert report["counts"]["data_kinds_checked"] == 1
    assert report["optional_deep_reads"][0]["property"] == "column_types"
    common_module = next(row for row in report["matrix"] if row["kind"] == "CommonModule")
    assert "list_status" not in common_module
