using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using Duckov.Economy;

namespace TradingCardMod
{
    /// <summary>
    /// Helper class for adding disassembly formulas to custom items.
    /// Uses reflection to modify the game's DecomposeDatabase at runtime.
    /// Based on the approach from AdditionalCollectibles mod.
    /// </summary>
    public static class DisassemblyHelper
    {
        // Common material IDs from the game
        public static readonly int Metal01 = 91043;
        public static readonly int Metal02 = 91044;
        public static readonly int Metal03 = 91045;
        public static readonly int Plastic01 = 91046;
        public static readonly int Plastic02 = 91047;
        public static readonly int Plastic03 = 91048;

        // Track added formulas for cleanup
        private static readonly List<int> _addedItemIds = new List<int>();

        // Cached reflection info for ItemEntry type
        private static Type? _itemEntryType;
        private static FieldInfo? _itemEntryIdField;
        private static FieldInfo? _itemEntryAmountField;

        /// <summary>
        /// Initializes the ItemEntry type info via reflection.
        /// Gets the type from Cost.items field to find the correct ItemEntry type.
        /// </summary>
        private static bool InitializeItemEntryType()
        {
            if (_itemEntryType != null)
                return true;

            try
            {
                // Get ItemEntry type from Cost.items field (more reliable than searching by name)
                FieldInfo? itemsField = typeof(Cost).GetField("items", BindingFlags.Instance | BindingFlags.Public);
                if (itemsField == null)
                {
                    Debug.LogError("[TradingCardMod] Could not find Cost.items field!");
                    return false;
                }

                // items is ItemEntry[], so get the element type
                Type itemsType = itemsField.FieldType;
                if (!itemsType.IsArray)
                {
                    Debug.LogError("[TradingCardMod] Cost.items is not an array!");
                    return false;
                }

                _itemEntryType = itemsType.GetElementType();
                if (_itemEntryType == null)
                {
                    Debug.LogError("[TradingCardMod] Could not get ItemEntry element type!");
                    return false;
                }

                Debug.Log($"[TradingCardMod] Found ItemEntry type: {_itemEntryType.FullName}");

                _itemEntryIdField = _itemEntryType.GetField("id", BindingFlags.Instance | BindingFlags.Public);
                _itemEntryAmountField = _itemEntryType.GetField("amount", BindingFlags.Instance | BindingFlags.Public);

                if (_itemEntryIdField == null || _itemEntryAmountField == null)
                {
                    // Try non-public fields
                    _itemEntryIdField = _itemEntryType.GetField("id", BindingFlags.Instance | BindingFlags.NonPublic);
                    _itemEntryAmountField = _itemEntryType.GetField("amount", BindingFlags.Instance | BindingFlags.NonPublic);
                }

                if (_itemEntryIdField == null || _itemEntryAmountField == null)
                {
                    // Log available fields for debugging
                    var fields = _itemEntryType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                    Debug.LogError($"[TradingCardMod] Could not find ItemEntry id/amount fields! Available: {string.Join(", ", fields.Select(f => f.Name))}");
                    return false;
                }

                Debug.Log($"[TradingCardMod] ItemEntry fields: id={_itemEntryIdField.Name}, amount={_itemEntryAmountField.Name}");
                return true;
            }
            catch (Exception ex)
            {
                Debug.LogError($"[TradingCardMod] Error initializing ItemEntry type: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// Creates an ItemEntry array using reflection.
        /// </summary>
        private static Array? CreateItemEntryArray((int id, long amount)[] items)
        {
            if (!InitializeItemEntryType() || _itemEntryType == null)
                return null;

            Array array = Array.CreateInstance(_itemEntryType, items.Length);
            for (int i = 0; i < items.Length; i++)
            {
                object entry = Activator.CreateInstance(_itemEntryType)!;
                _itemEntryIdField!.SetValue(entry, items[i].id);
                _itemEntryAmountField!.SetValue(entry, items[i].amount);
                array.SetValue(entry, i);
            }
            return array;
        }

        /// <summary>
        /// Adds a disassembly formula for an item.
        /// </summary>
        /// <param name="itemId">The TypeID of the item that can be disassembled</param>
        /// <param name="money">Money reward when disassembled (0 for none)</param>
        /// <param name="resultItems">Array of (itemId, amount) tuples for materials received</param>
        /// <returns>True if formula was added successfully</returns>
        public static bool AddFormula(int itemId, long money, params (int id, long amount)[] resultItems)
        {
            try
            {
                DecomposeDatabase instance = DecomposeDatabase.Instance;
                if (instance == null)
                {
                    Debug.LogError("[TradingCardMod] DecomposeDatabase.Instance is null!");
                    return false;
                }

                // Get the private entries field via reflection
                FieldInfo? entriesField = typeof(DecomposeDatabase).GetField("entries",
                    BindingFlags.Instance | BindingFlags.NonPublic);
                if (entriesField == null)
                {
                    Debug.LogError("[TradingCardMod] Could not find 'entries' field on DecomposeDatabase!");
                    return false;
                }

                DecomposeFormula[] currentEntries = (DecomposeFormula[])entriesField.GetValue(instance);
                List<DecomposeFormula> formulaList = new List<DecomposeFormula>(currentEntries);

                // Check if formula already exists for this item
                foreach (DecomposeFormula existing in formulaList)
                {
                    if (existing.item == itemId)
                    {
                        Debug.LogWarning($"[TradingCardMod] Disassembly formula already exists for item {itemId}, skipping");
                        return false;
                    }
                }

                // Create new formula
                DecomposeFormula newFormula = new DecomposeFormula
                {
                    item = itemId,
                    valid = true
                };

                // Create cost (result items)
                // Cost is a struct, so we need to box it for reflection to work
                Cost result = new Cost
                {
                    money = money
                };

                // Build item entries array using reflection
                if (resultItems.Length > 0)
                {
                    Array? itemEntries = CreateItemEntryArray(resultItems);
                    if (itemEntries == null)
                    {
                        Debug.LogError("[TradingCardMod] Failed to create ItemEntry array!");
                        return false;
                    }

                    // Set items field on Cost using reflection (it expects Cost+ItemEntry[])
                    // Must box the struct first, modify, then unbox
                    FieldInfo? itemsField = typeof(Cost).GetField("items", BindingFlags.Instance | BindingFlags.Public);
                    if (itemsField != null)
                    {
                        object boxedResult = result;  // Box the struct
                        itemsField.SetValue(boxedResult, itemEntries);
                        result = (Cost)boxedResult;   // Unbox back
                        Debug.Log($"[TradingCardMod] Set {itemEntries.Length} items on Cost for item {itemId}");
                    }
                }

                newFormula.result = result;

                // Add to list and write back
                formulaList.Add(newFormula);
                entriesField.SetValue(instance, formulaList.ToArray());

                // Track for cleanup
                if (!_addedItemIds.Contains(itemId))
                {
                    _addedItemIds.Add(itemId);
                }

                // Rebuild internal dictionary for lookup performance
                MethodInfo? rebuildMethod = typeof(DecomposeDatabase).GetMethod("RebuildDictionary",
                    BindingFlags.Instance | BindingFlags.NonPublic);
                rebuildMethod?.Invoke(instance, null);

                Debug.Log($"[TradingCardMod] Added disassembly formula for item {itemId}");
                return true;
            }
            catch (Exception ex)
            {
                Debug.LogError($"[TradingCardMod] Error adding disassembly formula: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// Adds a simple money-only disassembly formula.
        /// </summary>
        /// <param name="itemId">The TypeID of the item</param>
        /// <param name="money">Money reward when disassembled</param>
        /// <returns>True if formula was added successfully</returns>
        public static bool AddMoneyOnlyFormula(int itemId, long money)
        {
            return AddFormula(itemId, money);
        }

        /// <summary>
        /// Removes all disassembly formulas added by this mod.
        /// Should be called during mod cleanup/unload.
        /// </summary>
        public static void Cleanup()
        {
            try
            {
                DecomposeDatabase instance = DecomposeDatabase.Instance;
                if (instance == null)
                {
                    Debug.LogWarning("[TradingCardMod] DecomposeDatabase.Instance is null during cleanup");
                    _addedItemIds.Clear();
                    return;
                }

                FieldInfo entriesField = typeof(DecomposeDatabase).GetField("entries",
                    BindingFlags.Instance | BindingFlags.NonPublic);
                if (entriesField == null)
                {
                    Debug.LogWarning("[TradingCardMod] Could not find 'entries' field during cleanup");
                    _addedItemIds.Clear();
                    return;
                }

                DecomposeFormula[] currentEntries = (DecomposeFormula[])entriesField.GetValue(instance);
                List<DecomposeFormula> formulaList = new List<DecomposeFormula>(currentEntries);

                int removedCount = 0;
                for (int i = formulaList.Count - 1; i >= 0; i--)
                {
                    if (_addedItemIds.Contains(formulaList[i].item))
                    {
                        formulaList.RemoveAt(i);
                        removedCount++;
                    }
                }

                entriesField.SetValue(instance, formulaList.ToArray());

                // Rebuild dictionary
                MethodInfo rebuildMethod = typeof(DecomposeDatabase).GetMethod("RebuildDictionary",
                    BindingFlags.Instance | BindingFlags.NonPublic);
                rebuildMethod?.Invoke(instance, null);

                Debug.Log($"[TradingCardMod] Removed {removedCount} disassembly formulas");
                _addedItemIds.Clear();
            }
            catch (Exception ex)
            {
                Debug.LogError($"[TradingCardMod] Error during disassembly cleanup: {ex.Message}");
                _addedItemIds.Clear();
            }
        }

        /// <summary>
        /// Gets the count of disassembly formulas added by this mod.
        /// </summary>
        public static int GetAddedFormulaCount()
        {
            return _addedItemIds.Count;
        }
    }
}
