# retoor <retoor@molodetz.nl>

import pytest

from devplacepy.database import CONSENTS_TABLE, get_setting, get_table, set_setting
from devplacepy.services.acceptance.agreements import agreement_for
from devplacepy.services.acceptance.pending import (
    LIVE_ACCOUNT_CLAUSES,
    current_version,
    live_account_clauses,
    pending,
)
from devplacepy.utils import generate_uid

TERMS = agreement_for("terms")
PRIVACY = agreement_for("privacy")
AI = agreement_for("ai_third_party")


@pytest.fixture
def account(local_db):
    uid = generate_uid()
    get_table("users").insert(
        {
            "uid": uid,
            "username": f"pendingprobe_{uid[:8]}",
            "email": f"pendingprobe_{uid[:8]}@example.com",
            "password_hash": "x",
            "role": "Member",
            "terms_version": "",
            "terms_accepted_at": "",
            "deletion_requested_at": "",
            "created_at": "2020-01-01T00:00:00",
        }
    )
    yield uid
    get_table("users").delete(uid=uid)
    get_table(CONSENTS_TABLE).delete(owner_id=uid)


@pytest.fixture
def versions():
    before = {
        "terms_version": get_setting("terms_version", "1"),
        "privacy_version": get_setting("privacy_version", "1"),
    }
    yield
    for key, value in before.items():
        set_setting(key, value)


def write_consent(uid, kind, state, version):
    get_table(CONSENTS_TABLE).insert(
        {
            "uid": generate_uid(),
            "owner_kind": "user",
            "owner_id": uid,
            "kind": kind,
            "version": version,
            "state": state,
            "granted_at": "2021-01-01T00:00:00" if state == "granted" else "",
            "withdrawn_at": "" if state == "granted" else "2021-01-01T00:00:00",
            "created_at": "2021-01-01T00:00:00",
            "deleted_at": None,
            "deleted_by": None,
        }
    )


def is_pending(agreement, uid):
    return uid in {row["uid"] for row in pending(agreement, 5000)}


def test_an_account_that_never_accepted_is_pending(account):
    assert is_pending(TERMS, account)
    assert is_pending(AI, account)


def test_a_null_gate_column_is_pending_not_invisible(account, versions):
    set_setting("terms_version", "1")
    get_table("users").update({"uid": account, "terms_version": None}, ["uid"])
    assert is_pending(TERMS, account)


def test_a_satisfied_gate_column_is_not_pending(account, versions):
    set_setting("terms_version", "7")
    get_table("users").update({"uid": account, "terms_version": "7"}, ["uid"])
    assert not is_pending(TERMS, account)


def test_a_stale_gate_column_is_pending_again(account, versions):
    set_setting("terms_version", "7")
    get_table("users").update({"uid": account, "terms_version": "7"}, ["uid"])
    set_setting("terms_version", "8")
    assert is_pending(TERMS, account)


def test_a_withdrawal_removes_the_account_permanently(account, versions):
    set_setting("terms_version", "1")
    write_consent(account, "terms", "withdrawn", "1")
    assert not is_pending(TERMS, account)
    set_setting("terms_version", "2")
    assert not is_pending(TERMS, account)
    get_table("users").update({"uid": account, "terms_version": None}, ["uid"])
    assert not is_pending(TERMS, account)


def test_an_unversioned_agreement_is_satisfied_by_a_granted_row(account):
    write_consent(account, "ai_third_party", "granted", "1")
    assert not is_pending(AI, account)


def test_a_versioned_ledger_agreement_tracks_the_policy_version(account, versions):
    set_setting("privacy_version", "3")
    write_consent(account, "privacy", "granted", "3")
    assert not is_pending(PRIVACY, account)
    set_setting("privacy_version", "4")
    assert is_pending(PRIVACY, account)


def test_a_legacy_consent_row_without_a_version_is_stale(account, versions):
    set_setting("privacy_version", "1")
    write_consent(account, "privacy", "granted", None)
    assert is_pending(PRIVACY, account)


def test_a_deleted_account_is_never_pending(account):
    get_table("users").update(
        {"uid": account, "deletion_requested_at": "2024-01-01T00:00:00"}, ["uid"]
    )
    assert not is_pending(TERMS, account)


def test_a_deactivated_account_is_never_pending(account):
    get_table("users").update({"uid": account, "is_active": False}, ["uid"])
    assert not is_pending(TERMS, account)


def test_the_batch_size_bounds_the_sweep_and_the_order_is_stable(account):
    first = [row["uid"] for row in pending(TERMS, 1)]
    second = [row["uid"] for row in pending(TERMS, 1)]
    assert len(first) <= 1
    assert first == second


def test_a_zero_batch_size_still_returns_at_most_one_row(account):
    assert len(pending(TERMS, 0)) <= 1


def test_the_live_account_clauses_only_name_existing_columns(local_db):
    users = get_table("users")
    expected = [
        clause
        for column, clause in LIVE_ACCOUNT_CLAUSES
        if users.has_column(column)
    ]
    assert live_account_clauses() == expected


def test_an_empty_version_setting_falls_back_to_one(versions):
    set_setting("terms_version", "")
    assert current_version(TERMS) == "1"


def test_an_unversioned_agreement_reports_version_one():
    assert current_version(AI) == "1"
