// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License.

using System.Collections.Concurrent;
using Continuity.AuthServer.DB.Documents;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Wonderking.Game.Data;
using Wonderking.Game.Reader;

namespace Continuity.AuthServer.Services;

public class ItemObjectPoolService : IHostedService
{
    private readonly ConcurrentDictionary<uint, ItemObject> _itemObjectPool;
    private readonly ItemReader _itemReader;
    private readonly ILogger<ItemObjectPoolService> _logger;

    public ItemObjectPoolService(IConfiguration configuration, ILogger<ItemObjectPoolService> logger)
    {
        _logger = logger;
        _itemReader = new ItemReader(configuration.GetSection("Game").GetSection("Data").GetValue<string>("Path") ??
                                     string.Empty);

        _itemObjectPool = new ConcurrentDictionary<uint, ItemObject>();
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        var amountOfEntries = _itemReader.GetAmountOfEntries();
        Parallel.For(0, (int)amountOfEntries, i =>
        {
            var itemObject = _itemReader.GetEntry((uint)i);
            var result = _itemObjectPool.TryAdd(itemObject.ItemID, itemObject);
            if (!result)
            {
                throw new KeyNotFoundException($"Failed to add item {itemObject.ItemID} to the item object pool");
            }

            _logger.LogTrace("Item with {ID} has been added", itemObject.ItemID);
        });
        _logger.LogInformation("A total of {AmountOfEntries} items have been added to the item object pool",
            _itemObjectPool.Count);

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }

    public ItemObject GetItem(ushort itemId)
    {
        _ = _itemObjectPool.TryGetValue(itemId, out var itemObject);
        return itemObject;
    }

    public bool ContainsItem(ushort itemId)
    {
        return _itemObjectPool.ContainsKey(itemId);
    }

    public IQueryable<ItemObject> QueryItems()
    {
        return _itemObjectPool.AsReadOnly().Values.AsQueryable();
    }

    public InventoryItem GetBaseInventoryItem(ushort itemId, ushort count = 1, bool isWorn = false)
    {
        var item = GetItem(itemId);
        return new InventoryItem
        {
            ItemId = itemId,
            Count = count,
            Slot = (byte)item.SlotNo1,
            InventoryTab =
                item.ItemType switch
                {
                    1 => InventoryTab.WornCashEquipment,
                    2 => isWorn ? InventoryTab.WornEquipment : InventoryTab.Equipment,
                    3 => InventoryTab.Etc,
                    4 => isWorn ? InventoryTab.WornCashEquipment : InventoryTab.Cash,
                    5 => InventoryTab.Warehouse,
                    0 => InventoryTab.WornEquipment,
                    _ => InventoryTab.WornEquipment
                },
            Level = item.MinimumLevelRequirement,
            Rarity = 0,
            AddOption = 0,
            AddOption2 = 0,
            AddOption3 = 0,
            Option = 0,
            Option2 = 0,
            Option3 = 0
        };
    }
}