using System.Collections.Generic;
using System.Linq;
using EventsAppQuest.Application.Services;
using EventsAppQuest.Core;
using EventsAppQuest.Domain.Enums;
using EventsAppQuest.Domain.Models;

namespace EventsAppQuest.Infrastructure.Services
{
    /// <summary>
    /// Implementação mock do serviço de compatibilidade e validação da composição.
    /// Usa regras por tipo de item para decidir compatibilidade e ordenar slots preferidos.
    /// </summary>
    public class MockPlacementCompatibilityService : IPlacementCompatibilityService
    {
        private readonly List<ShowroomPlacementRule> _rules = new List<ShowroomPlacementRule>();

        public MockPlacementCompatibilityService()
        {
            BuildDefaultRules();
        }

        private void BuildDefaultRules()
        {
            _rules.Add(new ShowroomPlacementRule
            {
                ShowroomItemType = ShowroomItemType.Decoration,
                AllowedPlacementZones = new List<PlacementZoneType> { PlacementZoneType.Entrance, PlacementZoneType.Center, PlacementZoneType.StageArea, PlacementZoneType.WallArea },
                AllowedAnchorTypes = new List<SpatialAnchorType> { SpatialAnchorType.Freestanding, SpatialAnchorType.Wall, SpatialAnchorType.Floor },
                PreferredZone = PlacementZoneType.Center,
                PreferredSlotNames = new List<string> { "Área central", "Entrada principal", "Palco", "Parede de apoio" },
                MaxItemsPerSlot = 2
            });
            _rules.Add(new ShowroomPlacementRule
            {
                ShowroomItemType = ShowroomItemType.Buffet,
                AllowedPlacementZones = new List<PlacementZoneType> { PlacementZoneType.DiningArea, PlacementZoneType.LoungeArea, PlacementZoneType.Center },
                AllowedAnchorTypes = new List<SpatialAnchorType> { SpatialAnchorType.Tabletop, SpatialAnchorType.Floor },
                PreferredZone = PlacementZoneType.DiningArea,
                PreferredSlotNames = new List<string> { "Área de mesas", "Lounge", "Área central" },
                MaxItemsPerSlot = 2
            });
            _rules.Add(new ShowroomPlacementRule
            {
                ShowroomItemType = ShowroomItemType.Lighting,
                AllowedPlacementZones = new List<PlacementZoneType> { PlacementZoneType.Center, PlacementZoneType.StageArea, PlacementZoneType.Entrance },
                AllowedAnchorTypes = new List<SpatialAnchorType> { SpatialAnchorType.Ceiling, SpatialAnchorType.Floor, SpatialAnchorType.Freestanding },
                PreferredZone = PlacementZoneType.Center,
                PreferredSlotNames = new List<string> { "Área central", "Palco", "Entrada principal" },
                MaxItemsPerSlot = 3
            });
            _rules.Add(new ShowroomPlacementRule
            {
                ShowroomItemType = ShowroomItemType.Furniture,
                AllowedPlacementZones = new List<PlacementZoneType> { PlacementZoneType.DiningArea, PlacementZoneType.Center, PlacementZoneType.LoungeArea },
                AllowedAnchorTypes = new List<SpatialAnchorType> { SpatialAnchorType.Floor, SpatialAnchorType.Tabletop },
                PreferredZone = PlacementZoneType.DiningArea,
                PreferredSlotNames = new List<string> { "Área de mesas", "Área central", "Lounge" },
                MaxItemsPerSlot = 4
            });
            _rules.Add(new ShowroomPlacementRule
            {
                ShowroomItemType = ShowroomItemType.Stage,
                AllowedPlacementZones = new List<PlacementZoneType> { PlacementZoneType.StageArea },
                AllowedAnchorTypes = new List<SpatialAnchorType> { SpatialAnchorType.Floor, SpatialAnchorType.Wall },
                PreferredZone = PlacementZoneType.StageArea,
                PreferredSlotNames = new List<string> { "Palco", "Parede de apoio" },
                MaxItemsPerSlot = 2
            });
            _rules.Add(new ShowroomPlacementRule
            {
                ShowroomItemType = ShowroomItemType.Other,
                AllowedPlacementZones = new List<PlacementZoneType> { PlacementZoneType.StageArea, PlacementZoneType.WallArea, PlacementZoneType.Center, PlacementZoneType.Other },
                AllowedAnchorTypes = new List<SpatialAnchorType> { SpatialAnchorType.Wall, SpatialAnchorType.Floor, SpatialAnchorType.Freestanding },
                PreferredSlotNames = new List<string> { "Palco", "Parede de apoio", "Área central" },
                MaxItemsPerSlot = 2
            });
        }

        public bool CanItemGoInSlot(ShowroomItem item, SpaceLayoutSlot slot)
        {
            if (item == null || slot == null) return false;
            var rule = _rules.FirstOrDefault(r => r.ShowroomItemType == item.Type);
            if (rule != null)
            {
                if (rule.AllowedPlacementZones != null && rule.AllowedPlacementZones.Count > 0 && !rule.AllowedPlacementZones.Contains(slot.PlacementZone))
                    return false;
                if (rule.AllowedAnchorTypes != null && rule.AllowedAnchorTypes.Count > 0 && !rule.AllowedAnchorTypes.Contains(slot.AnchorType))
                    return false;
                return true;
            }
            return item.PlacementZone == slot.PlacementZone && item.SpatialAnchorType == slot.AnchorType;
        }

        public IReadOnlyList<SpaceLayoutSlot> GetCompatibleSlotsForItem(string spaceId, ShowroomItem item)
        {
            if (string.IsNullOrEmpty(spaceId) || item == null) return new List<SpaceLayoutSlot>();
            var layoutService = ServiceRegistry.Instance.Get<ISpaceLayoutService>();
            if (layoutService == null) return new List<SpaceLayoutSlot>();
            var allSlots = layoutService.GetBySpaceId(spaceId);
            var compatible = allSlots.Where(s => CanItemGoInSlot(item, s)).ToList();
            var rule = _rules.FirstOrDefault(r => r.ShowroomItemType == item.Type);
            if (rule != null && rule.PreferredSlotNames != null && rule.PreferredSlotNames.Count > 0)
            {
                compatible = compatible
                    .OrderBy(s => rule.PreferredSlotNames.IndexOf(s.Name) >= 0 ? rule.PreferredSlotNames.IndexOf(s.Name) : 999)
                    .ThenBy(s => rule.PreferredZone == s.PlacementZone ? 0 : 1)
                    .ThenBy(s => s.DisplayOrder)
                    .ToList();
            }
            else
            {
                compatible = compatible.OrderBy(s => s.DisplayOrder).ToList();
            }
            return compatible;
        }

        public CompositionValidationResult ValidateComposition(EventProject project)
        {
            var result = new CompositionValidationResult { Status = CompositionValidationStatus.Valid };
            if (project == null) return result;

            var showroomService = ServiceRegistry.Instance.Get<IShowroomService>();
            var layoutService = ServiceRegistry.Instance.Get<ISpaceLayoutService>();
            if (showroomService == null || layoutService == null) return result;

            var assignments = project.ItemSlotAssignments ?? new List<ItemSlotAssignment>();
            var itemToSlot = new Dictionary<string, string>();
            foreach (var a in assignments)
                if (!string.IsNullOrEmpty(a.ShowroomItemId))
                    itemToSlot[a.ShowroomItemId] = a.SlotId;

            var slotCounts = new Dictionary<string, int>();
            foreach (var slot in layoutService.GetBySpaceId(project.SpaceId ?? ""))
                slotCounts[slot.Id] = 0;
            foreach (var a in assignments)
                if (!string.IsNullOrEmpty(a.SlotId))
                    slotCounts[a.SlotId] = slotCounts.GetValueOrDefault(a.SlotId, 0) + 1;

            foreach (var itemId in project.ShowroomItemIds ?? new List<string>())
            {
                var item = showroomService.GetById(itemId);
                if (item == null) continue;
                if (!itemToSlot.TryGetValue(itemId, out var slotId) || string.IsNullOrEmpty(slotId))
                {
                    result.Inconsistencies.Add(new ValidationMessage
                    {
                        Code = "ItemSemSlot",
                        Message = $"Item \"{item.Name}\" não está alocado em nenhum slot.",
                        ItemId = itemId
                    });
                    continue;
                }
                var slot = layoutService.GetById(slotId);
                if (slot == null)
                {
                    result.Inconsistencies.Add(new ValidationMessage { Code = "SlotInvalido", Message = $"Slot inválido para item \"{item.Name}\".", ItemId = itemId, SlotId = slotId });
                    continue;
                }
                if (!CanItemGoInSlot(item, slot))
                {
                    result.Inconsistencies.Add(new ValidationMessage
                    {
                        Code = "SlotIncompativel",
                        Message = $"Item \"{item.Name}\" está em slot incompatível (\"{slot.Name}\") para o tipo.",
                        ItemId = itemId,
                        SlotId = slotId
                    });
                }
            }

            var ruleByType = _rules.ToDictionary(r => r.ShowroomItemType, r => r);
            foreach (var kv in slotCounts)
            {
                var slot = layoutService.GetById(kv.Key);
                if (slot == null) continue;
                var count = kv.Value;
                if (slot.CapacityHint > 0 && count > slot.CapacityHint)
                {
                    result.Warnings.Add(new ValidationMessage
                    {
                        Code = "ExcessoNoSlot",
                        Message = $"Slot \"{slot.Name}\" tem {count} itens (capacidade sugerida: {slot.CapacityHint}).",
                        SlotId = slot.Id
                    });
                }
                var itemsInSlot = assignments.Where(a => a.SlotId == kv.Key).ToList();
                var typesInSlot = itemsInSlot.Select(a => showroomService.GetById(a.ShowroomItemId)?.Type).Where(t => t.HasValue).Select(t => t.Value).ToList();
                foreach (var type in typesInSlot.Distinct())
                {
                    if (!ruleByType.TryGetValue(type, out var rule) || rule.MaxItemsPerSlot <= 0) continue;
                    var sameTypeCount = typesInSlot.Count(t => t == type);
                    if (sameTypeCount > rule.MaxItemsPerSlot)
                    {
                        result.Warnings.Add(new ValidationMessage
                        {
                            Code = "ExcessoTipoNoSlot",
                            Message = $"Slot \"{slot.Name}\": mais de {rule.MaxItemsPerSlot} itens do mesmo tipo ({sameTypeCount}).",
                            SlotId = slot.Id
                        });
                        break;
                    }
                }
            }

            if (result.Inconsistencies.Count > 0)
                result.Status = CompositionValidationStatus.Invalid;
            else if (result.Warnings.Count > 0)
                result.Status = CompositionValidationStatus.HasWarnings;

            return result;
        }
    }
}
