from app.jobs.repair_magento_configurable_visibility import (
    _is_magento_deadlock_error,
    _update_visibility_with_retry,
    find_visibility_repair_candidates,
)


class _ScalarResult:
    def __init__(self, rows):
        self._rows = rows

    def all(self):
        return self._rows


class _FakeSession:
    def __init__(self, rows):
        self._rows = rows

    def execute(self, stmt):
        return _ScalarResult(self._rows)


def test_find_visibility_repair_candidates_sets_parent_and_child_targets():
    session = _FakeSession(
        [
            ("PARENT-1", "CHILD-1"),
            ("PARENT-1", "CHILD-2"),
        ]
    )

    candidates = find_visibility_repair_candidates(session)

    assert [item.to_dict() for item in candidates] == [
        {"sku": "PARENT-1", "role": "parent", "target_visibility": 4, "parent_sku": None},
        {"sku": "CHILD-1", "role": "child", "target_visibility": 1, "parent_sku": "PARENT-1"},
        {"sku": "CHILD-2", "role": "child", "target_visibility": 1, "parent_sku": "PARENT-1"},
    ]


def test_find_visibility_repair_candidates_can_target_one_child():
    session = _FakeSession(
        [
            ("PARENT-1", "CHILD-1"),
            ("PARENT-1", "CHILD-2"),
        ]
    )

    candidates = find_visibility_repair_candidates(session, skus=["CHILD-2"])

    assert [item.to_dict() for item in candidates] == [
        {"sku": "CHILD-2", "role": "child", "target_visibility": 1, "parent_sku": "PARENT-1"},
    ]


class _FlakyApi:
    def __init__(self, failures_before_success):
        self.failures_before_success = failures_before_success
        self.calls = 0

    def update_product(self, sku, payload):
        self.calls += 1
        if self.calls <= self.failures_before_success:
            raise RuntimeError('{"message":"Database deadlock found when trying to get lock","code":1213}')
        return {"sku": sku, "visibility": payload["visibility"]}


def test_deadlock_error_detection_matches_magento_message():
    exc = RuntimeError('{"message":"Database deadlock found when trying to get lock","code":1213}')

    assert _is_magento_deadlock_error(exc) is True


def test_update_visibility_with_retry_recovers_from_deadlock(monkeypatch):
    monkeypatch.setattr(
        "app.jobs.repair_magento_configurable_visibility.time.sleep",
        lambda _seconds: None,
    )
    api = _FlakyApi(failures_before_success=2)
    candidate = find_visibility_repair_candidates(
        _FakeSession([("PARENT-1", "CHILD-1")]),
        skus=["PARENT-1"],
        include_children=False,
    )[0]

    _update_visibility_with_retry(
        api,
        candidate,
        retry_attempts=3,
        retry_delay_s=0.01,
    )

    assert api.calls == 3


def test_update_visibility_with_retry_raises_when_deadlock_exhausted(monkeypatch):
    monkeypatch.setattr(
        "app.jobs.repair_magento_configurable_visibility.time.sleep",
        lambda _seconds: None,
    )
    api = _FlakyApi(failures_before_success=5)
    candidate = find_visibility_repair_candidates(
        _FakeSession([("PARENT-1", "CHILD-1")]),
        skus=["PARENT-1"],
        include_children=False,
    )[0]

    try:
        _update_visibility_with_retry(
            api,
            candidate,
            retry_attempts=3,
            retry_delay_s=0.01,
        )
    except RuntimeError as exc:
        assert "deadlock" in str(exc).lower()
    else:
        raise AssertionError("expected deadlock error to be raised")
