using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;

namespace Grid {
    public class LevelGrid : MonoBehaviour {
        public const float FLOOR_HEIGHT = 3f;

        [FormerlySerializedAs("GridDebugObjectPrefab")] [SerializeField]
        private Transform gridDebugObjectPrefab;

        [SerializeField] private int width = 20;
        [SerializeField] private int height = 20;
        [SerializeField] private float cellSize = 2f;
        [SerializeField] private int floorAmount;

        private List<GridSystem<GridObject>> gridSystemList;

        public int FloorAmount => floorAmount;

        public static LevelGrid Instance { get; private set; }

        private void Awake() {
            if (Instance is not null) {
                Debug.LogError($"There is more than one LevelGrid! {transform} - {Instance}");
                Destroy(gameObject);
                return;
            }

            Instance = this;

            gridSystemList = new();
            for (int floor = 0; floor < FloorAmount; floor++) {
                GridSystem<GridObject> gridSystem = new(width, height, cellSize, floor, FLOOR_HEIGHT, (g, gridPosition) => new(g, gridPosition));
                gridSystemList.Add(gridSystem);
            }
        }

        private void Start() => Pathfinding.Instance.Setup(width, height, cellSize, floorAmount);

        private GridSystem<GridObject> GetGridSystem(int floor) {
            if (floor >= 0 && floor < floorAmount) return gridSystemList[floor];
            Debug.LogError($"Try to access floor {floor} and has only {gridSystemList.Count} GridSystems in list!");
            return null;
        }

        public event EventHandler OnAnyUnitMovedGridPosition;

        public void AddUnitAtGridPosition(GridPosition gridPosition, Unit unit) => GetGridSystem(gridPosition.Floor).GetGridObject(gridPosition).UnitList.Add(unit);

        public List<Unit> GetUnitListAtGridPosition(GridPosition gridPosition) => GetGridSystem(gridPosition.Floor).GetGridObject(gridPosition).UnitList;
        public void RemoveUnitAtGridPosition(GridPosition gridPosition, Unit unit) => GetGridSystem(gridPosition.Floor).GetGridObject(gridPosition).UnitList.Remove(unit);

        public static int GetFloor(Vector3 worldPosition) => Mathf.RoundToInt(worldPosition.y / FLOOR_HEIGHT);

        public GridPosition GetGridPosition(Vector3 worldPosition) => GetGridSystem(GetFloor(worldPosition)).GetGridPosition(worldPosition);
        public Vector3 GetWorldPosition(GridPosition gridPosition) => GetGridSystem(gridPosition.Floor).GetWorldPosition(gridPosition);

        public bool IsValidGridPosition(GridPosition gridPosition) {
            if (gridPosition.Floor >= 0 && gridPosition.Floor < FloorAmount) return GetGridSystem(gridPosition.Floor).IsValidGridPosition(gridPosition);
            // Debug.LogError($"Floor {gridPosition.Floor} is over floorAmount {FloorAmount}");
            return false;
        }

        public int GetWidth() => GetGridSystem(0).Width;
        public int GetHeight() => GetGridSystem(0).Height;

        public void UnitMovedGridPosition(Unit unit, GridPosition fromGridPosition, GridPosition toGridPosition) {
            RemoveUnitAtGridPosition(fromGridPosition, unit);
            AddUnitAtGridPosition(toGridPosition, unit);
            OnAnyUnitMovedGridPosition?.Invoke(this, EventArgs.Empty);
        }

        public bool HasAnyUnitOnGridPosition(GridPosition gridPosition) => GetGridSystem(gridPosition.Floor).GetGridObject(gridPosition).UnitList.Count > 0;
        public Unit GetUnitAtGridPosition(GridPosition gridPosition) => GetGridSystem(gridPosition.Floor).GetGridObject(gridPosition).GetUnit();
        public IInteractable GetInteractableAtGridPosition(GridPosition gridPosition) => GetGridSystem(gridPosition.Floor).GetGridObject(gridPosition).Interactable;
        public void SetInteractableAtGridPosition(GridPosition gridPosition, IInteractable interactable) => GetGridSystem(gridPosition.Floor).GetGridObject(gridPosition).Interactable = interactable;
    }
}