# retoor <retoor@molodetz.nl>

from typing import Annotated

from fastapi import APIRouter, Form, Request
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse

from devplacepy.models import (
    GameCosmeticForm,
    GameInfraForm,
    GameLegacyForm,
    GameMasteryForm,
    GamePerkForm,
    GamePlantForm,
    GameQuestForm,
    GameSlotForm,
)
from devplacepy.database import mark_notifications_read_by_target
from devplacepy.responses import json_error, respond, wants_json
from devplacepy.schemas import GameLeaderboardOut, GameStateOut
from devplacepy.services.game import GameError, economy, store
from devplacepy.services.audit import record as audit
from devplacepy.utils import award_rewards, get_current_user, require_user, track_action

from ._shared import action_error, game_seo, notify_farm, state_payload

router = APIRouter()


@router.get("", response_class=HTMLResponse)
async def game_home(request: Request):
    user = require_user(request)
    mark_notifications_read_by_target(user["uid"], "/game")
    farm = state_payload(user)
    error = request.query_params.get("error", "")
    seo_ctx = game_seo(
        request,
        "Code Farm",
        "Grow software projects on your Code Farm, harvest coins and XP, "
        "upgrade your CI, and water your friends' builds.",
    )
    return respond(
        request,
        "game.html",
        {**seo_ctx, "request": request, "user": user, "farm": farm, "game_error": error},
        model=GameStateOut,
    )


@router.get("/state")
async def game_state(request: Request):
    user = require_user(request)
    return JSONResponse(GameStateOut(farm=state_payload(user)).model_dump(mode="json"))


@router.get("/leaderboard")
async def game_leaderboard(request: Request, board: str = "score"):
    get_current_user(request)
    entries = store.leaderboard_for(board, 25)
    return JSONResponse(
        GameLeaderboardOut(entries=entries).model_dump(mode="json")
    )


async def _respond_action(request: Request, user: dict, fn, on_success=None):
    try:
        result = fn()
    except GameError as exc:
        return action_error(request, str(exc), "/game")
    if on_success:
        on_success(result)
    await notify_farm(user.get("username", ""))
    if wants_json(request):
        return JSONResponse(
            GameStateOut(farm=state_payload(user)).model_dump(mode="json")
        )
    return RedirectResponse(url="/game", status_code=302)


@router.post("/plant")
async def game_plant(request: Request, data: Annotated[GamePlantForm, Form()]):
    user = require_user(request)

    def recorded(result):
        audit.record(
            request,
            "game.plant",
            user=user,
            metadata=result,
            summary=(
                f"{user['username']} planted {result['crop']} on plot "
                f"{result['slot']} for {result['spent']} coins"
            ),
        )

    return await _respond_action(
        request, user, lambda: store.plant(user, data.slot, data.crop), recorded
    )


@router.post("/harvest")
async def game_harvest(request: Request, data: Annotated[GameSlotForm, Form()]):
    user = require_user(request)

    def reward(result):
        track_action(user["uid"], "harvest")
        award_rewards(user["uid"], economy.site_xp_for(result.get("xp", 0)))
        audit.record(
            request,
            "game.harvest",
            user=user,
            metadata=result,
            summary=(
                f"{user['username']} harvested {result['crop']} on plot "
                f"{result['slot']} for {result['coins']} coins"
            ),
        )

    return await _respond_action(
        request, user, lambda: store.harvest(user, data.slot), reward
    )


@router.post("/buy-plot")
async def game_buy_plot(request: Request):
    user = require_user(request)

    def recorded(result):
        audit.record(
            request,
            "game.plot.buy",
            user=user,
            metadata=result,
            summary=(
                f"{user['username']} bought Code Farm plot "
                f"{result['plot_count']} for {result['spent']} coins"
            ),
        )

    return await _respond_action(request, user, lambda: store.buy_plot(user), recorded)


@router.post("/upgrade")
async def game_upgrade(request: Request):
    user = require_user(request)

    def recorded(result):
        audit.record(
            request,
            "game.ci.upgrade",
            user=user,
            metadata=result,
            summary=(
                f"{user['username']} upgraded Code Farm CI to tier "
                f"{result['ci_tier']} for {result['spent']} coins"
            ),
        )

    return await _respond_action(request, user, lambda: store.upgrade_ci(user), recorded)


@router.post("/fertilize")
async def game_fertilize(request: Request, data: Annotated[GameSlotForm, Form()]):
    user = require_user(request)

    def recorded(result):
        audit.record(
            request,
            "game.fertilize",
            user=user,
            metadata=result,
            summary=(
                f"{user['username']} fertilized plot {result['slot']} "
                f"for {result['spent']} coins"
            ),
        )

    return await _respond_action(
        request, user, lambda: store.fertilize(user, data.slot), recorded
    )


@router.post("/daily")
async def game_daily(request: Request):
    user = require_user(request)

    def recorded(result):
        audit.record(
            request,
            "game.daily.claim",
            user=user,
            metadata=result,
            summary=(
                f"{user['username']} claimed the Code Farm daily bonus of "
                f"{result['reward']} coins (streak {result['streak']})"
            ),
        )

    return await _respond_action(request, user, lambda: store.claim_daily(user), recorded)


@router.post("/grant")
async def game_claim_grant(request: Request):
    user = require_user(request)

    def recorded(result):
        audit.record(
            request,
            "game.grant.claim",
            user=user,
            metadata=result,
            summary=f"{user['username']} claimed a {result['amount']} coin community grant",
        )

    return await _respond_action(request, user, lambda: store.claim_grant(user), recorded)


@router.post("/perk")
async def game_perk(request: Request, data: Annotated[GamePerkForm, Form()]):
    user = require_user(request)

    def recorded(result):
        audit.record(
            request,
            "game.perk.upgrade",
            user=user,
            metadata=result,
            summary=(
                f"{user['username']} upgraded perk {result['perk']} to level "
                f"{result['level']} for {result['spent']} coins"
            ),
        )

    return await _respond_action(
        request, user, lambda: store.upgrade_perk(user, data.perk), recorded
    )


@router.post("/prestige")
async def game_prestige(request: Request):
    user = require_user(request)

    def recorded(result):
        audit.record(
            request,
            "game.prestige",
            user=user,
            metadata=result,
            summary=(
                f"{user['username']} refactored to prestige {result['prestige']} "
                f"for {result['fee']} coins"
            ),
        )

    return await _respond_action(request, user, lambda: store.prestige(user), recorded)


@router.post("/legacy")
async def game_legacy(request: Request, data: Annotated[GameLegacyForm, Form()]):
    user = require_user(request)

    def recorded(result):
        audit.record(
            request,
            "game.legacy.upgrade",
            user=user,
            metadata=result,
            summary=(
                f"{user['username']} upgraded Legacy {result['key']} to level "
                f"{result['level']} for {result['spent']} stars"
            ),
        )

    return await _respond_action(
        request, user, lambda: store.upgrade_legacy(user, data.key), recorded
    )


@router.post("/quests/claim")
async def game_claim_quest(request: Request, data: Annotated[GameQuestForm, Form()]):
    user = require_user(request)

    def reward(result):
        award_rewards(user["uid"], economy.site_xp_for(result.get("reward_xp", 0)))
        audit.record(
            request,
            "game.quest.claim",
            user=user,
            metadata=result,
            summary=(
                f"{user['username']} claimed quest {result['kind']} for "
                f"{result['reward_coins']} coins"
            ),
        )

    return await _respond_action(
        request, user, lambda: store.claim_quest(user, data.quest, data.scope), reward
    )


@router.post("/defense/upgrade")
async def game_upgrade_defense(request: Request):
    user = require_user(request)

    def reward(result):
        track_action(user["uid"], "defense_upgraded")
        audit.record(
            request,
            "game.defense.upgrade",
            user=user,
            metadata=result,
            summary=(
                f"{user['username']} bought Code Farm defense level "
                f"{result['defense_level']} for {result['spent']} coins"
            ),
        )

    return await _respond_action(request, user, lambda: store.upgrade_defense(user), reward)


@router.post("/defense/downgrade")
async def game_downgrade_defense(request: Request):
    user = require_user(request)

    def recorded(result):
        audit.record(
            request,
            "game.defense.downgrade",
            user=user,
            metadata=result,
            summary=(
                f"{user['username']} dropped Code Farm defense to level "
                f"{result['defense_level']}"
            ),
        )

    return await _respond_action(
        request, user, lambda: store.downgrade_defense(user), recorded
    )


@router.post("/infrastructure/buy")
async def game_buy_infrastructure(request: Request, data: Annotated[GameInfraForm, Form()]):
    user = require_user(request)

    def reward(result):
        track_action(user["uid"], "infra_bought")
        audit.record(
            request,
            "game.infrastructure.buy",
            user=user,
            metadata=result,
            summary=(
                f"{user['username']} bought Code Farm infrastructure {result['key']} "
                f"for {result['spent']} coins"
            ),
        )

    return await _respond_action(
        request, user, lambda: store.buy_infrastructure(user, data.key), reward
    )


@router.post("/mastery")
async def game_upgrade_mastery(request: Request, data: Annotated[GameMasteryForm, Form()]):
    user = require_user(request)

    def recorded(result):
        audit.record(
            request,
            "game.mastery.upgrade",
            user=user,
            metadata=result,
            summary=(
                f"{user['username']} upgraded Mastery {result['key']} to level "
                f"{result['level']} for {result['spent']} mastery points"
            ),
        )

    return await _respond_action(
        request, user, lambda: store.upgrade_mastery(user, data.key), recorded
    )


@router.post("/cosmetics/buy")
async def game_buy_cosmetic(request: Request, data: Annotated[GameCosmeticForm, Form()]):
    user = require_user(request)

    def reward(result):
        track_action(user["uid"], "cosmetic_bought")
        audit.record(
            request,
            "game.cosmetic.buy",
            user=user,
            metadata=result,
            summary=(
                f"{user['username']} bought Code Farm cosmetic {result['key']} "
                f"for {result['spent']} coins"
            ),
        )

    return await _respond_action(
        request, user, lambda: store.buy_cosmetic(user, data.key), reward
    )


@router.post("/cosmetics/equip")
async def game_equip_cosmetic(request: Request, data: Annotated[GameCosmeticForm, Form()]):
    user = require_user(request)

    def recorded(result):
        audit.record(
            request,
            "game.cosmetic.equip",
            user=user,
            metadata=result,
            summary=f"{user['username']} equipped Code Farm title {result['active_title']}",
        )

    return await _respond_action(
        request, user, lambda: store.equip_title(user, data.key), recorded
    )
