# retoor <retoor@molodetz.nl>

from __future__ import annotations

from datetime import datetime

from devplacepy.cache import TTLCache
from devplacepy.database import get_table

from .. import economy
from .common import GameError, _iso, _iso_week, _lvl, _now, conditional_update_farm
from .farm import ensure_farm, get_farm

TREASURY_UID = "treasury-main"


def _treasury():
    return get_table("game_treasury")


def ensure_treasury() -> dict:
    row = _treasury().find_one(uid=TREASURY_UID)
    if row:
        return row
    _treasury().insert(
        {
            "uid": TREASURY_UID,
            "balance": 0,
            "collected_total": 0,
            "granted_total": 0,
            "updated_at": _iso(_now()),
        }
    )
    return _treasury().find_one(uid=TREASURY_UID)


def treasury_balance() -> int:
    row = _treasury().find_one(uid=TREASURY_UID)
    return int(row.get("balance") or 0) if row else 0


def _treasury_update(set_clause: str, where_clause: str, params: dict) -> int:
    from sqlalchemy import text

    from devplacepy.database import db

    sql = (
        f"UPDATE game_treasury SET {set_clause}, updated_at = :updated_at "
        f"WHERE uid = :treasury_uid AND ({where_clause})"
    )
    bind = {**params, "updated_at": _iso(_now()), "treasury_uid": TREASURY_UID}
    with db:
        result = db.executable.execute(text(sql), bind)
        return result.rowcount


def credit_treasury(amount: int) -> None:
    if amount <= 0:
        return
    ensure_treasury()
    _treasury_update(
        set_clause=(
            "balance = COALESCE(balance, 0) + :amount, "
            "collected_total = COALESCE(collected_total, 0) + :amount"
        ),
        where_clause="1 = 1",
        params={"amount": amount},
    )


def _debit_treasury(amount: int) -> bool:
    ensure_treasury()
    rows = _treasury_update(
        set_clause=(
            "balance = COALESCE(balance, 0) - :amount, "
            "granted_total = COALESCE(granted_total, 0) + :amount"
        ),
        where_clause="COALESCE(balance, 0) >= :amount",
        params={"amount": amount},
    )
    return rows > 0


def _refund_treasury(amount: int) -> None:
    if amount <= 0:
        return
    _treasury_update(
        set_clause=(
            "balance = COALESCE(balance, 0) + :amount, "
            "granted_total = COALESCE(granted_total, 0) - :amount"
        ),
        where_clause="1 = 1",
        params={"amount": amount},
    )


_eligible_cache = TTLCache(ttl=60, max_size=4)


def eligible_grant_farms(week: str) -> int:
    cached = _eligible_cache.get(week)
    if cached is not None:
        return cached
    from devplacepy.database import db

    rows = db.query(
        "SELECT COUNT(*) AS eligible FROM game_farms "
        "WHERE COALESCE(coins, 0) < :ceiling "
        "AND COALESCE(prestige, 0) <= :max_prestige "
        "AND COALESCE(harvests_week, 0) >= :min_harvests "
        "AND COALESCE(last_grant_week, '') != :week",
        ceiling=economy.GRANT_WEALTH_CEILING,
        max_prestige=economy.GRANT_MAX_PRESTIGE,
        min_harvests=economy.GRANT_MIN_WEEK_HARVESTS,
        week=week,
    )
    total = 1
    for row in rows:
        total = max(1, int(row.get("eligible") or 0))
        break
    _eligible_cache.set(week, total)
    return total


def grant_status(farm: dict, now: datetime) -> dict:
    week = _iso_week(now)
    if farm.get("last_grant_week") == week:
        return {"available": False, "amount": 0, "reason": "Grant already claimed this week."}
    if _lvl(farm, "prestige") > economy.GRANT_MAX_PRESTIGE:
        return {
            "available": False,
            "amount": 0,
            "reason": f"Grants support farms up to prestige {economy.GRANT_MAX_PRESTIGE}.",
        }
    if int(farm.get("coins", 0)) >= economy.GRANT_WEALTH_CEILING:
        return {
            "available": False,
            "amount": 0,
            "reason": f"Grants support farms below {economy.GRANT_WEALTH_CEILING} coins.",
        }
    if _lvl(farm, "harvests_week") < economy.GRANT_MIN_WEEK_HARVESTS:
        return {
            "available": False,
            "amount": 0,
            "reason": (
                f"Harvest at least {economy.GRANT_MIN_WEEK_HARVESTS} builds this week to qualify."
            ),
        }
    amount = economy.grant_amount(treasury_balance(), eligible_grant_farms(week))
    if amount <= 0:
        return {
            "available": False,
            "amount": 0,
            "reason": "The treasury has too little to share this week - refactor fees fill it.",
        }
    return {"available": True, "amount": amount, "reason": ""}


def claim_grant(user: dict) -> dict:
    farm = ensure_farm(user["uid"])
    now = _now()
    status = grant_status(farm, now)
    if not status["available"]:
        raise GameError(status["reason"])
    amount = status["amount"]
    week = _iso_week(now)
    if not _debit_treasury(amount):
        raise GameError("The treasury is empty - refactor fees fill it.")
    rows = conditional_update_farm(
        farm["uid"],
        set_clause=(
            "coins = coins + :amount, "
            "last_grant_week = :week, "
            "lifetime_coins_earned = COALESCE(lifetime_coins_earned, 0) + :amount"
        ),
        where_clause=(
            "(last_grant_week IS NULL OR last_grant_week != :week) AND coins < :ceiling"
        ),
        params={"amount": amount, "week": week, "ceiling": economy.GRANT_WEALTH_CEILING},
    )
    if rows == 0:
        _refund_treasury(amount)
        farm = get_farm(user["uid"])
        if farm and farm.get("last_grant_week") == week:
            raise GameError("Grant already claimed this week.")
        raise GameError("Your farm changed - refresh and try again.")
    return {"amount": amount, "week": week}
