# retoor <retoor@molodetz.nl>

from datetime import datetime, timedelta, timezone

from playwright.sync_api import expect

from tests.conftest import BASE_URL


def reset_farm(username, coins=100000):
    from devplacepy.database import get_table
    from devplacepy.services.game import store

    user = get_table("users").find_one(username=username)
    uid = user["uid"]
    farm = store.get_farm(uid)
    if farm:
        get_table("game_plots").delete(farm_uid=farm["uid"])
        get_table("game_quests").delete(farm_uid=farm["uid"])
        get_table("game_farms").delete(uid=farm["uid"])
    get_table("game_cosmetics").delete(user_uid=uid)
    get_table("game_steals").delete(thief_uid=uid)
    get_table("game_steals").delete(owner_uid=uid)
    get_table("game_market_ticks").delete()
    clear_game_caches()
    farm = store.ensure_farm(uid)
    get_table("game_farms").update({"uid": farm["uid"], "coins": coins}, ["uid"])
    return {"uid": uid, "username": username}


def warp_ready(username):
    from devplacepy.database import get_table
    from devplacepy.services.game import store

    user = get_table("users").find_one(username=username)
    farm = store.get_farm(user["uid"])
    past = (datetime.now(timezone.utc) - timedelta(seconds=5)).isoformat()
    for plot in store.get_plots(farm["uid"]):
        if plot.get("crop_key"):
            get_table("game_plots").update(
                {"uid": plot["uid"], "ready_at": past}, ["uid"]
            )


def set_farm_xp(username, xp):
    from devplacepy.database import get_table
    from devplacepy.services.game import store

    user = get_table("users").find_one(username=username)
    farm = store.get_farm(user["uid"])
    get_table("game_farms").update({"uid": farm["uid"], "xp": xp}, ["uid"])


def set_farm(username, **fields):
    from devplacepy.database import get_table
    from devplacepy.services.game import store

    user = get_table("users").find_one(username=username)
    farm = store.get_farm(user["uid"])
    get_table("game_farms").update({"uid": farm["uid"], **fields}, ["uid"])


def clear_game_caches():
    from devplacepy.services.game.store import farm as farm_store
    from devplacepy.services.game.store import market as market_store

    farm_store._leaderboard_cache.clear()
    farm_store._board_cache.clear()
    market_store._saturation_cache.clear()


def open_game(page):
    page.goto(f"{BASE_URL}/game", wait_until="domcontentloaded")
    page.locator("[data-game-grid]").first.wait_for(state="visible")
    page.wait_for_timeout(800)


def hud_number(value):
    from devplacepy.templating import format_number

    return format_number(value)


def coins(page):
    node = page.locator("[data-hud-coins]")
    exact = node.get_attribute("title")
    if exact:
        return int(exact.replace(",", ""))
    return int(node.inner_text().replace(",", ""))


def test_game_page_loads(alice):
    page, _ = alice
    reset_farm("alice_test")
    open_game(page)
    assert page.locator(".game-header h1").is_visible()
    expect(page.locator("[data-hud-coins]")).to_have_text(hud_number(100000))
    expect(page.locator("[data-game-grid] [data-slot]")).to_have_count(4)
    expect(page.locator("[data-perk-host] .perk-card")).to_have_count(4)
    expect(page.locator(".quest-item")).to_have_count(3)


def test_guest_redirected_to_login(page):
    page.goto(f"{BASE_URL}/game", wait_until="domcontentloaded")
    page.wait_for_timeout(500)
    assert "/auth/login" in page.url


def test_plant_crop(alice):
    page, _ = alice
    reset_farm("alice_test")
    open_game(page)
    form = page.locator("form[data-game-action='plant']").first
    form.locator("select[name='crop']").select_option("shell")
    form.locator("button").click()
    expect(page.locator(".game-plot-growing")).to_have_count(1)
    expect(page.locator("[data-hud-coins]")).to_have_text(hud_number(99995))


def test_harvest_crop(alice):
    page, _ = alice
    reset_farm("alice_test")
    open_game(page)
    form = page.locator("form[data-game-action='plant']").first
    form.locator("select[name='crop']").select_option("shell")
    form.locator("button").click()
    expect(page.locator(".game-plot-growing")).to_have_count(1)
    warp_ready("alice_test")
    open_game(page)
    page.locator("form[data-game-action='harvest'] button").first.click()
    expect(page.locator("[data-hud-coins]")).not_to_have_text(hud_number(99995))
    assert coins(page) > 99995
    expect(page.locator(".game-plot-growing")).to_have_count(0)


def test_fertilize_spends_coins(alice):
    page, _ = alice
    reset_farm("alice_test")
    open_game(page)
    form = page.locator("form[data-game-action='plant']").first
    form.locator("select[name='crop']").select_option("python")
    form.locator("button").click()
    expect(page.locator("[data-hud-coins]")).to_have_text(hud_number(99985))
    fert = page.locator("form[data-game-action='fertilize'] button").first
    fert.wait_for(state="visible")
    fert.click()
    expect(page.locator("[data-hud-coins]")).not_to_have_text(hud_number(99985))
    assert coins(page) < 99985


def test_buy_plot(alice):
    page, _ = alice
    reset_farm("alice_test")
    open_game(page)
    page.locator("form[data-game-action='buy-plot'] button").click()
    expect(page.locator("[data-game-grid] [data-slot]")).to_have_count(5)
    expect(page.locator("[data-hud-coins]")).to_have_text(hud_number(99900))


def test_upgrade_ci(alice):
    page, _ = alice
    reset_farm("alice_test")
    open_game(page)
    page.locator("form[data-game-action='upgrade'] button").click()
    expect(page.locator("[data-hud-ci]")).to_have_text("2")
    expect(page.locator("[data-hud-coins]")).to_have_text(hud_number(99850))


def test_claim_daily(alice):
    page, _ = alice
    reset_farm("alice_test")
    open_game(page)
    page.locator("form[data-game-action='daily'] button").click()
    expect(page.locator("[data-hud-coins]")).to_have_text(hud_number(100020))
    expect(page.locator("[data-daily-host] .daily-claimed")).to_be_visible()


def test_upgrade_perk(alice):
    page, _ = alice
    reset_farm("alice_test")
    open_game(page)
    growth = page.locator(
        "form[data-game-action='perk']:has(input[value='growth']) button"
    )
    growth.click()
    expect(page.locator("[data-hud-coins]")).to_have_text(hud_number(99850))
    expect(
        page.locator(".perk-card:has-text('Build Cache') .perk-level")
    ).to_have_text("Lv 1/10")


def _set_farm_stars(username, stars):
    from devplacepy.database import get_table
    from devplacepy.services.game import store

    user = get_table("users").find_one(username=username)
    farm = store.get_farm(user["uid"])
    get_table("game_farms").update({"uid": farm["uid"], "stars": stars}, ["uid"])


def test_upgrade_legacy_spends_stars(alice):
    page, _ = alice
    reset_farm("alice_test")
    _set_farm_stars("alice_test", 1000)
    open_game(page)
    buy = page.locator(
        "form[data-game-action='legacy']:has(input[value='multiplier']) button"
    )
    buy.wait_for(state="visible")
    buy.click()
    expect(
        page.locator(".legacy-card:has-text('Tech Debt Payoff') .perk-level")
    ).to_have_text("Lv 1/10")


def test_claim_quest(alice):
    page, _ = alice
    from devplacepy.database import get_table
    from devplacepy.services.game import store

    farm_info = reset_farm("alice_test")
    open_game(page)
    farm = store.get_farm(farm_info["uid"])
    quest = list(get_table("game_quests").find(farm_uid=farm["uid"]))[0]
    get_table("game_quests").update(
        {"uid": quest["uid"], "progress": quest["goal"]}, ["uid"]
    )
    open_game(page)
    page.locator("form[data-game-action='claim-quest'] button").first.click()
    expect(page.locator("[data-hud-coins]")).not_to_have_text(hud_number(100000))
    assert coins(page) > 100000


def test_prestige_hidden_below_level(alice):
    page, _ = alice
    reset_farm("alice_test")
    open_game(page)
    assert page.locator("form[data-game-action='prestige']").count() == 0
    assert page.is_visible("text=Reach level 10 to refactor")


def test_prestige_resets_farm(alice):
    page, _ = alice
    from devplacepy.services.game import economy

    reset_farm("alice_test")
    set_farm_xp("alice_test", economy.xp_threshold(economy.PRESTIGE_MIN_LEVEL))
    fee = economy.refactor_cost(0, 100000)
    carried = economy.refactor_carryover(100000, fee, 0)
    open_game(page)
    prestige = page.locator("form[data-game-action='prestige'] button")
    prestige.wait_for(state="visible")
    prestige.click()
    confirm_btn = page.locator(".dialog-confirm")
    confirm_btn.wait_for(state="visible")
    confirm_btn.click()
    expect(page.locator("[data-hud-prestige]")).to_have_text("1")
    expect(page.locator("[data-hud-coins]")).to_have_text(
        hud_number(economy.STARTING_COINS + carried)
    )
    assert page.locator("form[data-game-action='prestige']").count() == 0


def test_prestige_unaffordable_shows_cost_without_button(alice):
    page, _ = alice
    from devplacepy.services.game import economy

    reset_farm("alice_test", coins=100)
    set_farm_xp("alice_test", economy.xp_threshold(economy.PRESTIGE_MIN_LEVEL))
    open_game(page)
    assert page.locator("form[data-game-action='prestige']").count() == 0
    assert page.is_visible("text=keep farming to afford it")


def test_grant_section_claim(alice):
    page, _ = alice
    from devplacepy.database import get_table
    from devplacepy.services.game import economy, store
    from devplacepy.services.game.store import _iso_week, _now

    reset_farm("alice_test", coins=100)
    store.ensure_treasury()
    get_table("game_treasury").update(
        {"uid": "treasury-main", "balance": 100000}, ["uid"]
    )
    set_farm(
        "alice_test",
        harvests_week=economy.GRANT_MIN_WEEK_HARVESTS,
        harvests_week_start=_iso_week(_now()),
    )
    open_game(page)
    claim = page.locator("form[data-game-action='grant'] button")
    claim.wait_for(state="visible")
    claim.click()
    expect(page.locator("[data-hud-coins]")).to_have_text(
        hud_number(100 + economy.GRANT_CAP)
    )
    assert page.locator("form[data-game-action='grant']").count() == 0


def test_leaderboard_panel_populates(alice):
    page, _ = alice
    reset_farm("alice_test")
    open_game(page)
    page.locator(".game-lb-row").first.wait_for(state="visible")
    listed = False
    for _ in range(8):
        if page.is_visible("a.game-lb-name:has-text('alice_test')"):
            listed = True
            break
        page.wait_for_timeout(3000)
        page.reload(wait_until="domcontentloaded")
        page.locator(".game-lb-row").first.wait_for(state="visible")
    assert listed, "alice_test never appeared on the farm leaderboard within the 15s board cache TTL"


def test_infrastructure_hidden_below_prestige_requirement(alice):
    page, _ = alice
    reset_farm("alice_test", coins=100_000_000)
    open_game(page)
    assert page.is_visible("text=Requires prestige 3")
    assert page.locator("form[data-game-action='infrastructure']").count() == 0


def test_infrastructure_buy_succeeds_when_eligible(alice):
    page, _ = alice
    reset_farm("alice_test", coins=100_000_000)
    set_farm("alice_test", prestige=5)
    open_game(page)
    buy = page.locator("form[data-game-action='infrastructure'] button").first
    buy.wait_for(state="visible")
    buy.click()
    confirm_btn = page.locator(".dialog-confirm")
    confirm_btn.wait_for(state="visible")
    confirm_btn.click()
    expect(page.locator("form[data-game-action='infrastructure']")).to_have_count(0)
    from devplacepy.services.game import economy

    assert coins(page) == 100_000_000 - economy.infra_for("registry").cost


def test_defense_upgrade_spends_coins(alice):
    page, _ = alice
    reset_farm("alice_test")
    open_game(page)
    upgrade = page.locator("form[data-game-action='defense'] button").first
    upgrade.wait_for(state="visible")
    upgrade.click()
    expect(page.locator("[data-hud-coins]")).to_have_text(hud_number(95000))


def test_cosmetics_buy_and_equip_flow(alice):
    page, _ = alice
    reset_farm("alice_test", coins=1_000_000)
    open_game(page)
    buy = page.locator("form[data-game-action='cosmetic-buy']").first
    buy.locator("button").wait_for(state="visible")
    buy.locator("button").click()
    confirm_btn = page.locator(".dialog-confirm")
    confirm_btn.wait_for(state="visible")
    confirm_btn.click()
    equip = page.locator("form[data-game-action='cosmetic-equip'] button").first
    equip.wait_for(state="visible")
    equip.click()
    expect(page.locator(".game-cosmetics")).to_contain_text("Equipped")


def test_mastery_panel_hidden_without_mastery_points(alice):
    page, _ = alice
    reset_farm("alice_test")
    open_game(page)
    assert page.locator(".game-mastery").count() == 0


def test_mastery_panel_visible_and_functional_once_unlocked(alice):
    page, _ = alice
    reset_farm("alice_test")
    set_farm("alice_test", mastery_points=1000, mastery_points_earned_total=1)
    open_game(page)
    upgrade = page.locator(
        "form[data-game-action='mastery']:has(input[value='autoreplant']) button"
    )
    upgrade.wait_for(state="visible")
    upgrade.click()
    expect(
        page.locator(".perk-card:has-text('Continuous Delivery') .perk-level")
    ).to_have_text("Lv 1/1")


def test_leaderboard_board_selector_switches_boards(alice):
    page, _ = alice
    reset_farm("alice_test")
    set_farm("alice_test", prestige=7)
    open_game(page)
    page.locator(".game-lb-row").first.wait_for(state="visible")
    page.select_option("[data-lb-board]", "prestige")
    page.wait_for_timeout(400)
    page.locator(".game-lb-row").first.wait_for(state="visible")
    assert page.is_visible("a.game-lb-name:has-text('alice_test')")


def test_underdog_banner_shows_after_raiding_a_much_richer_farm(bob):
    page, _ = bob
    from devplacepy.database import get_table
    from devplacepy.services.game import store

    reset_farm("alice_test", coins=1_000_000)
    reset_farm("bob_test", coins=0)
    alice_user = get_table("users").find_one(username="alice_test")
    bob_user = get_table("users").find_one(username="bob_test")
    store.plant({"uid": alice_user["uid"], "username": "alice_test"}, 0, "shell")
    past = (datetime.now(timezone.utc) - timedelta(days=1)).isoformat()
    farm = store.get_farm(alice_user["uid"])
    plot = store._plot_at(farm["uid"], 0)
    get_table("game_plots").update(
        {"uid": plot["uid"], "planted_at": past, "ready_at": past}, ["uid"]
    )
    store.steal({"uid": bob_user["uid"], "username": "bob_test"}, alice_user, 0)
    page.goto(f"{BASE_URL}/game", wait_until="domcontentloaded")
    page.locator("[data-underdog-banner]").wait_for(state="visible")


def test_weekly_contract_appears_and_can_be_claimed(alice):
    page, _ = alice
    from devplacepy.database import get_table
    from devplacepy.services.game import store
    from devplacepy.services.game.store.common import _iso_week
    from devplacepy.services.game.store.quests import ensure_weekly_contract

    reset_farm("alice_test")
    set_farm("alice_test", mastery_points=1000)
    user = get_table("users").find_one(username="alice_test")
    store.upgrade_mastery({"uid": user["uid"], "username": "alice_test"}, "contracts")
    farm = store.get_farm(user["uid"])
    iso_week = _iso_week(datetime.now(timezone.utc))
    ensure_weekly_contract(farm, iso_week)
    row = get_table("game_quests").find_one(
        farm_uid=farm["uid"], day=iso_week, scope="weekly"
    )
    get_table("game_quests").update(
        {"uid": row["uid"], "progress": row["goal"]}, ["uid"]
    )
    open_game(page)
    claim = page.locator(
        "form[data-game-action='claim-quest']:has(input[value='weekly']) button"
    )
    claim.wait_for(state="visible")
    claim.click()
    page.wait_for_timeout(400)
    farm = store.get_farm(user["uid"])
    assert int(farm["stars"]) > 0


def test_crop_shows_saturated_label_when_overfarmed(alice):
    page, _ = alice
    from devplacepy.database import get_table
    from devplacepy.services.game.store import market as market_store
    from devplacepy.utils import generate_uid

    reset_farm("alice_test")
    from devplacepy.services.game import economy

    now = datetime.now(timezone.utc)
    crop = economy.crop_for("shell")
    worst_days = economy.MARKET_SATURATION_TIERS[-1][0]
    supply_per_harvest = crop.grow_seconds / 86400.0 / market_store.active_farms()
    get_table("game_market_ticks").insert(
        {
            "uid": generate_uid(),
            "crop_key": "shell",
            "hour_bucket": market_store._hour_bucket(now),
            "harvests": int(worst_days / supply_per_harvest) + 1,
            "updated_at": now.isoformat(),
        }
    )
    open_game(page)
    option = page.locator("form[data-game-action='plant'] select option[value='shell']").first
    option.wait_for(state="attached")
    deadline = datetime.now(timezone.utc) + timedelta(seconds=10)
    while "saturated" not in option.inner_text() and datetime.now(timezone.utc) < deadline:
        page.wait_for_timeout(500)
        page.reload(wait_until="domcontentloaded")
        page.locator("[data-game-grid]").first.wait_for(state="visible")
        option = page.locator(
            "form[data-game-action='plant'] select option[value='shell']"
        ).first
        option.wait_for(state="attached")
    assert "saturated" in option.inner_text()


def test_mobile_no_horizontal_overflow(mobile_page):
    page, _ = mobile_page
    reset_farm("alice_test")
    open_game(page)
    scroll_w = page.evaluate("document.documentElement.scrollWidth")
    client_w = page.evaluate("document.documentElement.clientWidth")
    assert scroll_w - client_w <= 1, f"horizontal overflow: {scroll_w} vs {client_w}"
    assert page.locator("[data-game-grid]").first.is_visible()
