# retoor <retoor@molodetz.nl>

from datetime import datetime, timedelta, timezone

import pytest
from devplacepy.database import (
    can_give_award,
    can_receive_award,
    get_table,
    has_giver_cooldown,
    has_receiver_cooldown,
    init_db,
    recompute_user_award_stats,
    revoke_award,
)
from devplacepy.utils import generate_uid, make_combined_slug


@pytest.fixture(autouse=True)
def _db(local_db):
    init_db()
    yield


def _user(prefix="award"):
    uid = generate_uid()
    get_table("users").insert(
        {
            "uid": uid,
            "username": f"{prefix}_{uid[:8]}",
            "terms_version": "1",
            "email": f"{uid[:8]}@t.dev",
            "created_at": datetime.now(timezone.utc).isoformat(),
            "award_count": 0,
            "last_award_at": None,
            "last_award_slug": None,
            "last_award_uid": None,
        }
    )
    return uid


def _award(giver_uid, receiver_uid, description="Great work", generated_at=None):
    uid = generate_uid()
    slug = make_combined_slug(description, uid)
    now = datetime.now(timezone.utc).isoformat()
    get_table("awards").insert(
        {
            "uid": uid,
            "slug": slug,
            "description": description,
            "giver_uid": giver_uid,
            "receiver_uid": receiver_uid,
            "attachment_uid_512": "",
            "attachment_uid_256": "",
            "attachment_uid_64": "",
            "generated_at": generated_at,
            "created_at": now,
            "job_uid": "",
            "deleted_at": None,
            "deleted_by": None,
        }
    )
    return uid, slug


def _attachment(user_uid):
    uid = generate_uid()
    get_table("attachments").insert(
        {
            "uid": uid,
            "target_type": "award",
            "target_uid": "",
            "user_uid": user_uid,
            "original_filename": "award.png",
            "stored_name": f"{uid}.png",
            "directory": "aa/bb",
            "file_size": 10,
            "mime_type": "image/png",
            "created_at": datetime.now(timezone.utc).isoformat(),
            "deleted_at": None,
            "deleted_by": None,
        }
    )
    return uid


def test_slug_matches_make_combined_slug():
    uid = generate_uid()
    slug = make_combined_slug("Ship it", uid)
    assert slug.endswith("-ship-it") or slug == uid.replace("-", "")[-12:]


def test_can_give_award_respects_giver_cooldown(monkeypatch):
    giver = _user("giver")
    receiver = _user("recv")
    _award(giver, receiver)
    monkeypatch.setattr(
        "devplacepy.database.awards.get_int_setting",
        lambda key, default: 24 if "cooldown" in key else default,
    )
    assert has_giver_cooldown(giver) is True
    assert can_give_award(giver, receiver) is False


def test_can_receive_award_respects_receiver_cooldown(monkeypatch):
    giver = _user("giver2")
    receiver = _user("recv2")
    _award(giver, receiver)
    monkeypatch.setattr(
        "devplacepy.database.awards.get_int_setting",
        lambda key, default: 24 if "receive" in key else default,
    )
    assert has_receiver_cooldown(receiver) is True
    assert can_receive_award(receiver) is False


def test_cooldown_ignores_soft_deleted_rows():
    giver = _user("giver3")
    receiver = _user("recv3")
    uid, _ = _award(giver, receiver)
    get_table("awards").update(
        {"uid": uid, "deleted_at": datetime.now(timezone.utc).isoformat(), "deleted_by": giver},
        ["uid"],
    )
    assert has_giver_cooldown(giver) is False
    assert has_receiver_cooldown(receiver) is False


def test_recompute_user_award_stats_sets_latest_generated():
    receiver = _user("recv4")
    giver = _user("giver4")
    older = datetime.now(timezone.utc) - timedelta(hours=2)
    newer = datetime.now(timezone.utc) - timedelta(hours=1)
    uid_old, slug_old = _award(giver, receiver, "older", older.isoformat())
    uid_new, slug_new = _award(giver, receiver, "newer", newer.isoformat())
    recompute_user_award_stats(receiver)
    row = get_table("users").find_one(uid=receiver)
    assert row["award_count"] == 2
    assert row["last_award_uid"] == uid_new
    assert row["last_award_slug"] == slug_new
    assert uid_old != uid_new
    assert slug_old != slug_new


def test_revoke_latest_recomputes_to_previous():
    receiver = _user("recv5")
    giver = _user("giver5")
    older = datetime.now(timezone.utc) - timedelta(hours=2)
    newer = datetime.now(timezone.utc) - timedelta(hours=1)
    uid_old, slug_old = _award(giver, receiver, "keep", older.isoformat())
    uid_new, _ = _award(giver, receiver, "gone", newer.isoformat())
    recompute_user_award_stats(receiver)
    revoke_award(uid_new, giver)
    row = get_table("users").find_one(uid=receiver)
    assert row["award_count"] == 1
    assert row["last_award_uid"] == uid_old
    assert row["last_award_slug"] == slug_old


def test_revoke_clears_stats_when_count_zero():
    receiver = _user("recv6")
    giver = _user("giver6")
    now = datetime.now(timezone.utc).isoformat()
    uid, _ = _award(giver, receiver, "only", now)
    recompute_user_award_stats(receiver)
    revoke_award(uid, giver)
    row = get_table("users").find_one(uid=receiver)
    assert row["award_count"] == 0
    assert not row.get("last_award_uid")


def test_revoke_soft_deletes_attachments_under_one_stamp():
    receiver = _user("recv7")
    giver = _user("giver7")
    admin = _user("admin7")
    now = datetime.now(timezone.utc).isoformat()
    uid, _ = _award(giver, receiver, "with files", now)
    a512 = _attachment(receiver)
    a256 = _attachment(giver)
    a64 = _attachment(giver)
    get_table("awards").update(
        {
            "uid": uid,
            "attachment_uid_512": a512,
            "attachment_uid_256": a256,
            "attachment_uid_64": a64,
        },
        ["uid"],
    )
    revoke_award(uid, admin)
    award = get_table("awards").find_one(uid=uid)
    assert award.get("deleted_at")
    stamps = {
        get_table("attachments").find_one(uid=a512).get("deleted_at"),
        get_table("attachments").find_one(uid=a256).get("deleted_at"),
        get_table("attachments").find_one(uid=a64).get("deleted_at"),
    }
    assert len(stamps) == 1
    assert award.get("deleted_at") in stamps