# retoor <retoor@molodetz.nl>

from __future__ import annotations

from datetime import datetime, timedelta

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


def upgrade_defense(user: dict) -> dict:
    farm = ensure_farm(user["uid"])
    level = _lvl(farm, "defense_level")
    next_tier = economy.next_defense_tier(level)
    if not next_tier:
        raise GameError("Defense is already at the top tier.")
    rows = conditional_update_farm(
        farm["uid"],
        set_clause="coins = coins - :cost, defense_level = :new_level",
        where_clause="COALESCE(defense_level, 0) = :current_level AND coins >= :cost",
        params={"cost": next_tier.upgrade_cost, "new_level": next_tier.level, "current_level": level},
    )
    if rows == 0:
        farm = get_farm(user["uid"])
        current_level = _lvl(farm, "defense_level")
        if current_level != level:
            raise GameError("Defense already changed - refresh and try again.")
        raise GameError("Not enough coins to upgrade defense.")
    return {"defense_level": next_tier.level, "spent": next_tier.upgrade_cost}


def charge_upkeep(farm: dict, now: datetime) -> dict:
    level = _lvl(farm, "defense_level")
    if level <= 0:
        return farm
    last_raw = farm.get("defense_last_upkeep_at") or ""
    last = _parse(last_raw)
    if not last:
        rows = conditional_update_farm(
            farm["uid"],
            set_clause="defense_last_upkeep_at = :ts",
            where_clause="(defense_last_upkeep_at IS NULL OR defense_last_upkeep_at = '') AND COALESCE(defense_level, 0) = :level",
            params={"ts": _iso(now), "level": level},
        )
        if rows:
            return {**farm, "defense_last_upkeep_at": _iso(now)}
        return get_farm(farm["user_uid"]) or farm
    if not _lvl(farm, "upkeep_amnesty"):
        rows = conditional_update_farm(
            farm["uid"],
            set_clause="upkeep_amnesty = 1, defense_last_upkeep_at = :ts",
            where_clause="COALESCE(upkeep_amnesty, 0) = 0",
            params={"ts": _iso(now)},
        )
        if rows:
            return {**farm, "upkeep_amnesty": 1, "defense_last_upkeep_at": _iso(now)}
        return get_farm(farm["user_uid"]) or farm
    elapsed_days = int((now - last).total_seconds() // 86400)
    if elapsed_days < 1:
        return farm
    tier = economy.defense_tier(level)
    coins = int(farm.get("coins", 0))
    total_due = economy.daily_upkeep(tier, coins) * elapsed_days
    new_timestamp = _iso(last + timedelta(days=elapsed_days))
    if coins >= total_due:
        rows = conditional_update_farm(
            farm["uid"],
            set_clause="coins = coins - :total_due, defense_last_upkeep_at = :new_ts",
            where_clause="defense_last_upkeep_at = :expected_last AND COALESCE(defense_level, 0) = :expected_level AND coins >= :total_due",
            params={
                "total_due": total_due,
                "new_ts": new_timestamp,
                "expected_last": last_raw,
                "expected_level": level,
            },
        )
        if rows == 0:
            return get_farm(farm["user_uid"]) or farm
        return {**farm, "coins": coins - total_due, "defense_last_upkeep_at": new_timestamp}
    new_level = max(0, level - 1)
    paid = min(coins, total_due)
    rows = conditional_update_farm(
        farm["uid"],
        set_clause=(
            "coins = MAX(0, COALESCE(coins, 0) - :paid), "
            "defense_level = :new_level, defense_last_upkeep_at = :new_ts"
        ),
        where_clause="defense_last_upkeep_at = :expected_last AND COALESCE(defense_level, 0) = :expected_level",
        params={
            "paid": paid,
            "new_level": new_level,
            "new_ts": new_timestamp,
            "expected_last": last_raw,
            "expected_level": level,
        },
    )
    if rows == 0:
        return get_farm(farm["user_uid"]) or farm
    _notify_decay(farm, level, new_level, paid)
    return {
        **farm,
        "coins": max(0, coins - paid),
        "defense_level": new_level,
        "defense_last_upkeep_at": new_timestamp,
    }


def _notify_decay(farm: dict, old_level: int, new_level: int, paid: int) -> None:
    from devplacepy.utils import create_notification

    try:
        create_notification(
            farm["user_uid"],
            "game_upkeep",
            (
                f"Your Code Farm defense dropped from {economy.defense_tier(old_level).name} "
                f"to {economy.defense_tier(new_level).name}: unpaid upkeep of {paid} coins."
            ),
            None,
            "/game",
        )
    except Exception:
        return


def downgrade_defense(user: dict) -> dict:
    farm = ensure_farm(user["uid"])
    level = _lvl(farm, "defense_level")
    if level <= 0:
        raise GameError("Defense is already at level 0.")
    rows = conditional_update_farm(
        farm["uid"],
        set_clause="defense_level = :new_level",
        where_clause="COALESCE(defense_level, 0) = :current_level",
        params={"new_level": level - 1, "current_level": level},
    )
    if rows == 0:
        raise GameError("Defense already changed - refresh and try again.")
    return {"defense_level": level - 1}
