using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Eft.Match;
using SPTarkov.Server.Core.Models.Eft.Profile;
using SPTarkov.Server.Core.Models.Enums;
using SPTarkov.Server.Core.Models.Spt.Config;
using SPTarkov.Server.Core.Models.Utils;
using SPTarkov.Server.Core.Servers;
using SPTarkov.Server.Core.Utils;
using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel;

namespace SPTarkov.Server.Core.Services;

[Injectable(InjectionType.Singleton)]
public class BtrDeliveryService(
    ISptLogger<BtrDeliveryService> logger,
    DatabaseService databaseService,
    RandomUtil randomUtil,
    TimeUtil timeUtil,
    SaveServer saveServer,
    MailSendService mailSendService,
    ConfigServer configServer,
    ServerLocalisationService serverLocalisationService
)
{
    protected readonly BtrDeliveryConfig _btrDeliveryConfig = configServer.GetConfig<BtrDeliveryConfig>();
    protected readonly TraderConfig _traderConfig = configServer.GetConfig<TraderConfig>();

    protected static readonly List<string> _transferTypes = ["btr", "transit"];

    /// <summary>
    ///     Check if player used BTR or transit item sending service and send items to player via mail if found
    /// </summary>
    /// <param name="sessionId"> Session ID </param>
    /// <param name="request"> End raid request from client </param>
    public void HandleItemTransferEvent(MongoId sessionId, EndLocalRaidRequestData request)
    {
        foreach (var transferType in _transferTypes)
        {
            var rootId = $"{Traders.BTR}_{transferType}";

            if (request.TransferItems is null)
            {
                continue;
            }

            // if rootId doesn't exist in TransferItems, skip
            if (!request.TransferItems.TryGetValue(rootId, out var itemsToSend))
            {
                continue;
            }

            // Filter out the btr container item from transferred items before delivering
            itemsToSend = itemsToSend?.Where(item => item.Id != Traders.BTR);
            if (itemsToSend is null || !itemsToSend.Any())
            {
                continue;
            }

            HandleTransferItemDelivery(sessionId, itemsToSend);
        }
    }

    protected void HandleTransferItemDelivery(MongoId sessionId, IEnumerable<Item> items)
    {
        var serverProfile = saveServer.GetProfile(sessionId);
        var pmcData = serverProfile.CharacterData.PmcData;

        // Remove any items that were returned by the item delivery, but also insured, from the player's insurance list
        // This is to stop items being duplicated by being returned from both item delivery and insurance
        var deliveredItemIds = items.Select(item => item.Id).ToHashSet();
        pmcData.InsuredItems = pmcData.InsuredItems.Where(insuredItem => !deliveredItemIds.Contains(insuredItem.ItemId.Value)).ToList();

        saveServer.GetProfile(sessionId).BtrDeliveryList ??= [];

        // Store delivery to send to player later in profile
        saveServer
            .GetProfile(sessionId)
            .BtrDeliveryList.Add(
                new BtrDelivery
                {
                    Id = new MongoId(),
                    ScheduledTime = (int)GetBTRDeliveryReturnTimestamp(),
                    Items = items.ToList(),
                }
            );
    }

    public void SendBTRDelivery(MongoId sessionId, IEnumerable<Item> items)
    {
        var dialogueTemplates = databaseService.GetTrader(Traders.BTR).Dialogue;
        if (dialogueTemplates is null)
        {
            logger.Error(serverLocalisationService.GetText("inraid-unable_to_deliver_item_no_trader_found", Traders.BTR));
            return;
        }

        if (!dialogueTemplates.TryGetValue("itemsDelivered", out var itemsDelivered))
        {
            logger.Error(serverLocalisationService.GetText("btr-unable_to_find_items_in_dialog_template", sessionId));

            return;
        }

        var messageId = randomUtil.GetArrayValue(itemsDelivered);
        var messageStoreTime = timeUtil.GetHoursAsSeconds(_traderConfig.Fence.BtrDeliveryExpireHours);

        // Send the items to the player
        mailSendService.SendLocalisedNpcMessageToPlayer(
            sessionId,
            Traders.BTR,
            MessageType.BtrItemsDelivery,
            messageId,
            items,
            messageStoreTime
        );
    }

    /// <summary>
    /// Remove a BTR delivery package from a profile using the package's ID.
    /// </summary>
    /// <param name="sessionId">The session ID of the profile to remove the package from.</param>
    /// <param name="delivery">The BTR delivery package to remove.</param>
    public void RemoveBTRDeliveryPackageFromProfile(MongoId sessionId, BtrDelivery delivery)
    {
        var profile = saveServer.GetProfile(sessionId);
        profile.BtrDeliveryList = profile.BtrDeliveryList.Where(package => package.Id != delivery.Id).ToList();

        if (logger.IsLogEnabled(LogLevel.Debug))
        {
            logger.Debug($"Removed processed BTR delivery package. Remaining packages: {profile.BtrDeliveryList.Count}");
        }
    }

    /// <summary>
    /// Get a timestamp of when items given to the BTR driver should be sent to player.
    /// </summary>
    /// <returns>Timestamp to return items to player in seconds</returns>
    protected double GetBTRDeliveryReturnTimestamp()
    {
        // If override in config is non-zero, use that
        if (_btrDeliveryConfig.ReturnTimeOverrideSeconds > 0)
        {
            if (logger.IsLogEnabled(LogLevel.Debug))
            {
                logger.Debug($"BTR delivery override used: returning in {_btrDeliveryConfig.ReturnTimeOverrideSeconds} seconds");
            }

            return timeUtil.GetTimeStamp() + _btrDeliveryConfig.ReturnTimeOverrideSeconds;
        }

        return timeUtil.GetTimeStamp();
    }
}
