using System.Diagnostics.CodeAnalysis;
using Content.Server.GameObjects.Components.Conveyor;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.Construction;
using Content.Shared.GameObjects.Components.Recycling;
using Content.Shared.Physics;
using Robust.Server.GameObjects;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.GameObjects.Components.Map;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;

namespace Content.Server.GameObjects.Components.Recycling
{
    // TODO: Add sound and safe beep
    [RegisterComponent]
    public class RecyclerComponent : Component, ICollideBehavior
    {
#pragma warning disable 649
        [Dependency] private readonly IEntityManager _entityManager = default!;
#pragma warning restore 649

        public override string Name => "Recycler";

        /// <summary>
        ///     Whether or not sentient beings will be recycled
        /// </summary>
        [ViewVariables]
        private bool _safe;

        /// <summary>
        ///     The percentage of material that will be recovered
        /// </summary>
        [ViewVariables]
        private int _efficiency; // TODO

        private bool Powered =>
            !Owner.TryGetComponent(out PowerReceiverComponent receiver) ||
            receiver.Powered;

        private void Bloodstain()
        {
            if (Owner.TryGetComponent(out AppearanceComponent appearance))
            {
                appearance.SetData(RecyclerVisuals.Bloody, true);
            }
        }

        private void Clean()
        {
            if (Owner.TryGetComponent(out AppearanceComponent appearance))
            {
                appearance.SetData(RecyclerVisuals.Bloody, false);
            }
        }

        private bool CanGib(IEntity entity)
        {
            return entity.HasComponent<SpeciesComponent>() &&
                   !_safe &&
                   Powered;
        }

        private bool CanRecycle(IEntity entity, [MaybeNullWhen(false)] out ConstructionPrototype prototype)
        {
            prototype = null;

            var constructionSystem = EntitySystem.Get<ConstructionSystem>();
            var entityId = entity.MetaData.EntityPrototype?.ID;

            if (entityId == null ||
                !constructionSystem.CraftRecipes.TryGetValue(entityId, out prototype))
            {
                return false;
            }

            return Powered;
        }

        private void Recycle(IEntity entity)
        {
            // TODO: Prevent collision with recycled items
            if (CanGib(entity))
            {
                entity.Delete(); // TODO: Gib
                Bloodstain();
                return;
            }

            if (!CanRecycle(entity, out var prototype))
            {
                return;
            }

            var constructionSystem = EntitySystem.Get<ConstructionSystem>();
            var recyclerPosition = Owner.Transform.MapPosition;
            foreach (var stage in prototype.Stages)
            {
                if (!(stage.Forward is ConstructionStepMaterial step))
                {
                    continue;
                }

                constructionSystem.SpawnIngredient(recyclerPosition, step);
            }

            entity.Delete();
        }

        private bool CanRun()
        {
            if (Owner.TryGetComponent(out PowerReceiverComponent receiver) &&
                !receiver.Powered)
            {
                return false;
            }

            if (Owner.HasComponent<ItemComponent>())
            {
                return false;
            }

            return true;
        }

        private bool CanMove(IEntity entity)
        {
            if (entity == Owner)
            {
                return false;
            }

            if (!entity.TryGetComponent(out ICollidableComponent collidable) ||
                collidable.Anchored)
            {
                return false;
            }

            if (entity.HasComponent<ConveyorComponent>())
            {
                return false;
            }

            if (entity.HasComponent<IMapGridComponent>())
            {
                return false;
            }

            if (ContainerHelpers.IsInContainer(entity))
            {
                return false;
            }

            return true;
        }

        public void Update(float frameTime)
        {
            if (!CanRun())
            {
                return;
            }

            var intersecting = _entityManager.GetEntitiesIntersecting(Owner, true);
            var direction = Vector2.UnitX;

            foreach (var entity in intersecting)
            {
                if (!CanMove(entity))
                {
                    continue;
                }

                if (entity.TryGetComponent(out ICollidableComponent collidable))
                {
                    var controller = collidable.EnsureController<ConveyedController>();
                    controller.Move(direction, frameTime);
                }
            }
        }

        public override void ExposeData(ObjectSerializer serializer)
        {
            base.ExposeData(serializer);

            serializer.DataField(ref _safe, "safe", true);
            serializer.DataField(ref _efficiency, "efficiency", 25);
        }

        void ICollideBehavior.CollideWith(IEntity collidedWith)
        {
            Recycle(collidedWith);
        }
    }
}
