using DunGen;
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEngine;

namespace MuzikaGromche
{
    internal class PoweredLightsAnimators
    {
        private readonly record struct AnimatorPatch(string AnimatorContainerPath, RuntimeAnimatorController AnimatorController);

        private readonly record struct TilePatch(string TileName, AnimatorPatch[] Patches)
        {
            // We are specifically looking for cloned tiles, not the original prototypes.
            public readonly string TileCloneName = $"{TileName}(Clone)";
        }

        private readonly record struct AnimatorPatchDescriptor(string AnimatorContainerPath, string AnimatorControllerAssetPath)
        {
            public AnimatorPatch Load(AssetBundle assetBundle)
            {
                var animationController = assetBundle.LoadAsset<RuntimeAnimatorController>(AnimatorControllerAssetPath)
                    ?? throw new FileNotFoundException($"RuntimeAnimatorController not found: {AnimatorControllerAssetPath}", AnimatorControllerAssetPath);
                return new(AnimatorContainerPath, animationController);
            }
        }

        private readonly record struct TilePatchDescriptor(string[] TileNames, AnimatorPatchDescriptor[] Descriptors)
        {
            public TilePatchDescriptor(string TileName, AnimatorPatchDescriptor[] Descriptors)
                : this([TileName], Descriptors)
            {
            }

            public TilePatch[] Load(AssetBundle assetBundle)
            {
                var patches = Descriptors.Select(d => d.Load(assetBundle)).ToArray();
                return [.. TileNames.Select(tileName => new TilePatch(tileName, patches))];
            }
        }

        private static IDictionary<string, TilePatch> Patches = new Dictionary<string, TilePatch>();

        public static void Load()
        {
            const string BundleFileName = "muzikagromche_poweredlightsanimators";
            string bundlePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), BundleFileName);
            var assetBundle = AssetBundle.LoadFromFile(bundlePath)
                ?? throw new NullReferenceException("Failed to load bundle");

            const string BasePath = "Assets/LethalCompany/Mods/MuzikaGromche/AnimatorControllers/";

            const string PointLight4 = $"{BasePath}Point Light (4) (Patched).controller";
            const string MineshaftSpotlight = $"{BasePath}MineshaftSpotlight (Patched).controller";
            const string LightbulbsLine = $"{BasePath}lightbulbsLineMesh (Patched).controller";
            const string CeilingFan = $"{BasePath}CeilingFan (originally GameObject) (Patched).controller";


            TilePatchDescriptor[] descriptors =
            [
                // < v70
                new("ManorStartRoom", [
                    new("ManorStartRoom/Chandelier/PoweredLightTypeB (1)", PointLight4),
                    new("ManorStartRoom/Chandelier2/PoweredLightTypeB", PointLight4),
                ]),
                // v70+
                new("ManorStartRoomSmall", [
                    new("ManorStartRoomMesh/Chandelier/PoweredLightTypeB (1)", PointLight4),
                    new("ManorStartRoomMesh/Chandelier2/PoweredLightTypeB", PointLight4),
                ]),
                new("BirthdayRoomTile", [
                    new("Lights/MineshaftSpotlight", MineshaftSpotlight),
                ]),
                new("BathroomTileContainer", [
                    new("MineshaftSpotlight", MineshaftSpotlight),
                    new("LightbulbLine/lightbulbsLineMesh", LightbulbsLine),
                ]),
                new(["BedroomTile", "BedroomTileB"], [
                    new("CeilingFanAnimContainer", CeilingFan),
                    new("MineshaftSpotlight (1)", MineshaftSpotlight),
                ]),
            ];

            Patches = descriptors
                .SelectMany(d => d.Load(assetBundle))
                .ToDictionary(d => d.TileCloneName, d => d);
        }

        public static void Patch(Tile tile)
        {
            if (tile == null)
            {
                throw new ArgumentNullException(nameof(tile));
            }

            if (Patches.TryGetValue(tile.gameObject.name, out var tilePatch))
            {
                foreach (var patch in tilePatch.Patches)
                {
                    Transform animationContainerTransform = tile.gameObject.transform.Find(patch.AnimatorContainerPath);
                    if (animationContainerTransform == null)
                    {
#if DEBUG
                        throw new NullReferenceException($"{tilePatch.TileName}/{patch.AnimatorContainerPath} Animation Container not found");
#endif
#pragma warning disable CS0162 // Unreachable code detected
                        continue;
#pragma warning restore CS0162 // Unreachable code detected
                    }

                    var animator = animationContainerTransform.gameObject.GetComponent<Animator>();
                    if (animator == null)
                    {
#if DEBUG
                        throw new NullReferenceException($"{tilePatch.TileName}/{patch.AnimatorContainerPath} Animation Component not found");
#endif
#pragma warning disable CS0162 // Unreachable code detected
                        continue;
#pragma warning restore CS0162 // Unreachable code detected
                    }

                    animator.runtimeAnimatorController = patch.AnimatorController;
                    Debug.Log($"{nameof(MuzikaGromche)} {nameof(PoweredLightsAnimatorsPatch)} {tilePatch.TileName}/{patch.AnimatorContainerPath}: Replaced animator controller");
                }
            }
        }
    }

    [HarmonyPatch(typeof(Tile))]
    internal class PoweredLightsAnimatorsPatch
    {
        [HarmonyPatch("AddTriggerVolume")]
        [HarmonyPostfix]
        public static void OnAddTriggerVolume(Tile __instance)
        {
            PoweredLightsAnimators.Patch(__instance);
        }
    }
}
