# retoor <retoor@molodetz.nl>

import pytest

from devplacepy.database import (
    CONSENTS_TABLE,
    consent_granted,
    get_setting,
    get_table,
    set_consent,
    set_setting,
)
from devplacepy.services.acceptance.agreements import agreement_for
from devplacepy.services.acceptance.grant import converge_user
from devplacepy.services.audit.store import AUDIT_TABLE
from devplacepy.utils import generate_uid

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


@pytest.fixture
def account(local_db):
    uid = generate_uid()
    get_table("users").insert(
        {
            "uid": uid,
            "username": f"grantprobe_{uid[:8]}",
            "email": f"grantprobe_{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 get_table("users").find_one(uid=uid)
    get_table("users").delete(uid=uid)
    get_table(CONSENTS_TABLE).delete(owner_id=uid)
    get_table(AUDIT_TABLE).delete(target_uid=uid)


@pytest.fixture
def versions():
    before = get_setting("terms_version", "1")
    yield
    set_setting("terms_version", before)


def consent_rows(uid, kind):
    return list(
        get_table(CONSENTS_TABLE).find(
            owner_kind="user", owner_id=uid, kind=kind, deleted_at=None
        )
    )


def audit_rows(uid, event_key):
    return list(
        get_table(AUDIT_TABLE).find(
            target_uid=uid, event_key=event_key, actor_kind="service"
        )
    )


def test_a_won_terms_claim_writes_the_gate_column_and_the_ledger(account, versions):
    set_setting("terms_version", "5")
    assert converge_user(TERMS, account, "5") is True
    row = get_table("users").find_one(uid=account["uid"])
    assert row["terms_version"] == "5"
    assert row["terms_accepted_at"]
    written = consent_rows(account["uid"], "terms")
    assert len(written) == 1
    assert written[0]["state"] == "granted"
    assert written[0]["version"] == "5"


def test_a_second_claim_at_the_same_version_writes_nothing(account, versions):
    set_setting("terms_version", "5")
    assert converge_user(TERMS, account, "5") is True
    assert converge_user(TERMS, account, "5") is False
    assert len(consent_rows(account["uid"], "terms")) == 1
    assert len(audit_rows(account["uid"], "terms.accept")) == 1


def test_a_withdrawn_terms_consent_is_never_converged(account, versions):
    set_setting("terms_version", "5")
    set_consent("user", account["uid"], "terms", False, version="5")
    assert converge_user(TERMS, account, "5") is False
    assert get_table("users").find_one(uid=account["uid"])["terms_version"] == ""


def test_a_withdrawn_ledger_agreement_is_never_converged(account):
    set_consent("user", account["uid"], "ai_third_party", False)
    assert converge_user(AI, account, "1") is False
    assert not consent_granted("user", account["uid"], "ai_third_party")


def test_a_ledger_agreement_converges_once(account):
    assert converge_user(AI, account, "1") is True
    assert consent_granted("user", account["uid"], "ai_third_party")
    assert converge_user(AI, account, "1") is False
    granted = [
        row for row in consent_rows(account["uid"], "ai_third_party")
        if row["state"] == "granted"
    ]
    assert len(granted) == 1


def test_the_written_row_matches_the_human_path_field_for_field(account):
    reference = set_consent("user", account["uid"], "container_credentials", True)
    get_table(CONSENTS_TABLE).delete(uid=reference["uid"])
    assert converge_user(AI, account, "1") is True
    written = consent_rows(account["uid"], "ai_third_party")[0]
    assert set(written.keys()) == set(reference.keys())
    assert written["owner_kind"] == reference["owner_kind"]
    assert written["state"] == reference["state"]
    assert written["withdrawn_at"] == reference["withdrawn_at"]
    assert written["deleted_at"] is None
    assert written["deleted_by"] is None
    assert written["granted_at"] == written["created_at"]


def test_a_won_claim_records_an_existing_event_key_as_a_service_actor(account, versions):
    set_setting("terms_version", "5")
    converge_user(TERMS, account, "5")
    converge_user(AI, account, "1")
    accepted = audit_rows(account["uid"], "terms.accept")
    granted = audit_rows(account["uid"], "consent.grant")
    assert len(accepted) == 1
    assert len(granted) == 1
    assert accepted[0]["actor_username"] == "acceptance"
    assert accepted[0]["origin"] == "service"
    assert accepted[0]["new_value"] == "5"
    assert granted[0]["new_value"] == "granted"


def test_a_lost_claim_records_nothing(account, versions):
    set_setting("terms_version", "5")
    converge_user(TERMS, account, "5")
    converge_user(TERMS, account, "5")
    assert len(audit_rows(account["uid"], "terms.accept")) == 1


def test_an_account_without_a_uid_is_refused():
    assert converge_user(AI, {}, "1") is False
