using Godot;
using System;
using System.Linq;
using System.Threading.Tasks;
using Cirno.Scripts;
using Cirno.Scripts.Components.FSM;
using Cirno.Scripts.Controllers;
using Cirno.Scripts.Enums;
using Cirno.Scripts.Misc;
using Cirno.Scripts.Resources;
using Godot.Collections;
using Cirno.Scripts.Utils;
using Environment = Godot.Environment;

public partial class GameManager : Node2D
{
    public static GameManager Instance { get; private set; }
    private Hud _hud;

    private PlayerFSMProxy _player;

    public GameState GameState { get; private set; }

    public PlayerStateMachine Player => _player.PlayerFSM;

    private Node2D _cameraTarget;

    public Vector2? PlayerPosition => _player?.GlobalPosition ?? null;
    public Vector2? PlayerVelocity => _player?.Velocity ?? null;

    [Export] public MapResource MapResource { get; private set; }
    
    [Export] public PackedScene PlayerTemplate { get; set; }

    [Export] public Dictionary<int, NodePath> SpawnMarkers { get; private set; } = new();

    //[Export] public Marker2D PlayerSpawnMarker { get; set; }
    private InventoryManager _inventoryManager { get; set; }

    //private AlarmManager _alarmManager { get; set; }

    //public InventoryManager Inventory => _inventoryManager;

    //public AlarmManager AlarmManager => _alarmManager;

    [Export]
    public StringName PauseActionName { get; private set; } = "pause";

    [Export]
    public TilemapAvoidance NavigationTilemap { get; private set; }
    
    private Node2D _bulletsContainer;
    public Node2D BulletsContainer => _bulletsContainer;

    [Signal]
    public delegate void GameStateChangeEventHandler(GameState state);

    [Signal]
    public delegate void PlayerRespawnedEventHandler();

    public Vector2 LastCheckpointPosition { get; set; }

    private AudioManager _audioManager;
    
    [Export]
    public Node2D PlayerParentNode { get; set; }

    [Export]
    public int EggStartIndex = 0;
    
    [Export] public NavigationRegion2D NavigationRegion { get; private set; }
    
    [Signal] public delegate void ManagerReadyEventHandler();
    
    // Called when the node enters the scene tree for the first time.
    public override void _Ready()
    {
        Instance = this;
        RenderingServer.SetDefaultClearColor(Colors.Black);
        if (GlobalState.Instance.SessionSettings.GameMode is GameMode.Roguelite)
        {
            if (GlobalState.Instance.SessionSettings.LevelNumber < 0)
            {
                // TODO: Change based on which level we're going to
                GlobalState.Instance.SessionSettings.LevelNumber = 0;
            }
        }
        else
        {
            GlobalState.Session.LevelNumber = MapResource.LevelId;
        }
        
        
        GlobalState.Instance.ChangeCursor(false);
        
        if (GlobalState.Instance.SessionSettings.AllowSaving)
        {
            GlobalState.Instance.SaveGame();
        }
        
        _audioManager = new AudioManager();
        this.AddChild(_audioManager);
        
        _hud = GetNodeOrNull<Hud>("HUD");
        if (_hud == null) GD.Print("No HUD in scene.");

        _cameraTarget = GetNodeOrNull<Node2D>("CameraTarget");
        if (_cameraTarget == null) GD.Print("No camera target in scene.");

        _inventoryManager = GetNodeOrNull<InventoryManager>("InventoryManager");
        if (_inventoryManager == null) GD.Print("No inventory manager in scene.");

        //_alarmManager = GetNode<AlarmManager>("AlarmManager");

        SpawnBulletsContainer();

        if (_hud != null)
        {
            this.GameStateChange += _hud.OnGameStateChanged;
        }

        if (_inventoryManager != null && _hud != null)
        {
            //_inventoryManager.ItemAdded += (item, currentAmount) => _hud.AddInventoryItem(item, currentAmount);
            //_inventoryManager.ItemRemoved += (item, currentAmount) => _hud.RemoveInventoryItem(item, currentAmount);

            _inventoryManager.WeaponUpdate += key => _hud.EquipWeapon(key);
        }

        PlayerRespawned += OnPlayerRespawned;

        GameState = GameState.Playing;

        //_ = DelayPlayerSpawn();

        CallDeferred(MethodName.DelayPlayerSpawn);

        CallDeferred(MethodName.OnFinished);
    }

    private void OnFinished()
    {
        EmitSignalManagerReady();
    } 
    
    private void OnPlayerRespawned()
    {
        this.ClearBullets();
    }

    public void ApplyMapStartData(MapStartDataResource mapStartData)
    {
        EggStartIndex = mapStartData.EggIndex;
        //MapStartData = mapStartData;

        // StartingEquipment.AddRange(mapStartData.StartingEquipment);
    }

    public void AddMotivation(float motivation)
    {
        Player.AddMotivation(motivation);
    }

    public void ApplySessionState(SessionSettings settings)
    {
        //_inventoryManager.Load(settings.Items);
    }

    public override void _Process(double delta)
    {
        if (GameState is GameState.Paused && Input.IsActionJustPressed(PauseActionName))
        {
            Unpause();
        }
    }

    private void DelayPlayerSpawn()
    {
        if (SpawnMarkers.Any())
        {
            SpawnPlayer();
        }
        else
        {
            GD.Print("No player spawn marker in scene.");
            return;
        }

        if (_player != null && _hud != null)
        {
            // _player.HealthChanged += (newHealth, maxHealth) => _hud.UpdateHealth(newHealth, maxHealth);
            //
            // _player.ShieldChanged += (newShield, maxShield) => _hud.UpdateShield(newShield, maxShield);
            //
            // _player.InteractableAreaEntered += (interactable) => _hud.UpdateInteractable(interactable);
            //
            // _player.Death += () =>
            // {
            //     // Show Game Over
            //     _hud.ShowGameOver();
            // };
        }
        else
        {
            GD.Print("No player and hud in scene");
            return;
        }

        // Wait before the player is fully initialized before spawning weapons on it
        CallDeferred(MethodName.SpawnWeapons);
    }

    public void SpawnPlayer()
    {
        if (_player != null) return;

        //_player = this.CreateChild<PlayerMovement>(PlayerTemplate, PlayerSpawnMarker.Position );
        _player = PlayerTemplate.Instantiate<PlayerFSMProxy>();

        //this.CallDeferred("add_child", _player);
        if (PlayerParentNode is not null)
        {
            PlayerParentNode.AddChild(_player);
            _player.Owner = PlayerParentNode;
        }
        else
        {
            this.AddChild(_player);
            _player.Owner = this;
        }
        
        _player.Transform = this.GlobalTransform;
        
        _player.GlobalPosition = GetStartPosition();

        LastCheckpointPosition = _player.GlobalPosition;

        //_player.GlobalPosition = PlayerSpawnMarker.Position;

        CameraTargetPlayer();

        //
        // if (_cameraTarget != null)
        // {
        // 	_cameraTarget.Reparent(_player, true);
        // 	_cameraTarget.GlobalPosition = _player.Position;
        // }
    }

    private Vector2 GetStartPosition()
    {
        if (MapResource != null)
        {
            if (SpawnMarkers.TryGetValue(EggStartIndex, out var spawnMarker))
            {
                var marker = GetNode<Node2D>(spawnMarker);

                return marker.Position; // Why position and not globalposition? I have no idea
            }
        }

        var m = GetNode<Node2D>(SpawnMarkers.First().Value);
        return m.GlobalPosition;
    }

    public void CameraTargetPlayer()
    {
        if (_player is null) return;
        CameraTargetObject(_player);
    }

    public void CameraTargetObject(Node2D target, Vector2? offset = null)
    {
        if (_cameraTarget is null) return;
        _cameraTarget.Reparent(target, true);
        _cameraTarget.GlobalPosition = target.GlobalPosition;

        if (offset.HasValue)
        {
            _cameraTarget.Position += offset.Value;
        }

    }

    private void SpawnWeapons()
    {
        foreach (var startingItem in MapResource.StartData.StartingEquipment)
        {
            _inventoryManager.AddItem(startingItem);
        }

        foreach (var item in MapResource.StartData.RemoveEquipment)
        {
            _inventoryManager.RemoveItem(item);
        }
    }

    private void SpawnBulletsContainer()
    {
        _bulletsContainer = new Node2D();
        _bulletsContainer.Name = "BulletsContainer";
        _bulletsContainer.ProcessMode = ProcessModeEnum.Pausable;

        AddChild(_bulletsContainer);
    }

    // public void TogglePause()
    // {
    //     if (GameState == GameState.Paused)
    //     {
    //         Unpause();
    //     }
    //     else if (GameState == GameState.Playing)
    //     {
    //         Pause();
    //     }
    // }

    public void Pause()
    {
        if (GameState == GameState.Playing)
        {
            ChangeState(GameState.Paused);
        }
    }

    public void Unpause()
    {
        if (GameState == GameState.Paused)
        {
            CallDeferred(MethodName.ChangeState, (int)GameState.Playing);
            //ChangeState(GameState.Playing);
        }
    }

    public GameState ToggleControlMode()
    {
        if (GameState is GameState.Playing)
        {
            ChangeState(GameState.Controlling);
        }
        else if (GameState is GameState.Controlling)
        {
            ChangeState(GameState.Playing);
        }

        return GameState;
    }

    public void ChangeState(GameState state)
    {
        if (state == GameState) return;
        GameState = state;
        EmitSignal(SignalName.GameStateChange, (int)state);
        GD.Print($"Game state changed to {state}");

        switch (state)
        {
            case GameState.Paused:
            case GameState.Dialogue:
            case GameState.Shop:
            case GameState.Inventory:
                GlobalState.Instance.ChangeCursor(true);
                
                GetTree().SetPause(true);
                //Input.MouseMode = Input.MouseModeEnum.Visible;
                break;
            case GameState.Playing:
            case GameState.Controlling:
                //Input.MouseMode = Input.MouseModeEnum.Confined;
                GlobalState.Instance.ChangeCursor(false);
                DelayedUnpause();
                //CallDeferred(MethodName.DelayedUnpause);
                //GetTree().SetPause(false);
                break;
            case GameState.Menu:
                GlobalState.Instance.ChangeCursor(true);
                //Input.MouseMode = Input.MouseModeEnum.Visible;
                DelayedUnpause();
                //CallDeferred(MethodName.DelayedUnpause);
                //GetTree().SetPause(false);
                break;
        }
    }

    private void DelayedUnpause()
    {
        GetTree().SetPause(false);
    }

    public void ClearBullets()
    {
        PoolingManager.Instance.DisableAllBullets();
        // if (_bulletsContainer is null) return;
        // foreach (var node in _bulletsContainer.GetChildren())
        // {
        //     node.QueueFree();
        // }
    }

    public void RecalculateTilemap(Vector2 position)
    {
        //CallDeferred(MethodName.RecalculateTilemapDeferred, position);
    }
    
    private void RecalculateTilemapDeferred(Vector2 position)
    {
        //NavigationTilemap.Recalculate(position);
        GD.Print("Rebaking tilemap");
        NavigationRegion.BakeNavigationPolygon(true);
    }

    public void RebakeNavigation()
    {
        GD.Print("Rebaking tilemap");
        //NavigationRegion.BakeNavigationPolygon(true);
    }
}

public enum GameState
{
    Menu,
    Paused,
    Playing,
    Dialogue,
    Controlling,
    Inventory,
    Shop
}