#include "mod_bounty_board.h"
#include "Chat.h"
#include "Config.h"
#include "Creature.h"
#include "DatabaseEnv.h"
#include "Log.h"
#include "Map.h"
#include "MapMgr.h"
#include "Player.h"
#include "ScriptMgr.h"
#include 

using namespace Acore::ChatCommands;

// v0.2: adds kill_target spawning on top of v0.1's kill-count vertical slice. Progress tracking
// (BountyBoard_Player::OnPlayerCreatureKill) was already generic across types -- kill_target is
// just count_required == 1 -- so the only new code here is actually summoning the creature.
// Rewards auto-grant on completion; the board is browse-only, no turn-in step -- see PLAN.md.
//
// Spawned kill_target creatures aren't despawned or re-summoned after death yet -- there's no
// rotation timer to drive that (PLAN.md step 2), and once a summoned target is killed it stays
// gone until the next server restart. Fine for proving the summon mechanism works; revisit
// respawn-after-death behavior (so more than one player/week can complete the same slot) when the
// rotation timer lands.

namespace
{
    std::vector ActiveBounties;

    void LoadActiveBounties()
    {
        ActiveBounties.clear();

        QueryResult result = WorldDatabase.Query(
            "SELECT r.id, t.type, t.name, t.creature_entry, t.count_required, t.item_reward, "
            "t.item_reward_count, t.money_reward, t.is_champion, t.position_map, t.position_x, "
            "t.position_y, t.position_z, t.orientation "
            "FROM mod_bounty_board_rotation r "
            "JOIN mod_bounty_board_template t ON t.id = r.template_id "
            "ORDER BY r.id");

        if (!result)
        {
            LOG_WARN("server.loading", "mod-bounty-board: mod_bounty_board_rotation is empty -- "
                "no active bounties loaded.");
            return;
        }

        do
        {
            Field* row = result->Fetch();
            ActiveBounties.push_back(ActiveBounty
            {
                row[0].Get(),
                row[1].Get(),
                row[2].Get(),
                row[3].Get(),
                row[4].Get(),
                row[5].Get(),
                row[6].Get(),
                row[7].Get(),
                row[8].Get() != 0,
                row[9].Get(),
                row[10].Get(),
                row[11].Get(),
                row[12].Get(),
                row[13].Get()
            });
        } while (result->NextRow());

        LOG_INFO("server.loading", "mod-bounty-board: loaded {} active bounty rotation slot(s).",
            ActiveBounties.size());
    }

    // Summons the world-placed creature for every active kill_target bounty. Called once at
    // startup, after maps are initialized -- see the comment on BountyBoard_World::OnStartup for
    // why this can't happen from OnLoadCustomDatabaseTable instead.
    void SpawnBountyTargets()
    {
        uint32 spawned = 0;
        for (ActiveBounty const& bounty : ActiveBounties)
        {
            if (bounty.type != "kill_target")
                continue;

            Map* map = sMapMgr->CreateBaseMap(bounty.positionMap);
            Position pos(bounty.positionX, bounty.positionY, bounty.positionZ, bounty.orientation);
            if (map->SummonCreature(bounty.creatureEntry, pos))
                ++spawned;
            else
                LOG_ERROR("server.loading", "mod-bounty-board: failed to summon kill_target "
                    "bounty '{}' (creature {}) at map {}.", bounty.name, bounty.creatureEntry,
                    bounty.positionMap);
        }

        if (spawned)
            LOG_INFO("server.loading", "mod-bounty-board: summoned {} kill_target bounty "
                "target(s).", spawned);
    }

    // Grants the reward and marks the row turned-in in the same statement as the completing kill
    // -- see the file-level comment on why there's no separate turn-in step yet.
    void CompleteBounty(Player* player, ActiveBounty const& bounty)
    {
        if (bounty.moneyReward)
            player->ModifyMoney(static_cast(bounty.moneyReward));

        if (bounty.itemReward && bounty.itemRewardCount)
            player->AddItem(bounty.itemReward, bounty.itemRewardCount);

        ChatHandler(player->GetSession()).PSendSysMessage("Bounty complete: {}! Reward delivered.", bounty.name);
    }
}

std::vector const& GetActiveBounties()
{
    return ActiveBounties;
}

class BountyBoardCommandScript : public CommandScript
{
public:
    BountyBoardCommandScript() : CommandScript("BountyBoardCommandScript") { }

    ChatCommandTable GetCommands() const override
    {
        static ChatCommandTable commandTable =
        {
            { "bounty", HandleBountyCommand, SEC_PLAYER, Console::No },
        };

        return commandTable;
    }

    static bool HandleBountyCommand(ChatHandler* handler)
    {
        if (!sConfigMgr->GetOption("BountyBoard.Enable", true))
        {
            handler->PSendSysMessage("The Bounty Board module is currently disabled.");
            return true;
        }

        if (GetActiveBounties().empty())
        {
            handler->PSendSysMessage("A tattered notice board creaks in the wind: no contracts posted this week.");
            return true;
        }

        Player* player = handler->GetPlayer();
        for (ActiveBounty const& bounty : GetActiveBounties())
        {
            QueryResult result = CharacterDatabase.Query(
                "SELECT kill_count, completed FROM mod_bounty_board_progress WHERE guid = {} AND rotation_id = {}",
                player->GetGUID().GetCounter(), bounty.rotationId);

            uint32 killCount = 0;
            bool completed = false;
            if (result)
            {
                Field* row = result->Fetch();
                killCount = row[0].Get();
                completed = row[1].Get() != 0;
            }

            handler->PSendSysMessage("{}: {} ({}/{})", bounty.name, completed ? "completed" : "in progress",
                killCount, bounty.countRequired);
        }

        return true;
    }
};

class BountyBoard_Player : public PlayerScript
{
public:
    BountyBoard_Player() : PlayerScript("BountyBoard_Player") { }

    void OnPlayerCreatureKill(Player* killer, Creature* killed) override
    {
        if (!sConfigMgr->GetOption("BountyBoard.Enable", true))
            return;

        for (ActiveBounty const& bounty : GetActiveBounties())
        {
            if (bounty.creatureEntry != killed->GetEntry())
                continue;

            QueryResult result = CharacterDatabase.Query(
                "SELECT kill_count, completed FROM mod_bounty_board_progress WHERE guid = {} AND rotation_id = {}",
                killer->GetGUID().GetCounter(), bounty.rotationId);

            uint32 killCount = 0;
            bool alreadyCompleted = false;
            if (result)
            {
                Field* row = result->Fetch();
                killCount = row[0].Get();
                alreadyCompleted = row[1].Get() != 0;
            }

            if (alreadyCompleted)
                continue; // Reward already granted -- don't keep counting past the requirement.

            killCount = std::min(killCount + 1, bounty.countRequired);
            bool completedNow = killCount >= bounty.countRequired;

            CharacterDatabase.Execute(
                "INSERT INTO mod_bounty_board_progress (guid, rotation_id, kill_count, completed, turned_in) "
                "VALUES ({}, {}, {}, {}, {}) "
                "ON DUPLICATE KEY UPDATE kill_count = {}, completed = {}, turned_in = {}",
                killer->GetGUID().GetCounter(), bounty.rotationId, killCount, completedNow ? 1 : 0,
                completedNow ? 1 : 0, killCount, completedNow ? 1 : 0, completedNow ? 1 : 0);

            if (completedNow)
                CompleteBounty(killer, bounty);
            else
                ChatHandler(killer->GetSession()).PSendSysMessage("{}: {}/{}", bounty.name, killCount,
                    bounty.countRequired);

            return; // A kill can only match one active bounty slot in this vertical slice.
        }
    }
};

class BountyBoard_World : public WorldScript
{
public:
    BountyBoard_World() : WorldScript("BountyBoard_World") { }

    void OnStartup() override
    {
        bool enabled = sConfigMgr->GetOption("BountyBoard.Enable", true);
        LOG_INFO("server.loading", "mod-bounty-board: loaded (v0.2, enabled={}).", enabled);

        // Maps aren't initialized yet during OnLoadCustomDatabaseTable (sMapMgr->Initialize()
        // runs later in World::SetInitialWorldSettings, well before OnStartup fires from
        // Main.cpp) -- this is the earliest point summoning kill_target creatures is safe.
        if (enabled)
            SpawnBountyTargets();
    }

    void OnLoadCustomDatabaseTable() override
    {
        LoadActiveBounties();
    }
};

void AddSC_mod_bounty_board()
{
    new BountyBoardCommandScript();
    new BountyBoard_Player();
    new BountyBoard_World();
}