using Barotrauma.Items.Components;
using Barotrauma.Networking;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Barotrauma
{
    partial class Traitor
    {
        public class GoalFindItem : HumanoidGoal
        {
            private readonly TraitorMission.CharacterFilter filter;
            private readonly string identifier;
            private readonly bool preferNew;
            private readonly bool allowNew;
            private readonly bool allowExisting;
            private readonly HashSet<Identifier> allowedContainerIdentifiers = new HashSet<Identifier>();

            private ItemPrefab targetPrefab;
            private ItemPrefab containedPrefab;
            private Item targetContainer;
            private Item target;
            private HashSet<Item> existingItems = new HashSet<Item>();
            private string targetNameText;
            private string targetContainerNameText;
            private string targetHullNameText;
            private float percentage;
            private int spawnAmount = 1;

            private const string itemContainerId = "toolbox";

            public override IEnumerable<string> InfoTextKeys => base.InfoTextKeys.Concat(new string[] { "[identifier]", "[target]", "[targethullname]" });
            public override IEnumerable<string> InfoTextValues(Traitor traitor) => base.InfoTextValues(traitor).Concat(new string[] { targetNameText ?? "", targetContainerNameText ?? "", targetHullNameText ?? "" });

            public override bool IsCompleted => target != null && Traitors.Any(traitor => traitor.Character.HasItem(target));
            public override bool CanBeCompleted(ICollection<Traitor> traitors)
            {
                if (!base.CanBeCompleted(traitors))
                {
                    return false;
                }
                if (target == null)
                {
                    var targetPrefabCandidate = FindItemPrefab(identifier);
                    return targetPrefabCandidate != null && FindTargetContainer(traitors, targetPrefabCandidate) != null;
                }
                if (target.Removed)
                {
                    return false;
                }
                if (target.Submarine == null)
                {
                    if (!(target.ParentInventory?.Owner is Character))
                    {
                        return false;
                    }
                }
                else
                {
                    if (Traitors.All(traitor => target.Submarine.TeamID != traitor.Character.TeamID))
                    {
                        return false;
                    }
                }
                return true;
            }

            public override bool IsEnemy(Character character) => base.IsEnemy(character) || (target != null && target.FindParentInventory(inventory => inventory == character.Inventory) != null);

            protected ItemPrefab FindItemPrefab(string identifier)
            {
                return (ItemPrefab)MapEntityPrefab.List.FirstOrDefault(prefab => prefab is ItemPrefab && prefab.Identifier == identifier);
            }

            protected Item FindRandomContainer(ICollection<Traitor> traitors, ItemPrefab targetPrefabCandidate, bool includeNew, bool includeExisting)
            {
                List<Item> suitableItems = new List<Item>();
                foreach (Item item in Item.ItemList)
                {
                    if (item.HiddenInGame || item.NonInteractable || item.NonPlayerTeamInteractable) { continue; }
                    if (item.Submarine == null || traitors.All(traitor => item.Submarine.TeamID != traitor.Character.TeamID))
                    {
                        continue;
                    }
                    if (item.GetComponent<ItemContainer>() != null && allowedContainerIdentifiers.Contains(((MapEntity)item).Prefab.Identifier))
                    {
                        if ((includeNew && !item.OwnInventory.IsFull()) || (includeExisting && item.OwnInventory.FindItemByIdentifier(targetPrefabCandidate.Identifier) != null))
                        {
                            suitableItems.Add(item);
                        }
                    }
                }

                if (suitableItems.Count == 0) { return null; }
                return suitableItems[TraitorManager.RandomInt(suitableItems.Count)];
            }

            protected Item FindTargetContainer(ICollection<Traitor> traitors, ItemPrefab targetPrefabCandidate)
            {
                Item result = null;
                if (preferNew)
                {
                    result = FindRandomContainer(traitors, targetPrefabCandidate, true, false);
                }
                if (result == null)
                {
                    result = FindRandomContainer(traitors, targetPrefabCandidate, allowNew, allowExisting);
                }
                if (result == null)
                {
                    return null;
                }
                if (allowNew && !result.OwnInventory.IsFull())
                {
                    return result;
                }
                if (allowExisting && result.OwnInventory.FindItemByIdentifier(targetPrefabCandidate.Identifier) != null)
                {
                    return result;
                }
                return null;
            }

            public override bool Start(Traitor traitor)
            {
                if (!base.Start(traitor))
                {
                    return false;
                }
                if (targetPrefab != null)
                {
                    return true;
                }

                string targetPrefabTextId;

                if (percentage > 0f)
                {
                    spawnAmount = (int)Math.Floor(Character.CharacterList.FindAll(c => c.TeamID == traitor.Character.TeamID && c != traitor.Character && !c.IsDead && (filter == null || filter(c))).Count * percentage);
                }

                if (spawnAmount > 1 && allowNew)
                {
                    containedPrefab = FindItemPrefab(identifier);
                    targetPrefab = FindItemPrefab(itemContainerId);

                    if (containedPrefab == null || targetPrefab == null)
                    {
                        return false;
                    }

                    targetPrefabTextId = containedPrefab.GetItemNameTextId();
                }
                else
                {
                    spawnAmount = 1;
                    containedPrefab = null;
                    targetPrefab = FindItemPrefab(identifier);

                    if (targetPrefab == null)
                    {
                        return false;
                    }

                    targetPrefabTextId = targetPrefab.GetItemNameTextId();
                }

                targetNameText = targetPrefabTextId != null ? TextManager.FormatServerMessage(targetPrefabTextId) : targetPrefab.Name.Value;
                targetContainer = FindTargetContainer(Traitors, targetPrefab);
                if (targetContainer == null)
                {
                    targetPrefab = null;
                    targetContainer = null;
                    return false;
                }
                var containerPrefabTextId = targetContainer.Prefab.GetItemNameTextId();
                targetContainerNameText = containerPrefabTextId != null ? TextManager.FormatServerMessage(containerPrefabTextId) : targetContainer.Prefab.Name.Value;
                var targetHullTextId = targetContainer.CurrentHull?.Prefab.GetHullNameTextId();
                targetHullNameText = targetHullTextId != null ? TextManager.FormatServerMessage(targetHullTextId) : targetContainer?.CurrentHull?.DisplayName.Value ?? "";
                if (allowNew && !targetContainer.OwnInventory.IsFull())
                {
                    existingItems.Clear();
                    foreach (var item in targetContainer.OwnInventory.AllItems.Distinct())
                    {
                        existingItems.Add(item);
                    }
                    Entity.Spawner.AddItemToSpawnQueue(targetPrefab, targetContainer.OwnInventory, onSpawned: item =>
                    {
                        item.AddTag("traitormissionitem");
                    });
                    target = null;
                }
                else if (allowExisting)
                {
                    target = targetContainer.OwnInventory.FindItemByIdentifier(targetPrefab.Identifier);
                }
                else
                {
                    targetPrefab = null;
                    targetContainer = null;
                    return false;
                }
                return true;
            }

            public override void Update(float deltaTime)
            {
                base.Update(deltaTime);
                if (target == null)
                {
                    target = targetContainer.ContainedItems.FirstOrDefault(item => item.Prefab.Identifier == (containedPrefab != null ? itemContainerId : identifier) && !existingItems.Contains(item));
                    if (target != null)
                    {
                        if (containedPrefab != null)
                        {
                            for (int i = 0; i < spawnAmount; i++)
                            {
                                Entity.Spawner.AddItemToSpawnQueue(containedPrefab, target.OwnInventory);
                            }
                        }
                        existingItems.Clear();
                    }
                }
            }

            public GoalFindItem(TraitorMission.CharacterFilter filter, string identifier, bool preferNew, bool allowNew, bool allowExisting, float percentage, params Identifier[] allowedContainerIdentifiers)
            {
                this.filter = filter;
                this.identifier = identifier;
                this.preferNew = preferNew;
                this.allowNew = allowNew;
                this.allowExisting = allowExisting;
                this.percentage = percentage / 100f;
                this.allowedContainerIdentifiers.UnionWith(allowedContainerIdentifiers);
            }
        }
    }
}
