#pragma once

#include <cstdint>
#include <string>
#include "core/schema.hpp"
#include "core/memory.hpp"
#include "sdk/entity_handle.hpp"
#include "sdk/datatypes/vector.hpp"
#include "sdk/datatypes/utl_vector.hpp"

enum class e_team : uint8_t
{
    none,
    spec,
    t,
    ct
};

enum class e_colors : uint32_t
{
    blue,
    green,
    yellow,
    orange,
    purple,
    white
};

enum class e_weapon_type : uint32_t
{
    knife,
    pistol,
    submachinegun,
    rifle,
    shotgun,
    sniper_rifle,
    machinegun,
    c4,
    taser,
    grenade,
    equipment,
    stackableitem,
    fists,
    breachcharge,
    bumpmine,
    tablet,
    melee,
    shield,
    zone_repulsor,
    unknown
};

// ── CEntityIdentity ───────────────────────────────────────────────────────────
class c_entity_identity
{
public:
    SCHEMA_ADD_OFFSET(uintptr_t, m_pClassInfo, 0x08);
    SCHEMA_ADD_OFFSET(uint32_t, m_Idx, 0x10);
    SCHEMA_ADD_FIELD(const char*, m_designerName, "CEntityIdentity->m_designerName");
    SCHEMA_ADD_FIELD(uint32_t, m_flags, "CEntityIdentity->m_flags");

    bool is_valid()
    {
        return m_Idx() != INVALID_EHANDLE_IDX;
    }

    int32_t get_entry_idx()
    {
        if (!is_valid())
            return ENT_ENTRY_MASK;
        return m_Idx() & ENT_ENTRY_MASK;
    }

    int32_t get_serial_number()
    {
        return m_Idx() >> NUM_SERIAL_NUM_SHIFT_BITS;
    }
};

// ── CEntityInstance ───────────────────────────────────────────────────────────
class c_entity_instance
{
public:
    SCHEMA_ADD_FIELD(c_entity_identity*, m_pEntity, "CEntityInstance->m_pEntity");

    const c_base_handle get_ref_e_handle();
    const std::string   get_schema_class_name();
};

// ── CGameSceneNode ────────────────────────────────────────────────────────────
class c_game_scene_node
{
public:
    SCHEMA_ADD_FIELD(f_vector, m_vecAbsOrigin, "CGameSceneNode->m_vecAbsOrigin");
};

// ── C_BaseEntity ──────────────────────────────────────────────────────────────
class c_base_entity : public c_entity_instance
{
public:
    SCHEMA_ADD_FIELD(c_game_scene_node*, m_pGameSceneNode, "C_BaseEntity->m_pGameSceneNode");
    SCHEMA_ADD_FIELD(int32_t, m_iHealth, "C_BaseEntity->m_iHealth");
    SCHEMA_ADD_FIELD(e_team, m_iTeamNum, "C_BaseEntity->m_iTeamNum");
    SCHEMA_ADD_FIELD(c_base_entity*, m_hOwnerEntity, "C_BaseEntity->m_hOwnerEntity");
    SCHEMA_ADD_FIELD(int32_t, m_fFlags, "C_BaseEntity->m_fFlags");

    const f_vector& get_scene_origin();
};

// ── CPlayer_WeaponServices ────────────────────────────────────────────────────
class c_base_player_weapon;

class c_player_weapon_services
{
public:
    SCHEMA_ADD_FIELD(c_base_handle, m_hActiveWeapon, "CPlayer_WeaponServices->m_hActiveWeapon");
    SCHEMA_ADD_FIELD(c_network_utl_vector_base<class c_base_player_weapon>, m_hMyWeapons, "CPlayer_WeaponServices->m_hMyWeapons");
};

// ── CCSPlayer_ItemServices ────────────────────────────────────────────────────
class c_player_item_services
{
public:
    SCHEMA_ADD_FIELD(bool, m_bHasDefuser, "CCSPlayer_ItemServices->m_bHasDefuser");
    SCHEMA_ADD_FIELD(bool, m_bHasHelmet, "CCSPlayer_ItemServices->m_bHasHelmet");
    SCHEMA_ADD_FIELD(bool, m_bHasHeavyArmor, "CCSPlayer_ItemServices->m_bHasHeavyArmor");
};

// ── C_BasePlayerPawn ──────────────────────────────────────────────────────────
class c_base_player_pawn : public c_base_entity
{
public:
    SCHEMA_ADD_FIELD(c_player_weapon_services*, m_pWeaponServices, "C_BasePlayerPawn->m_pWeaponServices");
    SCHEMA_ADD_FIELD(c_player_item_services*, m_pItemServices, "C_BasePlayerPawn->m_pItemServices");
};

// ── C_CSPlayerPawn ────────────────────────────────────────────────────────────
class c_cs_player_pawn : public c_base_player_pawn
{
public:
    SCHEMA_ADD_FIELD(int32_t, m_ArmorValue, "C_CSPlayerPawn->m_ArmorValue");
    SCHEMA_ADD_FIELD(f_vector, m_angEyeAngles, "C_CSPlayerPawn->m_angEyeAngles");
    SCHEMA_ADD_FIELD(float, m_flFlashDuration, "C_CSPlayerPawnBase->m_flFlashDuration");
    SCHEMA_ADD_FIELD(float, m_flFlashOverlayAlpha, "C_CSPlayerPawnBase->m_flFlashOverlayAlpha");
    SCHEMA_ADD_FIELD(bool, m_bIsWalking, "C_CSPlayerPawn->m_bIsWalking");
    SCHEMA_ADD_FIELD(bool, m_bIsScoped, "C_CSPlayerPawn->m_bIsScoped");

    const std::string get_model_name();
};

// ── CBasePlayerController ─────────────────────────────────────────────────────
class c_base_player_controller : public c_base_entity
{
public:
    SCHEMA_ADD_FIELD(c_base_handle, m_hPawn, "CBasePlayerController->m_hPawn");
    SCHEMA_ADD_FIELD(uint64_t, m_steamID, "CBasePlayerController->m_steamID");
};

// ── CCSPlayerController_InGameMoneyServices ───────────────────────────────────
class c_in_game_money_services
{
public:
    SCHEMA_ADD_FIELD(int32_t, m_iAccount, "CCSPlayerController_InGameMoneyServices->m_iAccount");
};

// ── CCSPlayerController_InventoryServices ────────────────────────────────────
class c_inventory_services
{
public:
    SCHEMA_ADD_OFFSET(uint32_t, m_rank_id, 0x5C);
};

// ── CCSPlayerController_ActionTrackingServices ────────────────────────────────
class c_cs_player_controller_action_tracking_services
{
public:
    int32_t get_kills() const
    {
        return m_memory->read_t<int32_t>(
            reinterpret_cast<uintptr_t>(this)
            + SCHEMA_GET_OFFSET("CCSPlayerController_ActionTrackingServices->m_matchStats")
            + SCHEMA_GET_OFFSET("CSPerRoundStats_t->m_iKills"));
    }

    int32_t get_deaths() const
    {
        return m_memory->read_t<int32_t>(
            reinterpret_cast<uintptr_t>(this)
            + SCHEMA_GET_OFFSET("CCSPlayerController_ActionTrackingServices->m_matchStats")
            + SCHEMA_GET_OFFSET("CSPerRoundStats_t->m_iDeaths"));
    }

    int32_t get_assists() const
    {
        return m_memory->read_t<int32_t>(
            reinterpret_cast<uintptr_t>(this)
            + SCHEMA_GET_OFFSET("CCSPlayerController_ActionTrackingServices->m_matchStats")
            + SCHEMA_GET_OFFSET("CSPerRoundStats_t->m_iAssists"));
    }
};

// ── CCSPlayerController ───────────────────────────────────────────────────────
class c_cs_player_controller : public c_base_player_controller
{
public:
    SCHEMA_ADD_FIELD(c_in_game_money_services*, m_pInGameMoneyServices, "CCSPlayerController->m_pInGameMoneyServices");
    SCHEMA_ADD_FIELD(c_inventory_services*, m_pInventoryServices, "CCSPlayerController->m_pInventoryServices");
    SCHEMA_ADD_FIELD(c_cs_player_controller_action_tracking_services*, m_pActionTrackingServices, "CCSPlayerController->m_pActionTrackingServices");
    SCHEMA_ADD_FIELD(e_colors, m_iCompTeammateColor, "CCSPlayerController->m_iCompTeammateColor");
    SCHEMA_ADD_FIELD(int32_t, m_iCompetitiveRanking, "CCSPlayerController->m_iCompetitiveRanking");
    SCHEMA_ADD_FIELD(uint32_t, m_iPing, "CCSPlayerController->m_iPing");
    SCHEMA_ADD_FIELD(int32_t, m_iScore, "CCSPlayerController->m_iScore");
    SCHEMA_ADD_STRING(m_sSanitizedPlayerName, "CCSPlayerController->m_sSanitizedPlayerName");

    static c_cs_player_controller* get_local_player_controller();
    c_cs_player_pawn* get_player_pawn();
    const e_colors     get_color();
    const f_vector& get_vec_origin();
};

// ── C_PlantedC4 ───────────────────────────────────────────────────────────────
class c_planted_c4 : public c_base_entity
{
public:
    SCHEMA_ADD_FIELD(bool, m_bBombTicking, "C_PlantedC4->m_bBombTicking");
    SCHEMA_ADD_FIELD(float, m_flC4Blow, "C_PlantedC4->m_flC4Blow");
    SCHEMA_ADD_FIELD(bool, m_bBombDefused, "C_PlantedC4->m_bBombDefused");
    SCHEMA_ADD_FIELD(bool, m_bBeingDefused, "C_PlantedC4->m_bBeingDefused");
    SCHEMA_ADD_FIELD(float, m_flDefuseCountDown, "C_PlantedC4->m_flDefuseCountDown");
};

// ── CCSWeaponBaseVData ────────────────────────────────────────────────────────
class c_cs_weapon_base_v_data
{
public:
    SCHEMA_ADD_FIELD(e_weapon_type, m_WeaponType, "CCSWeaponBaseVData->m_WeaponType");
    SCHEMA_ADD_STRING(m_szName, "CCSWeaponBaseVData->m_szName");
    SCHEMA_ADD_FIELD(int32_t, m_iMaxClip1, "CBasePlayerWeaponVData->m_iMaxClip1");
};

// ── C_BasePlayerWeapon ────────────────────────────────────────────────────────
class c_base_player_weapon : public c_base_entity
{
public:
    SCHEMA_ADD_FIELD_OFFSET(c_cs_weapon_base_v_data*, m_WeaponData, "C_BaseEntity->m_nSubclassID", 0x08);
    SCHEMA_ADD_FIELD(int32_t, m_iClip1, "C_BasePlayerWeapon->m_iClip1");
    SCHEMA_ADD_FIELD(bool, m_bInReload, "C_CSWeaponBase->m_bInReload");

    int32_t get_reserve_ammo() const
    {
        const auto addr = reinterpret_cast<uintptr_t>(this)
            + SCHEMA_GET_OFFSET("C_BasePlayerWeapon->m_pReserveAmmo");
        if (!addr || addr <= 0x10000)
            return 0;
        return m_memory->read_t<int32_t>(addr);
    }

    c_base_player_weapon* get(const int32_t idx);
};

// ── Grenade projectile classes ────────────────────────────────────────────────

class c_base_grenade : public c_base_entity
{
public:
    SCHEMA_ADD_FIELD(bool, m_bIsLive, "C_BaseGrenade->m_bIsLive");
    SCHEMA_ADD_FIELD(float, m_flDetonateTime, "C_BaseGrenade->m_flDetonateTime");
    SCHEMA_ADD_FIELD(c_base_handle, m_hThrower, "C_BaseGrenade->m_hThrower");
};

class c_base_cs_grenade_projectile : public c_base_grenade
{
public:
    SCHEMA_ADD_FIELD(f_vector, m_vInitialVelocity, "C_BaseCSGrenadeProjectile->m_vInitialVelocity");
    SCHEMA_ADD_FIELD(float, m_flSpawnTime, "C_BaseCSGrenadeProjectile->m_flSpawnTime");
    SCHEMA_ADD_FIELD(bool, m_bExplodeEffectBegan, "C_BaseCSGrenadeProjectile->m_bExplodeEffectBegan");
};

class c_smoke_grenade_projectile : public c_base_cs_grenade_projectile
{
public:
    SCHEMA_ADD_FIELD(bool, m_bDidSmokeEffect, "C_SmokeGrenadeProjectile->m_bDidSmokeEffect");
    SCHEMA_ADD_FIELD(bool, m_bSmokeEffectSpawned, "C_SmokeGrenadeProjectile->m_bSmokeEffectSpawned");
    SCHEMA_ADD_FIELD(f_vector, m_vSmokeDetonationPos, "C_SmokeGrenadeProjectile->m_vSmokeDetonationPos");
};

class c_molotov_projectile : public c_base_cs_grenade_projectile
{
public:
    SCHEMA_ADD_FIELD(bool, m_bIsIncGrenade, "C_MolotovProjectile->m_bIsIncGrenade");
};

class c_decoy_projectile : public c_base_cs_grenade_projectile
{
public:
    SCHEMA_ADD_FIELD(int32_t, m_nDecoyShotTick, "C_DecoyProjectile->m_nDecoyShotTick");
};

class c_inferno : public c_base_entity
{
public:
    SCHEMA_ADD_FIELD(int32_t, m_fireCount, "C_Inferno->m_fireCount");
    SCHEMA_ADD_FIELD(uint32_t, m_nInfernoType, "C_Inferno->m_nInfernoType");
    SCHEMA_ADD_FIELD(float, m_nFireLifetime, "C_Inferno->m_nFireLifetime");
    SCHEMA_ADD_FIELD(bool, m_bInPostEffectTime, "C_Inferno->m_bInPostEffectTime");
};

// ── C_CSGameRules ─────────────────────────────────────────────────────────────
class c_cs_game_rules : public c_base_entity
{
public:
    SCHEMA_ADD_FIELD(bool, m_bFreezePeriod, "C_CSGameRules->m_bFreezePeriod");
    SCHEMA_ADD_FIELD(bool, m_bWarmupPeriod, "C_CSGameRules->m_bWarmupPeriod");
    SCHEMA_ADD_FIELD(float, m_fWarmupPeriodEnd, "C_CSGameRules->m_fWarmupPeriodEnd");
    SCHEMA_ADD_FIELD(int32_t, m_iRoundTime, "C_CSGameRules->m_iRoundTime");
    SCHEMA_ADD_FIELD(float, m_fRoundStartTime, "C_CSGameRules->m_fRoundStartTime");
    SCHEMA_ADD_FIELD(float, m_flRestartRoundTime, "C_CSGameRules->m_flRestartRoundTime");
    SCHEMA_ADD_FIELD(int32_t, m_gamePhase, "C_CSGameRules->m_gamePhase");
    SCHEMA_ADD_FIELD(int32_t, m_totalRoundsPlayed, "C_CSGameRules->m_totalRoundsPlayed");
    SCHEMA_ADD_FIELD(int32_t, m_nRoundsPlayedThisPhase, "C_CSGameRules->m_nRoundsPlayedThisPhase");
    SCHEMA_ADD_FIELD(int32_t, m_nOvertimePlaying, "C_CSGameRules->m_nOvertimePlaying");
    SCHEMA_ADD_FIELD(bool, m_bBombPlanted, "C_CSGameRules->m_bBombPlanted");
    SCHEMA_ADD_FIELD(bool, m_bBombDropped, "C_CSGameRules->m_bBombDropped");
    SCHEMA_ADD_FIELD(int32_t, m_iRoundWinStatus, "C_CSGameRules->m_iRoundWinStatus");
};

// ── C_CSGameRulesProxy ────────────────────────────────────────────────────────
class c_cs_game_rules_proxy : public c_base_entity
{
public:
    c_cs_game_rules* get_game_rules()
    {
        return m_memory->read_t<c_cs_game_rules*>(
            reinterpret_cast<uintptr_t>(this) + 0x600);
    }
};

// ── C_CSTeam ──────────────────────────────────────────────────────────────────
class c_cs_team : public c_base_entity
{
public:
    SCHEMA_ADD_OFFSET(int32_t, m_scoreFirstHalf, 0x8C0);
    SCHEMA_ADD_OFFSET(int32_t, m_scoreSecondHalf, 0x8C4);
    SCHEMA_ADD_OFFSET(int32_t, m_scoreOvertime, 0x8C8);

    int32_t m_iScore()
    {
        return m_scoreFirstHalf() + m_scoreSecondHalf() + m_scoreOvertime();
    }
};
