// retoor <retoor@molodetz.nl>

import { Format } from "./Format.js";
import { Http } from "./Http.js";
import { Poller } from "./Poller.js";

export class GameFarm {
    constructor() {
        this.root = document.querySelector("[data-game-root]");
        if (!this.root) return;
        this.mode = this.root.dataset.mode || "own";
        this.username = this.root.dataset.username || "";
        this.stateUrl = this.mode === "view" ? `/game/farm/${this.username}` : "/game/state";
        this.farm = null;
        this.board = "score";
        this._bindActions();
        this._bindLeaderboardBoard();
        this._startTicker();
        this.load();
        if (this.mode === "own") this._loadLeaderboard();
        if (window.app && window.app.pubsub && this.username) {
            window.app.pubsub.subscribe(`public.game.farm.${this.username}`, () => this.load());
        }
        this.poller = new Poller(() => this.load(), 20000, { pauseHidden: true, immediate: false });
    }

    async load() {
        try {
            const data = await Http.getJson(this.stateUrl);
            if (window.app && window.app.dialog && window.app.dialog.isOpen) return;
            this.render(data.farm);
        } catch (error) {
            return;
        }
    }

    _bindActions() {
        this.root.addEventListener("submit", (event) => {
            const form = event.target.closest("form[data-game-action]");
            if (!form) return;
            event.preventDefault();
            this._submit(form);
        });
    }

    _bindLeaderboardBoard() {
        const select = this.root.querySelector("[data-lb-board]");
        if (!select) return;
        select.addEventListener("change", () => {
            this.board = select.value;
            this._loadLeaderboard();
        });
    }

    async _submit(form) {
        const action = form.dataset.gameAction;
        const params = {};
        form.querySelectorAll("[name]").forEach((field) => {
            params[field.name] = field.value;
        });
        const button = form.querySelector("button");
        if (button) button.disabled = true;
        try {
            const data = await Http.send(form.getAttribute("action"), params);
            this.render(data.farm);
            if (action === "steal" && window.app && window.app.toast) {
                window.app.toast.show(`You stole ${Format.coins(data.stole_coins)}!`, { type: "success" });
            }
            if (action === "harvest" || action === "water" || action === "steal" || action === "prestige") {
                this._loadLeaderboard();
            }
        } catch (error) {
            if (button) button.disabled = false;
        }
    }

    render(farm) {
        if (!farm) return;
        this.farm = farm;
        this._renderHud(farm);
        this._setHost("[data-game-grid-host]", this._gridHtml(farm));
        if (this.mode === "own") {
            this._setHost("[data-shop-host]", this._shopHtml(farm));
            this._setHost("[data-defense-host]", this._defenseHtml(farm));
            this._setHost("[data-infra-host]", this._infraHtml(farm));
            this._setHost("[data-perk-host]", this._perksHtml(farm));
            this._setHost("[data-legacy-host]", this._legacyHtml(farm));
            this._setHost("[data-mastery-host]", this._masteryHtml(farm));
            this._setHost("[data-analytics-host]", this._analyticsHtml(farm));
            this._setHost("[data-cosmetics-host]", this._cosmeticsHtml(farm));
            this._setHost("[data-daily-host]", this._dailyHtml(farm));
            this._setHost("[data-grant-host]", this._grantHtml(farm));
            this._setHost("[data-quest-host]", this._questsHtml(farm));
        }
        this._tick();
    }

    _setHost(selector, html) {
        const host = this.root.querySelector(selector);
        if (host) host.innerHTML = html;
    }

    _renderHud(farm) {
        const set = (selector, value) => {
            const node = this.root.querySelector(selector);
            if (node) node.textContent = value;
        };
        this._setCoins("[data-hud-coins]", farm.coins);
        set("[data-hud-level]", farm.level);
        set("[data-hud-ci]", farm.ci_tier);
        set("[data-hud-ci-label]", `${farm.ci_label} (${Format.speed(farm.ci_speed)})`);
        set("[data-hud-harvests]", Format.exact(farm.total_harvests));
        set("[data-hud-prestige]", farm.prestige);
        set("[data-hud-prestige-mult]", `+${Math.round(farm.prestige_multiplier * 100 - 100)}% coins`);
        set("[data-hud-stars]", Format.exact(farm.stars));
        set("[data-hud-mastery]", Format.exact(farm.mastery_points));
        set("[data-era-coins]", Format.coins(farm.era_coins));
        set("[data-era-harvests]", Format.exact(farm.era_harvests));
        const fill = this.root.querySelector("[data-hud-xp-fill]");
        if (fill) {
            const pct = farm.level_is_max || !farm.level_span ? 100 : Math.floor((100 * farm.level_into) / farm.level_span);
            fill.style.setProperty("--hud-xp", `${pct}%`);
        }
        set("[data-hud-xp]", farm.level_is_max ? "max level" : `${Format.exact(farm.level_into)} / ${Format.exact(farm.level_span)} XP`);
    }

    _shopHtml(farm) {
        const ciUpgrade = farm.ci_next_tier
            ? `<form method="post" action="/game/upgrade" data-game-action="upgrade"><button type="submit" class="btn btn-sm btn-primary">Upgrade (${Format.coins(farm.ci_next_cost)})</button></form>`
            : "";
        const ciText = farm.ci_next_tier
            ? `Upgrade to ${farm.ci_next_label} for faster builds`
            : "CI is at the top tier.";
        const prestigeBtn = farm.refactor_affordable
            ? `<form method="post" action="/game/prestige" data-game-action="prestige"><button type="submit" class="btn btn-sm btn-primary" data-confirm="Refactoring costs ${Format.exact(farm.refactor_cost)} coins and resets coins, level, CI, extra plots, and perks for a permanent +25% coin bonus. You carry over ${Format.exact(farm.refactor_carryover_preview)} coins and keep your Stars and Legacy upgrades. Continue?">Refactor (${Format.coins(farm.refactor_cost)})</button></form>`
            : "";
        let prestigeText;
        if (!farm.prestige_available) {
            prestigeText = `Reach level ${farm.prestige_min_level} to refactor.`;
        } else if (farm.refactor_affordable) {
            prestigeText = `Pay ${Format.coins(farm.refactor_cost)} to reset your farm for a permanent +25% coin bonus. You keep ${farm.refactor_carryover_pct}% of what remains (${Format.coins(farm.refactor_carryover_preview)}) plus your Stars and Legacy upgrades.`;
        } else {
            prestigeText = `Refactoring costs ${Format.coins(farm.refactor_cost)} right now - keep farming to afford it.`;
        }
        return (
            `<div class="shop-row"><div class="shop-info"><strong>CI upgrade</strong><span>${ciText}</span></div>${ciUpgrade}</div>` +
            `<div class="shop-row"><div class="shop-info"><strong>Refactor (prestige ${farm.prestige})</strong><span>${prestigeText}</span></div>${prestigeBtn}</div>`
        );
    }

    _perksHtml(farm) {
        return (farm.perks || [])
            .map((perk) => {
                const action = perk.maxed
                    ? `<span class="perk-maxed">Maxed</span>`
                    : `<form method="post" action="/game/perk" data-game-action="perk"><input type="hidden" name="perk" value="${perk.key}"><button type="submit" class="btn btn-sm">Upgrade (${Format.coins(perk.cost)})</button></form>`;
                return `<div class="perk-card"><span class="perk-icon" aria-hidden="true">${perk.icon}</span><strong class="perk-name">${perk.name}</strong><span class="perk-level">Lv ${perk.level}/${perk.max_level}</span><span class="perk-effect">${perk.effect || perk.description}</span>${action}</div>`;
            })
            .join("");
    }

    _legacyHtml(farm) {
        return (farm.legacy || [])
            .map((up) => {
                const action = up.maxed
                    ? `<span class="perk-maxed">Maxed</span>`
                    : `<form method="post" action="/game/legacy" data-game-action="legacy"><input type="hidden" name="key" value="${up.key}"><button type="submit" class="btn btn-sm">Buy (${Format.exact(up.cost)} &#9733;)</button></form>`;
                return `<div class="perk-card legacy-card"><span class="perk-icon" aria-hidden="true">${up.icon}</span><strong class="perk-name">${up.name}</strong><span class="perk-level">Lv ${up.level}/${up.max_level}</span><span class="perk-effect">${up.effect || up.description}</span>${action}</div>`;
            })
            .join("");
    }

    _masteryHtml(farm) {
        return (farm.mastery || [])
            .map((m) => {
                const action = m.maxed
                    ? `<span class="perk-maxed">Maxed</span>`
                    : `<form method="post" action="/game/mastery" data-game-action="mastery"><input type="hidden" name="key" value="${m.key}"><button type="submit" class="btn btn-sm">Upgrade (${Format.exact(m.cost)} Mastery)</button></form>`;
                return `<div class="perk-card"><span class="perk-icon" aria-hidden="true">${m.icon}</span><strong class="perk-name">${m.name}</strong><span class="perk-level">Lv ${m.level}/${m.max_level}</span><span class="perk-effect">${m.effect || m.description}</span>${action}</div>`;
            })
            .join("");
    }

    _analyticsHtml(farm) {
        if (!farm.mastery_analytics_unlocked) return "";
        return (
            `<div class="shop-row"><div class="shop-info"><strong>Lifetime coins earned</strong><span>${Format.coins(farm.lifetime_coins_earned)} since Farm Analytics was unlocked.</span></div></div>` +
            `<div class="shop-row"><div class="shop-info"><strong>Lifetime harvests</strong><span>${Format.exact(farm.lifetime_harvests)} builds harvested since Farm Analytics was unlocked.</span></div></div>`
        );
    }

    _infraHtml(farm) {
        return (farm.infrastructure || [])
            .map((infra) => {
                let action;
                if (infra.owned) {
                    action = `<span class="perk-maxed">Owned</span>`;
                } else if (farm.prestige < infra.min_prestige) {
                    action = `<span class="perk-maxed">Requires prestige ${infra.min_prestige}</span>`;
                } else {
                    action = `<form method="post" action="/game/infrastructure/buy" data-game-action="infrastructure"><input type="hidden" name="key" value="${infra.key}"><button type="submit" class="btn btn-sm" data-confirm="Buy ${infra.name} for ${Format.exact(infra.cost)} coins?">Buy (${Format.coins(infra.cost)})</button></form>`;
                }
                return `<div class="perk-card"><span class="perk-icon" aria-hidden="true">${infra.icon}</span><strong class="perk-name">${infra.name}</strong><span class="perk-effect">${infra.description}</span>${action}</div>`;
            })
            .join("");
    }

    _defenseHtml(farm) {
        const upkeep = farm.defense_upkeep_daily
            ? `Costs ${Format.coins(farm.defense_upkeep_daily)}/day upkeep. If you cannot pay you lose one level - never your whole balance.`
            : "No upkeep at level 0.";
        const action = farm.defense_next_cost
            ? `<form method="post" action="/game/defense/upgrade" data-game-action="defense"><button type="submit" class="btn btn-sm btn-primary">Upgrade (${Format.coins(farm.defense_next_cost)})</button></form>`
            : `<span class="perk-maxed">Maxed</span>`;
        const downgrade = farm.defense_downgrade_available
            ? `<form method="post" action="/game/defense/downgrade" data-game-action="defense-downgrade"><button type="submit" class="btn btn-sm" data-confirm="Drop to the next defense tier down? Lower upkeep, less protection.">Downgrade</button></form>`
            : "";
        return `<div class="shop-row"><div class="shop-info"><strong>Defense: ${farm.defense_tier_name} (level ${farm.defense_level})</strong><span>${upkeep}</span></div><div class="shop-actions">${action}${downgrade}</div></div>`;
    }

    _cosmeticsHtml(farm) {
        return (farm.cosmetics || [])
            .map((c) => {
                let action;
                if (c.owned && c.kind === "title") {
                    action =
                        farm.active_title === c.key
                            ? `<span class="perk-maxed">Equipped</span>`
                            : `<form method="post" action="/game/cosmetics/equip" data-game-action="cosmetic-equip"><input type="hidden" name="key" value="${c.key}"><button type="submit" class="btn btn-sm">Equip</button></form>`;
                } else if (c.owned) {
                    action = `<span class="perk-maxed">Owned</span>`;
                } else {
                    action = `<form method="post" action="/game/cosmetics/buy" data-game-action="cosmetic-buy"><input type="hidden" name="key" value="${c.key}"><button type="submit" class="btn btn-sm" data-confirm="Buy ${c.name} for ${Format.exact(c.cost_coins)} coins?">Buy (${Format.coins(c.cost_coins)})</button></form>`;
                }
                return `<div class="perk-card"><span class="perk-icon" aria-hidden="true">${c.icon}</span><strong class="perk-name">${c.name}</strong><span class="perk-effect">${c.description}</span>${action}</div>`;
            })
            .join("");
    }

    _grantHtml(farm) {
        const action = farm.grant_available
            ? `<form method="post" action="/game/grant" data-game-action="grant"><button type="submit" class="btn btn-sm btn-primary">Claim +${Format.coins(farm.grant_amount)}</button></form>`
            : `<span class="daily-claimed">${farm.grant_reason}</span>`;
        return `<p class="daily-streak">Treasury: <strong>${Format.coins(farm.treasury_balance)}</strong> collected from refactor fees</p>${action}`;
    }

    _dailyHtml(farm) {
        const action = farm.daily_available
            ? `<form method="post" action="/game/daily" data-game-action="daily"><button type="submit" class="btn btn-sm btn-primary">Claim +${Format.coins(farm.daily_reward)}</button></form>`
            : `<span class="daily-claimed">Claimed today. Come back tomorrow.</span>`;
        return `<p class="daily-streak">Streak: <strong>${Format.exact(farm.streak)}</strong> day(s)${farm.daily_streak_reset ? " - your streak lapsed, claiming today starts a new one" : ""}</p>${action}`;
    }

    _questsHtml(farm) {
        const quests = farm.quests || [];
        if (!quests.length) return `<li class="game-lb-empty">No quests today.</li>`;
        return quests
            .map((quest) => {
                const pct = quest.goal ? Math.floor((100 * quest.progress) / quest.goal) : 0;
                const reward = quest.reward_stars ? `+${Format.exact(quest.reward_stars)} &#9733;` : `+${Format.coins(quest.reward_coins)}`;
                let foot = "";
                if (quest.can_claim) {
                    foot = `<form method="post" action="/game/quests/claim" data-game-action="claim-quest"><input type="hidden" name="quest" value="${quest.kind}"><input type="hidden" name="scope" value="${quest.scope}"><button type="submit" class="btn btn-sm btn-primary">Claim</button></form>`;
                } else if (quest.claimed) {
                    foot = `<span class="quest-claimed">Done</span>`;
                }
                return `<li class="quest-item${quest.claimed ? " quest-done" : ""}"><div class="quest-head"><span>${quest.label}</span><span class="quest-reward">${reward}</span></div><div class="quest-bar"><span class="quest-fill" style="--quest-pct:${pct}%"></span></div><div class="quest-foot"><span class="quest-progress">${Format.exact(quest.progress)}/${Format.exact(quest.goal)}</span>${foot}</div></li>`;
            })
            .join("");
    }

    _gridHtml(farm) {
        const cells = farm.plots.map((plot) => this._plotHtml(plot, farm)).join("");
        let buy = "";
        if (farm.is_owner && farm.plot_count < farm.max_plots) {
            buy = `<form class="game-plot game-plot-buy" method="post" action="/game/buy-plot" data-game-action="buy-plot"><button type="submit" class="btn btn-sm">+ New plot (${Format.coins(farm.next_plot_cost)})</button></form>`;
        }
        return `<div class="game-grid" data-game-grid>${cells}${buy}</div>`;
    }

    _plotHtml(plot, farm) {
        const golden = plot.is_golden ? " game-plot-golden" : "";
        const head = `<div class="game-plot game-plot-${plot.state}${golden}" data-slot="${plot.slot}" data-state="${plot.state}" data-ready-at="${plot.ready_at}">`;
        let body = "";
        if (plot.state === "empty") {
            if (farm.is_owner) {
                const options = farm.crops
                    .map((crop) => {
                        const disabled = crop.locked || crop.cost > farm.coins ? " disabled" : "";
                        const title = crop.locked ? ` title="${crop.locked_text}"` : "";
                        const lock = crop.locked_reason === "mastery"
                            ? " (Mastery)"
                            : crop.locked_reason === "era"
                              ? " (Era)"
                              : crop.locked
                                ? ` (Lv ${crop.min_level})`
                                : "";
                        const market =
                            crop.market_state === "saturated"
                                ? " (saturated)"
                                : crop.market_state === "boosted"
                                  ? " (boosted)"
                                  : "";
                        return `<option value="${crop.key}"${disabled}${title}>${crop.icon} ${crop.name} - ${Format.coins(crop.cost)}${lock}${market}</option>`;
                    })
                    .join("");
                body = `<form class="plot-plant-form" method="post" action="/game/plant" data-game-action="plant"><input type="hidden" name="slot" value="${plot.slot}"><select name="crop" class="plot-crop-select" aria-label="Choose a project to build">${options}</select><button type="submit" class="btn btn-sm btn-primary">Plant</button></form>`;
            } else {
                body = `<span class="plot-empty-label">Empty plot</span>`;
            }
        } else if (plot.state === "growing") {
            body = `<span class="plot-icon" aria-hidden="true">${plot.crop_icon}</span><span class="plot-crop-name">${plot.crop_name}</span><span class="plot-timer" data-plot-timer data-ready-at="${plot.ready_at}">building</span><span class="plot-water-count">${plot.watered_count}/${plot.max_waters} &#128167;</span>`;
            if (plot.can_water) {
                body += `<form method="post" action="/game/farm/${farm.owner_username}/water" data-game-action="water"><input type="hidden" name="slot" value="${plot.slot}"><button type="submit" class="btn btn-sm">&#128167; Water</button></form>`;
            }
            if (farm.is_owner && plot.fertilize_cost) {
                body += `<form method="post" action="/game/fertilize" data-game-action="fertilize"><input type="hidden" name="slot" value="${plot.slot}"><button type="submit" class="btn btn-sm">&#9889; Fertilize (${Format.coins(plot.fertilize_cost)})</button></form>`;
            }
        } else {
            const goldenMark = plot.is_golden ? " &#10024;" : "";
            const raided = plot.raided_pct ? `<span class="plot-raided">Raided ${plot.raided_pct}%</span>` : "";
            body = `<span class="plot-icon plot-ready-icon" aria-hidden="true">${plot.crop_icon}</span><span class="plot-crop-name">${plot.crop_name}${goldenMark}</span>${raided}`;
            if (farm.is_owner) {
                const label = plot.is_golden ? "Harvest golden" : "Harvest";
                body += `<form method="post" action="/game/harvest" data-game-action="harvest"><input type="hidden" name="slot" value="${plot.slot}"><button type="submit" class="btn btn-sm btn-primary">${label}</button></form>`;
            } else if (plot.can_steal) {
                body += `<form method="post" action="/game/farm/${farm.owner_username}/steal" data-game-action="steal"><input type="hidden" name="slot" value="${plot.slot}"><button type="submit" class="btn btn-sm btn-danger" data-confirm="Steal this ready build?">Steal +${Format.coins(plot.steal_coins)}</button></form>`;
            } else if (plot.steal_reason === "stripped") {
                body += `<span class="plot-ready-label plot-steal-locked">Already stripped</span>`;
            } else if (plot.steal_reason === "cooldown") {
                body += `<span class="plot-ready-label plot-steal-locked">Raid again in ${this._format(plot.steal_cooldown_seconds)}</span>`;
            } else {
                body += `<span class="plot-ready-label">Ready</span>`;
            }
        }
        return `${head}${body}</div>`;
    }

    _startTicker() {
        this.ticker = window.setInterval(() => this._tick(), 1000);
    }

    _tick() {
        this._tickBoosts();
        let flipped = false;
        this.root.querySelectorAll("[data-plot-timer]").forEach((node) => {
            const readyAt = node.dataset.readyAt;
            const remaining = this._remaining(readyAt);
            if (remaining <= 0) {
                node.textContent = "ready";
                flipped = true;
            } else {
                node.textContent = this._format(remaining);
            }
        });
        if (flipped) this.load();
    }

    _remaining(iso) {
        if (!iso) return 0;
        const target = Date.parse(iso);
        if (Number.isNaN(target)) return 0;
        return Math.max(0, Math.floor((target - Date.now()) / 1000));
    }

    _format(seconds) {
        return Format.duration(seconds);
    }

    _setCoins(selector, value) {
        const node = this.root.querySelector(selector);
        if (!node) return;
        node.textContent = Format.number(value);
        node.title = Format.exact(value);
    }

    _tickBoosts() {
        this.root.querySelectorAll("[data-seconds]").forEach((node) => {
            const remaining = Math.max(0, Number(node.dataset.seconds || 0) - 1);
            node.dataset.seconds = String(remaining);
            node.textContent = Format.duration(remaining);
        });
    }

    async _loadLeaderboard() {
        const list = this.root.querySelector("[data-game-leaderboard]");
        if (!list) return;
        try {
            const data = await Http.getJson(`/game/leaderboard?board=${encodeURIComponent(this.board || "score")}`);
            if (!data.entries || !data.entries.length) {
                list.innerHTML = `<li class="game-lb-empty">No farmers yet.</li>`;
                return;
            }
            const board = this.board || "score";
            list.innerHTML = data.entries
                .map((entry) => {
                    const title = entry.title ? `<span class="game-lb-title">${entry.title}</span>` : "";
                    const value = this._leaderboardValue(board, entry);
                    return `<li class="game-lb-row${entry.username === this.username ? " game-lb-self" : ""}"><span class="game-lb-rank">#${entry.rank}</span><div class="game-lb-name-group"><a class="game-lb-name" href="/game/farm/${entry.username}">${entry.username}</a>${title}</div><span class="game-lb-level">Lv ${entry.level}</span><span class="game-lb-score">${value}</span></li>`;
                })
                .join("");
        } catch (error) {
            return;
        }
    }

    _leaderboardValue(board, entry) {
        if (board === "raids") return `${Format.coins(entry.raid_avg)}/raid`;
        if (board === "time_to_kernel") return Format.duration(entry.time_to_kernel_seconds);
        return Format.exact(entry.score);
    }
}
