using System;
using AceFieldNewHorizon.Scripts.Tiles;
using Godot;
using System.Linq;

namespace AceFieldNewHorizon.Scripts.System;

public partial class PlacementManager : Node2D
{
    [Export] public GridManager Grid { get; set; }
    [Export] public BuildingRegistry Registry { get; set; }

    private string _currentBuildingId = "wall";
    private Vector2I _hoveredCell;
    private BaseTile _ghostBuilding;
    private float _currentRotation;
    private Vector2I _currentBuildingSize = Vector2I.One;

    public void SetCurrentBuilding(string buildingId)
    {
        _currentBuildingId = buildingId;
        var buildingData = Registry.GetBuilding(buildingId);
        if (buildingData != null)
        {
            _currentBuildingSize = buildingData.Size;
            // Reset rotation to nearest allowed rotation
            if (!buildingData.IsRotationAllowed(_currentRotation))
            {
                _currentRotation = (int)buildingData.AllowedRotations[0] * 90f;
            }
        }

        // Replace ghost immediately
        if (_ghostBuilding != null)
        {
            _ghostBuilding.QueueFree();
            _ghostBuilding = null;
        }
    }

    private void RotateGhost(bool reverse = false)
    {
        if (_ghostBuilding == null) return;

        var buildingData = Registry.GetBuilding(_currentBuildingId);
        if (buildingData == null) return;

        // Calculate next rotation
        var currentDirection = (RotationDirection)((Mathf.RoundToInt(_currentRotation / 90f) % 4 + 4) % 4);
        var currentIndex = Array.IndexOf(buildingData.AllowedRotations.ToArray(), currentDirection);

        if (reverse)
            currentIndex = (currentIndex - 1 + buildingData.AllowedRotations.Length) %
                           buildingData.AllowedRotations.Length;
        else
            currentIndex = (currentIndex + 1) % buildingData.AllowedRotations.Length;

        _currentRotation = (int)buildingData.AllowedRotations[currentIndex] * 90f;
        _ghostBuilding.RotationDegrees = _currentRotation;

        // Update ghost position to keep the same cell under cursor
        UpdateGhostPosition();
    }

    private void UpdateGhostPosition()
    {
        if (_ghostBuilding == null) return;

        var buildingData = Registry.GetBuilding(_currentBuildingId);
        if (buildingData == null) return;

        var rotatedSize = buildingData.GetRotatedSize(_currentRotation);
        var offset = GridUtils.GetCenterOffset(rotatedSize, _currentRotation);
        _ghostBuilding.Position = GridUtils.GridToWorld(_hoveredCell) + offset;

        // Update occupied cells
        GridUtils.GetOccupiedCells(_hoveredCell, rotatedSize, _currentRotation);
    }

    public override void _Process(double delta)
    {
        // Snap mouse to grid
        var mousePos = GetGlobalMousePosition();
        var newHoveredCell = GridUtils.WorldToGrid(mousePos);

        // Only update if cell changed
        if (newHoveredCell != _hoveredCell)
        {
            _hoveredCell = newHoveredCell;
            UpdateGhostPosition();
        }

        if (Input.IsActionJustPressed("rotate_tile_reverse"))
            RotateGhost(reverse: true);
        else if (Input.IsActionJustPressed("rotate_tile"))
            RotateGhost();

        if (_ghostBuilding == null)
        {
            var building = Registry.GetBuilding(_currentBuildingId);
            if (building == null) return;
            var scene = building.Scene;

            _ghostBuilding = (BaseTile)scene.Instantiate();
            _ghostBuilding.SetGhostMode(true);
            _ghostBuilding.RotationDegrees = _currentRotation;
            _ghostBuilding.ZAsRelative = false;
            _ghostBuilding.ZIndex = (int)building.Layer;
            UpdateGhostPosition();
            AddChild(_ghostBuilding);
        }

        var canPlace = CanPlaceBuilding();
        _ghostBuilding.SetGhostMode(canPlace);

        // Left click to place
        if (Input.IsActionPressed("build_tile") && canPlace)
        {
            var building = Registry.GetBuilding(_currentBuildingId);
            if (building == null) return;

            var scene = building.Scene;
            var buildingInstance = (BaseTile)scene.Instantiate();
            buildingInstance.RotationDegrees = _currentRotation;
            buildingInstance.ZIndex = (int)building.Layer;
            buildingInstance.Position = _ghostBuilding.Position;
            AddChild(buildingInstance);

            Grid.OccupyArea(_hoveredCell, buildingInstance, building.Size, _currentRotation, building.Layer);

            if (building.BuildTime > 0f)
                buildingInstance.StartConstruction(building.BuildTime);
        }

        if (Input.IsActionPressed("destroy_tile") &&
            !Grid.IsAreaFree(_hoveredCell, Vector2I.One, 0f))
        {
            // Right click to destroy from current layer
            var building = Grid.GetBuildingAtCell(_hoveredCell);
            if (building == null) return;
            // Find all cells occupied by this building
            var buildingInfo = Grid.GetBuildingInfoAtCell(_hoveredCell, GridLayer.Building);
            if (buildingInfo == null) return;
            building.QueueFree();
            Grid.FreeArea(buildingInfo.Value.Position, buildingInfo.Value.Size, buildingInfo.Value.Rotation);
        }
    }

    private bool CanPlaceBuilding()
    {
        var buildingData = Registry.GetBuilding(_currentBuildingId);
        if (buildingData == null) return false;

        // Check if rotation is allowed
        if (!buildingData.IsRotationAllowed(_currentRotation))
            return false;

        // Check if area is free
        var rotatedSize = buildingData.GetRotatedSize(_currentRotation);
        return !Grid.IsAreaOccupied(_hoveredCell, rotatedSize, _currentRotation, GetBlockingLayers(buildingData.Layer));
    }

    private GridLayer[] GetBlockingLayers(GridLayer layer)
    {
        return layer switch
        {
            GridLayer.Ground => [GridLayer.Ground],
            GridLayer.Building => [GridLayer.Building],
            GridLayer.Decoration => [GridLayer.Decoration],
            _ => []
        };
    }
}

public static class GridManagerExtensions
{
    public static (Vector2I Position, Vector2I Size, float Rotation)? GetBuildingInfoAtCell(this GridManager grid,
        Vector2I cell, GridLayer layer)
    {
        if (grid.GetBuildingAtCell(cell, layer) is { } building)
        {
            // Find the top-left position of the building
            for (int x = 0; x < 100; x++) // Arbitrary max size
            {
                for (int y = 0; y < 100; y++)
                {
                    var checkCell = new Vector2I(cell.X - x, cell.Y - y);
                    if (grid.GetBuildingAtCell(checkCell, layer) == building)
                    {
                        // Found the top-left corner, now find the size
                        var size = Vector2I.One;
                        // Search right
                        while (grid.GetBuildingAtCell(new Vector2I(checkCell.X + size.X, checkCell.Y), layer) ==
                               building)
                            size.X++;
                        // Search down
                        while (grid.GetBuildingAtCell(new Vector2I(checkCell.X, checkCell.Y + size.Y), layer) ==
                               building)
                            size.Y++;

                        // Get rotation from the first cell
                        var rotation = 0f; // You'll need to store rotation in GridManager to make this work
                        return (checkCell, size, rotation);
                    }
                }
            }
        }

        return null;
    }
}