# 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
elapsed_days = int((now - last).total_seconds() // 86400)
if elapsed_days < 1:
return farm
days_due = min(elapsed_days, economy.UPKEEP_GRACE_DAYS)
tier = economy.defense_tier(level)
coins = int(farm.get("coins", 0))
total_due = economy.daily_upkeep(tier, coins) * days_due
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)
rows = conditional_update_farm(
farm["uid"],
set_clause="coins = 0, 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={"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
return {
**farm,
"coins": 0,
"defense_level": new_level,
"defense_last_upkeep_at": new_timestamp,
}