using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;

using Mag.Shared;

using Decal.Adapter;
using Decal.Adapter.Wrappers;
using System.Diagnostics;
using YamlDotNet.Serialization;

namespace MosswartMassacre
{
    class MossyInventory : IDisposable
    {

        private string InventoryFileName
        {
            get
            {
                // 1) Character name
                var characterName = CoreManager.Current.CharacterFilter.Name;

                // 2) Plugin folder - handle hot reload scenarios
                string pluginFolder;
                if (!string.IsNullOrEmpty(PluginCore.AssemblyDirectory))
                {
                    pluginFolder = PluginCore.AssemblyDirectory;
                }
                else
                {
                    pluginFolder = Path.GetDirectoryName(
                        System.Reflection.Assembly
                          .GetExecutingAssembly()
                          .Location
                    );
                }

                // 3) Character-specific folder path
                var characterFolder = Path.Combine(pluginFolder, characterName);

                // 4) Ensure directory exists (can do it here, thread-safe for most single-user plugin cases)
                if (!Directory.Exists(characterFolder))
                    Directory.CreateDirectory(characterFolder);

                // 5) Return full path to the .json file inside the character folder
                return Path.Combine(characterFolder, $"{characterName}.json");
            }
        }

        public MossyInventory()
        {
            try
            {
                CoreManager.Current.CharacterFilter.LoginComplete += CharacterFilter_LoginComplete;
                CoreManager.Current.WorldFilter.CreateObject += WorldFilter_CreateObject;
                CoreManager.Current.WorldFilter.ChangeObject += WorldFilter_ChangeObject;
                CoreManager.Current.CharacterFilter.Logoff += CharacterFilter_Logoff;
                PluginCore.WriteToChat($"[INV] {InventoryFileName}");
                PluginCore.WriteToChat("Started MOSSY!");
            }
            catch (Exception ex)
            {
                PluginCore.WriteToChat($"[INV] {ex}");
            }
        }

        private bool disposed;
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposing)
        {
            if (disposed) return;
            if (disposing)
            {
                CoreManager.Current.CharacterFilter.LoginComplete -= CharacterFilter_LoginComplete;
                CoreManager.Current.WorldFilter.CreateObject -= WorldFilter_CreateObject;
                CoreManager.Current.WorldFilter.ChangeObject -= WorldFilter_ChangeObject;
                CoreManager.Current.CharacterFilter.Logoff -= CharacterFilter_Logoff;
            }
            disposed = true;
        }

        private bool loginComplete;
        private bool loggedInAndWaitingForIdData;
        private bool forceUploadWaitingForIdData;
        private readonly List<int> requestedIds = new List<int>();

        private void CharacterFilter_LoginComplete(object sender, EventArgs e)
        {
            try
            {
                loginComplete = true;
                
                // Defensive check - settings might not be initialized yet due to event handler order
                bool inventoryLogEnabled;
                try 
                {
                    inventoryLogEnabled = PluginSettings.Instance.InventoryLog;
                }
                catch (InvalidOperationException)
                {
                    PluginCore.WriteToChat("[INV] Settings not ready, skipping inventory check");
                    return;
                }
                
                if (!inventoryLogEnabled)
                    return;

                if (!File.Exists(InventoryFileName))
                {
                    PluginCore.WriteToChat("Requesting id information for all armor/weapon inventory. This will take a few minutes...");
                    foreach (WorldObject wo in CoreManager.Current.WorldFilter.GetInventory())
                    {
                        if (!wo.HasIdData && ObjectClassNeedsIdent(wo.ObjectClass, wo.Name))
                            CoreManager.Current.Actions.RequestId(wo.Id);
                    }
                    loggedInAndWaitingForIdData = true;
                }
                else
                {
                    DumpInventoryToFile(true);
                }
            }
            catch (Exception ex)
            {
                PluginCore.WriteToChat($"[INV] {ex}");
            }
        }

        private void WorldFilter_CreateObject(object sender, CreateObjectEventArgs e)
        {
            if (!loginComplete) return;
            
            try 
            {
                if (!PluginSettings.Instance.InventoryLog) return;
            }
            catch (InvalidOperationException)
            {
                return; // Settings not ready, skip silently
            }

            if (!e.New.HasIdData && ObjectClassNeedsIdent(e.New.ObjectClass, e.New.Name)
                && !requestedIds.Contains(e.New.Id)
                && e.New.Container == CoreManager.Current.CharacterFilter.Id)
            {
                requestedIds.Add(e.New.Id);
                CoreManager.Current.Actions.RequestId(e.New.Id);
            }
        }

        private void WorldFilter_ChangeObject(object sender, ChangeObjectEventArgs e)
        {
            if (!loginComplete) return;
            
            try 
            {
                if (!PluginSettings.Instance.InventoryLog) return;
            }
            catch (InvalidOperationException)
            {
                return; // Settings not ready, skip silently
            }

            if (loggedInAndWaitingForIdData || forceUploadWaitingForIdData)
            {
                bool allHaveId = true;
                foreach (WorldObject wo in CoreManager.Current.WorldFilter.GetInventory())
                {
                    if (!wo.HasIdData && ObjectClassNeedsIdent(wo.ObjectClass, wo.Name))
                    {
                        allHaveId = false;
                        break;
                    }
                }
                if (allHaveId)
                {
                    bool wasForceUpload = forceUploadWaitingForIdData;
                    loggedInAndWaitingForIdData = false;
                    forceUploadWaitingForIdData = false;
                    DumpInventoryToFile();
                    if (wasForceUpload)
                        PluginCore.WriteToChat("[INV] All items identified. Inventory upload completed.");
                    else
                        PluginCore.WriteToChat("Requesting id information for all armor/weapon inventory completed. Log file written.");
                }
            }
            else
            {
                if (!e.Changed.HasIdData && ObjectClassNeedsIdent(e.Changed.ObjectClass, e.Changed.Name)
                    && !requestedIds.Contains(e.Changed.Id)
                    && e.Changed.Container == CoreManager.Current.CharacterFilter.Id)
                {
                    requestedIds.Add(e.Changed.Id);
                    CoreManager.Current.Actions.RequestId(e.Changed.Id);
                }
            }            

        }

        private void CharacterFilter_Logoff(object sender, Decal.Adapter.Wrappers.LogoffEventArgs e)
        {
            try
            {
                try 
                {
                    if (!PluginSettings.Instance.InventoryLog) return;
                }
                catch (InvalidOperationException)
                {
                    return; // Settings not ready, skip silently
                }
                DumpInventoryToFile(true); // Request IDs if missing to ensure complete data
            }
            catch (Exception ex)
            {
                PluginCore.WriteToChat($"[INV] {ex}");
            }
        }

        private void DumpInventoryToFile(bool requestIdsIfMissing = false)
        {
            var previouslySaved = new List<MyWorldObject>();

            if (File.Exists(InventoryFileName))
            {
                try
                {
                    string oldJson = File.ReadAllText(InventoryFileName);
                    previouslySaved = JsonConvert.DeserializeObject<List<MyWorldObject>>(oldJson)
                                      ?? new List<MyWorldObject>();
                }
                catch (Exception)
                {
                   PluginCore.WriteToChat("Inventory file is corrupt.");
                }
            }

            var currentList = new List<MyWorldObject>();
            foreach (WorldObject wo in CoreManager.Current.WorldFilter.GetInventory())
            {
                // Check to see if we already have some information for this item
                foreach (var prev in previouslySaved)
                {
                    if (prev.Id == wo.Id && prev.ObjectClass == (int)wo.ObjectClass)
                    {
                        // If neither our past nor our current item HadIdData, but it should, lets request it
                        if (requestIdsIfMissing && !prev.HasIdData && !wo.HasIdData && ObjectClassNeedsIdent(wo.ObjectClass, wo.Name))
                        {
                            CoreManager.Current.Actions.RequestId(wo.Id);
                            currentList.Add(MyWorldObjectCreator.Create(wo));
                        }
                        else
                        {
                            // Add the WorldObject to the MyWorldObject data so we have up to date information
                            currentList.Add(MyWorldObjectCreator.Combine(prev, wo));
                        }

                        goto end;
                    }
                }

                if (requestIdsIfMissing && !wo.HasIdData && ObjectClassNeedsIdent(wo.ObjectClass, wo.Name))
                    CoreManager.Current.Actions.RequestId(wo.Id);

                currentList.Add(MyWorldObjectCreator.Create(wo));

                end: ;
            }

            var fi = new FileInfo(InventoryFileName);
            if (fi.Directory != null && !fi.Directory.Exists)
                fi.Directory.Create();

            string json = JsonConvert.SerializeObject(currentList, Formatting.Indented);
            File.WriteAllText(InventoryFileName, json);

            // Send full inventory via WebSocket
            if (PluginCore.WebSocketEnabled)
            {
                _ = WebSocket.SendFullInventoryAsync(currentList);
                PluginCore.WriteToChat("Inventory sent to MosswartOverlord");
            }
        }

        private bool ObjectClassNeedsIdent(ObjectClass oc, string name)
        {
            return oc == ObjectClass.Armor
                || oc == ObjectClass.Clothing
                || oc == ObjectClass.MeleeWeapon
                || oc == ObjectClass.MissileWeapon
                || oc == ObjectClass.WandStaffOrb
                || oc == ObjectClass.Jewelry
                || (oc == ObjectClass.Gem && !string.IsNullOrEmpty(name) && name.Contains("Aetheria"))
                || (oc == ObjectClass.Misc && !string.IsNullOrEmpty(name) && name.Contains("Essence"));
        }

        /// <summary>
        /// Forces a full inventory scan: requests ID data for all items that need it,
        /// waits for all identifications to complete, then sends the complete inventory.
        /// </summary>
        public void ForceInventoryUpload()
        {
            try
            {
                // Check if inventory logging is enabled
                try
                {
                    if (!PluginSettings.Instance.InventoryLog)
                    {
                        PluginCore.WriteToChat("[INV] Inventory logging is disabled");
                        return;
                    }
                }
                catch (InvalidOperationException)
                {
                    PluginCore.WriteToChat("[INV] Settings not ready");
                    return;
                }

                // Check if WebSocket is enabled
                if (!PluginCore.WebSocketEnabled)
                {
                    PluginCore.WriteToChat("[INV] WebSocket streaming is disabled");
                    return;
                }

                // Count items needing identification
                int needsId = 0;
                int totalItems = 0;
                foreach (WorldObject wo in CoreManager.Current.WorldFilter.GetInventory())
                {
                    totalItems++;
                    if (!wo.HasIdData && ObjectClassNeedsIdent(wo.ObjectClass, wo.Name))
                    {
                        CoreManager.Current.Actions.RequestId(wo.Id);
                        needsId++;
                    }
                }

                if (needsId > 0)
                {
                    // Wait for all IDs to come back before sending
                    forceUploadWaitingForIdData = true;
                    PluginCore.WriteToChat($"[INV] Scanning {totalItems} items, requesting ID for {needsId}. Waiting for identification...");
                }
                else
                {
                    // All items already identified, send immediately
                    PluginCore.WriteToChat($"[INV] All {totalItems} items already identified. Sending...");
                    DumpInventoryToFile();
                    PluginCore.WriteToChat("[INV] Inventory upload completed");
                }
            }
            catch (Exception ex)
            {
                PluginCore.WriteToChat($"[INV] Force upload failed: {ex.Message}");
            }
        }
    }
}
