using System.Collections.Generic;
using System.Linq;
using Godot;
using static System.Int32;

namespace AceFieldNewHorizon.Scripts.System;

public enum RotationDirection
{
	Up,     // 0 degrees
	Right,  // 90 degrees
	Down,   // 180 degrees
	Left    // 270 degrees
}

public record BuildingData(
	PackedScene Scene,
	Dictionary<string, int> Cost,
	int Durability,
	GridLayer Layer,
	float BuildTime,
	Vector2I Size = default,
	RotationDirection[] AllowedRotations = null
)
{
	public Vector2I Size { get; } = Size == default ? Vector2I.One : Size;
	public RotationDirection[] AllowedRotations { get; } = 
		AllowedRotations ?? [RotationDirection.Up, RotationDirection.Right, RotationDirection.Down, RotationDirection.Left
		];

	public bool IsRotationAllowed(float degrees)
	{
		var direction = (RotationDirection)((Mathf.RoundToInt(degrees / 90f) % 4 + 4) % 4);
		return AllowedRotations.Contains(direction);
	}

	public Vector2I GetRotatedSize(float degrees)
	{
		return (Mathf.RoundToInt(degrees / 90f) % 2) == 0 ? Size : new Vector2I(Size.Y, Size.X);
	}
}

public class BuildingRegistry
{
	private Dictionary<string, BuildingData> _registry = new();

	

	public BuildingRegistry(string jsonPath)
	{
		LoadFromJson(jsonPath);
	}

	public void LoadFromJson(string path)
	{
		var file = FileAccess.Open(path, FileAccess.ModeFlags.Read);
		if (file == null)
		{
			GD.PrintErr($"[BuildingRegistry] Failed to open {path}");
			return;
		}

		var text = file.GetAsText();
		file.Close();

		var json = new Json();
		var error = json.Parse(text);
		if (error != Error.Ok)
		{
			GD.PrintErr($"[BuildingRegistry] Failed to parse JSON: {json.GetErrorMessage()}");
			return;
		}

		var dict = json.Data.AsGodotDictionary();

		foreach (string key in dict.Keys)
		{
			// Each entry is a Dictionary with keys: "scene", "cost", "durability", "buildTime", "size", "allowedRotations"
			var buildingDict = dict[key].AsGodotDictionary();

			// Parse scene
			var scenePath = buildingDict.ContainsKey("scene") ? buildingDict["scene"].AsString() : null;
			if (string.IsNullOrEmpty(scenePath))
			{
				GD.PrintErr($"[BuildingRegistry] No scene path for '{key}'");
				continue;
			}

			var scene = GD.Load<PackedScene>(scenePath);
			if (scene == null)
			{
				GD.PrintErr($"[BuildingRegistry] Failed to load scene for '{key}' at {scenePath}");
				continue;
			}

			// Parse cost
			Dictionary<string, int> cost = new();
			if (buildingDict.TryGetValue("cost", out var value))
			{
				var costDict = value.AsGodotDictionary();
				foreach (string mat in costDict.Keys)
				{
					int val;
					var obj = costDict[mat];
					switch (obj.VariantType)
					{
						case Variant.Type.PackedInt64Array:
							val = (int)obj.AsInt64();
							break;
						case Variant.Type.PackedInt32Array:
							val = obj.AsInt32();
							break;
						case Variant.Type.Float:
							val = (int)obj.AsDouble();
							break;
						default:
							if (!TryParse(obj.ToString(), out val))
								GD.PrintErr($"[BuildingRegistry] Failed to parse cost for '{key}': {obj.ToString()}");
							break;
					}
					cost[mat] = val;
				}
			}

			// Parse durability
			var durability = 0;
			if (buildingDict.TryGetValue("durability", out var dObj))
			{
				if (dObj.VariantType == Variant.Type.PackedInt64Array)
					durability = (int)dObj.AsInt64();
				else if (dObj.VariantType == Variant.Type.PackedInt32Array)
					durability = dObj.AsInt32();
				else
					TryParse(dObj.ToString(), out durability);
			}

			// Parse buildTime
			var buildTime = 0f;
			if (buildingDict.TryGetValue("buildTime", out var bObj))
			{
				switch (bObj.VariantType)
				{
					case Variant.Type.PackedFloat32Array or Variant.Type.PackedFloat64Array:
						buildTime = (float)bObj.AsDouble();
						break;
					case Variant.Type.PackedInt64Array:
						buildTime = bObj.AsInt64();
						break;
					case Variant.Type.PackedInt32Array:
						buildTime = bObj.AsInt32();
						break;
					default:
						float.TryParse(bObj.ToString(), out buildTime);
						break;
				}
			}

			// Parse size
			var size = Vector2I.One;
			if (buildingDict.TryGetValue("size", out var sObj))
			{
				var sizeArray = sObj.AsGodotArray();
				if (sizeArray.Count == 2)
				{
					size = new Vector2I((int)sizeArray[0].AsInt64(), (int)sizeArray[1].AsInt64());
				}
			}

			// Parse allowedRotations
			RotationDirection[] allowedRotations = null;
			if (buildingDict.TryGetValue("allowedRotations", out var arObj))
			{
				var arArray = arObj.AsGodotArray();
				allowedRotations = new RotationDirection[arArray.Count];
				for (var i = 0; i < arArray.Count; i++)
				{
					allowedRotations[i] = (RotationDirection)arArray[i].AsInt32();
				}
			}

			var layer = GridLayer.Building;
			if (buildingDict.TryGetValue("layer", out var lObj))
			{
				layer = (GridLayer)lObj.AsInt32();
			}

			var buildingData = new BuildingData(scene, cost, durability, layer, buildTime, size, allowedRotations);
			_registry[key] = buildingData;
			GD.Print($"[BuildingRegistry] Loaded building '{key}' from {scenePath}");
		}

		GD.Print($"[BuildingRegistry] Loaded {_registry.Count} buildings");
	}

	public BuildingData GetBuilding(string id)
	{
		_registry.TryGetValue(id, out var data);
		return data;
	}
}
