DemoPlayer.cs
using Sandbox.Citizen;
using Sandbox.Events;
using Sandbox.Movement;
using Sandbox.Sboku;
using Sandbox.Sboku.Arena;
using Sandbox.Sboku.UI;
using SWB.Base;
using SWB.Demo.Clothing;
using SWB.HUD;
using SWB.Player;
using SWB.Shared;
using SWB.Visuals;
using SWB.Visuals.Profiles;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace SWB.Demo;

[Group("SWB")]
[Title("Demo Player")]
public partial class DemoPlayer : PlayerBase,
    IGameEventHandler<RoundManager.OpenUpgradeScreen>,
    IGameEventHandler<RoundManager.Victory>
{

    

    private bool IsPaused;
    private PauseMenu PauseMenu;

    public enum MovementMode
    {
        Grid,
        Free
    }
    private bool IsStartMenuOpen;
    public MovementMode CurrentMovementMode { get; private set; } = MovementMode.Free;

    [Property]
    public GameObject ArenaUI { get; set; }

    /// <summary>
    /// Grid-pelimuodissa ei käytetä upgradeja
    /// </summary>
    public bool UsesUpgrades => false;

    private GridMap GridMap
    {
        get
        {
            return Scene.GetComponentInChildren<GridSpawner>()?.Map;
        }
    }

    protected override void OnAwake()
    {
        base.OnAwake();

        
    }
   
    // -------------------------------------------------
    //  START / UI
    // -------------------------------------------------

    protected override void OnStart()
    {
        base.OnStart();

        if (ArenaUI.IsValid())
            ArenaUI.Enabled = false;

        if (!IsValid)
            return;
        

        var grid = GameObject.Components.GetOrCreate<GridMovement>();
        var free = Components.Get<CameraMovement>();

        ApplyMovementMode();

        if (ArenaUI.IsValid())
        {
            ArenaUI.Enabled = true;
            PauseMenu = ArenaUI.Components.GetOrCreate<PauseMenu>();
        }

        // 👇 post process kameraan
        if (Camera != null)
        {
            Camera.GameObject.Components.GetOrCreate<DarkProfile>();
        }

    }


    void DebugNavMeshBounds()
    {
        var nav = Scene.NavMesh;
        if (nav == null) return;

        Vector3 min = new(float.MaxValue);
        Vector3 max = new(float.MinValue);

        for (int x = -10000; x <= 10000; x += 200)
            for (int y = -10000; y <= 10000; y += 200)
            {
                var p = new Vector3(x, y, 1000);
                var hit = nav.GetClosestPoint(p, 2000f);

                if (hit.HasValue)
                {
                    min = Vector3.Min(min, hit.Value);
                    max = Vector3.Max(max, hit.Value);
                }
            }

        DebugOverlay.Box((min + max) * 0.5f, max - min, Color.Green, 30f);
        Log.Info($"NavMesh bounds: {min} -> {max}");
    }


    protected override void OnUpdate()
    {
        base.OnUpdate();

        if (IsRagdolled)
            return;

            if (IsProxy || !IsValid)
            return;

        if (Input.Pressed("toggleconsole"))
        {
            // älä tee mitään
        }

        // ESC → pause
        if (Input.Pressed("pause_menu"))
        {
            TogglePause();
            return;
        }

        if (IsPaused)
            return;

        // 🔒 Lukitse body pystysuoraan (FPS)
        var yaw = Rotation.FromYaw(Camera.Transform.Rotation.Yaw());
        Transform.Rotation = yaw;


        if (Input.Pressed("toggle_movement")) // X
        {
            ToggleMovementMode();
        }

  
    }

    private void TogglePause()
    {
        if (IsPaused)
            ResumeGame();
        else
            PauseGame();
    }

    private void PauseGame()
    {
        IsPaused = true;

        Mouse.Visibility = MouseVisibility.Visible;

        PauseMenu?.Show();

        if (Camera != null) Camera.Enabled = false;
        if (ViewModelCamera != null) ViewModelCamera.Enabled = false;

        var grid = Components.Get<GridMovement>();
        if (grid != null)
            grid.IgnoreInput = true;

        var cam = Components.Get<CameraMovement>();
        if (cam != null)
            cam.Enabled = false;

        var vmh = Components.Get<ViewModelHandler>();
        if (vmh != null)
            vmh.Enabled = false;
    }

    public void ResumeGame()
    {
        IsPaused = false;

        Mouse.Visibility = MouseVisibility.Hidden;

        PauseMenu?.Hide();

        if (Camera != null) Camera.Enabled = true;
        if (IsProxy && ViewModelCamera != null)
        {
            ViewModelCamera.Enabled = true;
        }

        ApplyMovementMode();

        var grid = Components.Get<GridMovement>();
        if (grid != null)
            grid.IgnoreInput = false;

        if (IsProxy)
        {
            var vmh = Components.Get<ViewModelHandler>();
            if (vmh != null)
            {
                vmh.Enabled = true;
                vmh.Reset(); // ⬅️ tärkeä
            }
        }
    }

    private void ToggleMovementMode()
    {
        var grid = Components.Get<GridMovement>();
        var free = Components.Get<CameraMovement>();

        if (grid == null)
            return;

        if (CurrentMovementMode == MovementMode.Grid)
        {
            // GRID → FREE
            CurrentMovementMode = MovementMode.Free;

            grid.EnterFreeMode();
            if (free != null)
                free.Enabled = true;
        }
        else
        {
            // FREE → GRID
            CurrentMovementMode = MovementMode.Grid;

            if (free != null)
                free.Enabled = false;

            grid.EnterGridMode(); // 🔑 TÄMÄ KORJAA ONGELMAN
        }
    }


    // -------------------------------------------------
    //  RESPAWN
    // -------------------------------------------------

    public override void Respawn()
    {
        IsDead = false;
        Health = MaxHealth;

        var spawn = GetSpawnLocation();
        WorldPosition = spawn.Position;
        EyeAngles = spawn.Rotation.Angles();
        Network.ClearInterpolation();

        // 🔑 sama objekti herää henkiin
        BodyRenderer.Enabled = true;
        AnimationHelper.Enabled = true;

        Inventory.Clear();
        GiveWeapon("swb_veresk", true);

        _ = RespawnAfterGridReady();
    }





    private async Task RespawnAfterGridReady()
    {
        // 🔑 VAIN SERVER ODOTTAA GRIDMAPPIA
        if (!IsProxy)
        {
            for (int i = 0; i < 10; i++)
            {
                if (GridMap != null)
                    break;

                await Task.Delay(1);
            }

            if (GridMap == null)
            {
                Log.Error("[DemoPlayer] GridMap never became available");
                return;
            }

            WorldPosition = GetRandomPlayerSpawn();

            var grid = Components.Get<GridMovement>();
            grid?.SnapToGrid();
        }

        // 🎮 CLIENT-ONLY ASIAT
        Mouse.Visibility = MouseVisibility.Hidden;

        // SERVER
        if (!IsProxy && Inventory.Items.Count == 0)
        {
            GiveWeapon("swb_veresk", true); // setActive = true
        }

    }



    public override async void RespawnWithDelay(float delay)
    {
        await GameTask.DelaySeconds(delay);

        // ❌ EI GameOveria
        // ✅ vain tämä pelaaja respawnaa
        Respawn();

        // 🔑 Palauta input clientillä seuraavassa framessa
        await GameTask.Delay(1);
        RestoreClientInput();
    }

    private void RestoreClientInput()
    {
        if (IsProxy) // client
        {
            Mouse.Visibility = MouseVisibility.Hidden;

            BodyRenderer.RenderType = ModelRenderer.ShadowRenderType.ShadowsOnly;

            var movement = Components.Get<CameraMovement>();
            if (movement != null)
                movement.Enabled = true;

            if (Camera != null)
                Camera.Enabled = true;

            if (ViewModelCamera != null)
                ViewModelCamera.Enabled = true;
        }
    }


    // -------------------------------------------------
    //  GAME EVENTS
    // -------------------------------------------------

    /// <summary>
    /// Upgrade-event on pakko implementoida,
    /// mutta grid-modessa se IGNOROIDAAN täysin.
    /// </summary>
    public void OnGameEvent(RoundManager.OpenUpgradeScreen eventArgs)
    {
        // Intentionally empty
        // Älä dispatchaa UpgradeScreenClosed
        // Älä avaa UI:ta
    }

    public void OnGameEvent(RoundManager.Victory eventArgs)
    {
        if (!IsValid || IsProxy)
            return;

        //CreateVictoryScreen();
    }


    // -------------------------------------------------
    //  END SCREENS
    // -------------------------------------------------

    public void CreateFailScreen()
    {
        if (ArenaUI.IsValid())
            ArenaUI.AddComponent<EndMenu>();
    }

    public void CreateVictoryScreen()
    {
        if (!ArenaUI.IsValid())
            return;

        var menu = ArenaUI.AddComponent<EndMenu>();
        menu.WinVariant = true;
    }

    // -------------------------------------------------
    //  CLEANUP
    // -------------------------------------------------

    protected override void OnDestroy()
    {
        base.OnDestroy();
 
    }

    public void GiveWeapon(string className, bool setActive = true)
    {
        var weapon = WeaponRegistry.Instance.Get(className);

        if (weapon == null)
        {
            Log.Error($"[DemoPlayer] Weapon '{className}' not found!");
            return;
        }

        // Lisää ase inventoryyn
        var weaponGO = Inventory.AddClone(weapon.GameObject, false);

        if (weaponGO == null)
            return;

        // 🔫 AKTIVOI ASE KÄTEEN
        if (setActive)
        {
            Inventory.SetActive(weaponGO);
        }

        SetAmmo(weapon.Primary.AmmoType, 360);

        Log.Info($"Inventory items: {Inventory.Items.Count}");
        Log.Info($"Active item: {Inventory.Active?.Name}");
    }


    private Vector3 GetRandomPlayerSpawn()
    {
        var prisonSpawns = Scene.GetAllComponents<PrisonSpawnMarker>()
            .Where(m => m.IsValid && m.PreferForPlayers)
            .Select(m => m.GameObject.WorldPosition + Vector3.Up * 64f)
            .ToList();

        if (prisonSpawns.Count > 0)
        {
            // Etsi toinen elossa oleva pelaaja (jos on)
            var otherPlayer = Scene.GetAllComponents<DemoPlayer>()
                .FirstOrDefault(p => p != this && p.IsValid);

            // Jos ei ole toista pelaajaa → satunnainen spawn
            if (otherPlayer == null)
            {
                return prisonSpawns[Game.Random.Int(0, prisonSpawns.Count - 1)];
            }

            // Valitse kauimmaisin spawn suhteessa toiseen pelaajaan
            Vector3 bestPos = WorldPosition;
            float bestDistSqr = -1f;

            foreach (var worldPos in prisonSpawns)
            {
                float distSqr = worldPos.DistanceSquared(otherPlayer.Camera.WorldPosition);

                if (distSqr > bestDistSqr)
                {
                    bestDistSqr = distSqr;
                    bestPos = worldPos;
                }
            }

            return bestPos;
        }

        var map = GridMap;
        if (map == null)
        {
            Log.Warning("[DemoPlayer] No prison spawn markers or GridMap found, using current position");
            return WorldPosition;
        }

        var spawnCells = map.GetAllSpawnPoints('@')
            .Where(c => map.IsWalkable(c.x, c.y))
            .ToList();

        if (spawnCells.Count == 0)
        {
            Log.Warning("[DemoPlayer] No '@' spawn points found");
            return WorldPosition;
        }

        var fallbackPlayer = Scene.GetAllComponents<DemoPlayer>()
            .FirstOrDefault(p => p != this && p.IsValid);

        if (fallbackPlayer == null)
        {
            var cell = spawnCells[Game.Random.Int(0, spawnCells.Count - 1)];
            return map.GridToWorld(cell.x, cell.y, 64f);
        }

        Vector3 fallbackBest = WorldPosition;
        float fallbackBestDistSqr = -1f;

        foreach (var cell in spawnCells)
        {
            var worldPos = map.GridToWorld(cell.x, cell.y, 64f);
            float distSqr = worldPos.DistanceSquared(fallbackPlayer.Camera.WorldPosition);

            if (distSqr > fallbackBestDistSqr)
            {
                fallbackBestDistSqr = distSqr;
                fallbackBest = worldPos;
            }
        }

        return fallbackBest;
    }


    [Rpc.Host]
    public void ApplyEnvironmentalDamage(int damage)
    {
        if (IsProxy)
            return;

        Health -= damage;

        if (Health <= 0)
        {
            Health = 0;
            OnKilledByEnvironment();
        }
    }

    private void OnKilledByEnvironment()
    {
        // vain tämä pelaaja kuolee
        Respawn();
    }


    


    private void ApplyMovementMode()
    {
        var grid = Components.Get<GridMovement>();
        var free = Components.Get<CameraMovement>();

        if (grid == null)
            return;

        if (CurrentMovementMode == MovementMode.Free)
        {
            // 🔑 FREE MODE
            grid.Enabled = false;              // ⬅️ TÄMÄ PUUTTUI
            grid.IgnoreInput = false;

            if (free != null)
                free.Enabled = true;

            grid.EnterFreeMode();
        }
        else
        {
            // 🔑 GRID MODE
            if (free != null)
                free.Enabled = false;

            grid.Enabled = true;               // ⬅️ JA TÄMÄ
            grid.IgnoreInput = false;

            grid.EnterGridMode();
        }
    }

    public async Task DestroyAllUIAsync()
    {
        // odota että kaikki tämän framen eventit on ajettu
        await GameTask.Delay(1);

        if (!ArenaUI.IsValid())
            return;

        foreach (var panel in ArenaUI.Components.GetAll<ScreenPanel>())
        {
            panel.GameObject.Destroy();
        }

        ArenaUI.Enabled = false;
        IsStartMenuOpen = false;
    }

    [Rpc.Broadcast]
    public override void OnDeath(Shared.DamageInfo info)
    {
        if (IsDead) return;

        IsDead = true;
        Health = 0;

        SpawnDeathModel(info.Force, info.Origin);

        // 🔑 pelaaja piiloon, EI POIS
        BodyRenderer.Enabled = false;
        AnimationHelper.Enabled = false;

        CharacterController.Velocity = 0;

        // 🔑 botit näkevät yhä targetin
        RespawnWithDelay(2f);
    }


    private void SpawnDeathModel(Vector3 force, Vector3 origin)
    {
        var death = Scene.CreateObject();
        death.Name = "PlayerDeath";

        death.WorldPosition = Body.WorldPosition;
        death.WorldRotation = Body.WorldRotation;

        // renderer
        var smr = death.Components.Create<SkinnedModelRenderer>();
        smr.Model = BodyRenderer.Model;

        // 🧥 vaatteet
        ClothingBuilder.Build(
            smr,
            clothingJSON,   // 🔑 käytä pelaajan omia vaatteita
            null,
            null,
            null,
            null,
            null,
            null
        );

        // physics
        var phys = death.Components.Create<ModelPhysics>();
        phys.Renderer = smr;
        phys.Enabled = true;

        if (phys.PhysicsGroup != null)
        {
            foreach (var body in phys.PhysicsGroup.Bodies)
                body.ApplyImpulseAt(origin, force);
        }

        // estä animaatiot
        foreach (var anim in death.Components.GetAll<CitizenAnimationHelper>())
            anim.Enabled = false;
    }


}