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

namespace EventsAppQuest.Core
{
    /// <summary>
    /// Estado de sessao do app em memoria: selecoes atuais e projeto em andamento.
    /// </summary>
    public class AppSession
    {
        private EventCategory _selectedCategory;
        private Space _selectedSpace;
        private EventTemplate _selectedTemplate;
        private readonly List<string> _selectedSupplierIds = new List<string>();
        private readonly List<ProposalVersionSnapshot> _savedSnapshots = new List<ProposalVersionSnapshot>();
        private EventProject _currentProject;

        /// <summary>Categoria de evento selecionada (ou null).</summary>
        public EventCategory SelectedCategory
        {
            get => _selectedCategory;
            set
            {
                _selectedCategory = value;
                OnSessionChanged?.Invoke();
            }
        }

        /// <summary>Espaco selecionado (ou null).</summary>
        public Space SelectedSpace
        {
            get => _selectedSpace;
            set
            {
                _selectedSpace = value;
                OnSessionChanged?.Invoke();
            }
        }

        /// <summary>Template de evento selecionado (ou null).</summary>
        public EventTemplate SelectedTemplate
        {
            get => _selectedTemplate;
            set
            {
                _selectedTemplate = value;
                OnSessionChanged?.Invoke();
            }
        }

        /// <summary>Ids dos fornecedores selecionados ou visualizados.</summary>
        public IReadOnlyList<string> SelectedSupplierIds => _selectedSupplierIds;

        /// <summary>Projeto de evento atual em memoria (ou null se nao iniciado).</summary>
        public EventProject CurrentProject
        {
            get => _currentProject;
            private set
            {
                _currentProject = value;
                OnSessionChanged?.Invoke();
            }
        }

        /// <summary>Id da categoria selecionada (conveniencia).</summary>
        public string SelectedCategoryId => _selectedCategory?.Id;

        /// <summary>Id do espaco selecionado (conveniencia).</summary>
        public string SelectedSpaceId => _selectedSpace?.Id;

        /// <summary>Id do template selecionado (conveniencia).</summary>
        public string SelectedTemplateId => _selectedTemplate?.Id;

        /// <summary>Snapshots de versões da proposta salvos em memória (para comparação).</summary>
        public IReadOnlyList<ProposalVersionSnapshot> SavedSnapshots => _savedSnapshots;

        /// <summary>Disparado quando categoria, espaco, template, fornecedores ou projeto mudam.</summary>
        public event Action OnSessionChanged;

        /// <summary>Adiciona ou remove um fornecedor da selecao (toggle).</summary>
        public void ToggleSupplierSelection(string supplierId)
        {
            if (string.IsNullOrEmpty(supplierId)) return;
            var idx = _selectedSupplierIds.IndexOf(supplierId);
            if (idx >= 0)
                _selectedSupplierIds.RemoveAt(idx);
            else
                _selectedSupplierIds.Add(supplierId);
            OnSessionChanged?.Invoke();
        }

        /// <summary>Limpa a selecao de fornecedores.</summary>
        public void ClearSupplierSelection()
        {
            if (_selectedSupplierIds.Count == 0) return;
            _selectedSupplierIds.Clear();
            OnSessionChanged?.Invoke();
        }

        /// <summary>Cria um EventProject em memoria com a selecao atual e define como projeto atual.</summary>
        /// <param name="projectName">Nome do projeto (ex.: "Meu evento").</param>
        /// <returns>O projeto criado ou null se categoria ou espaco nao estiverem selecionados.</returns>
        public EventProject StartProject(string projectName)
        {
            if (_selectedCategory == null || _selectedSpace == null)
                return null;

            var project = new EventProject
            {
                Id = Guid.NewGuid().ToString(),
                ClientId = "client-mock",
                Name = string.IsNullOrEmpty(projectName) ? "Novo projeto" : projectName,
                SpaceId = _selectedSpace.Id,
                EventCategoryId = _selectedCategory.Id,
                EventTemplateId = _selectedTemplate?.Id,
                SupplierIds = new List<string>(_selectedSupplierIds),
                ShowroomItemIds = new List<string>(),
                Status = EventProjectStatus.Draft,
                PlannedDate = null
            };

            var showroomService = ServiceRegistry.Instance.Get<IShowroomService>();
            if (showroomService != null && !string.IsNullOrEmpty(project.EventTemplateId))
            {
                var defaultItems = showroomService.GetByTemplateId(project.EventTemplateId);
                if (defaultItems != null)
                    foreach (var item in defaultItems)
                        if (item.IsIncludedByDefault)
                            project.ShowroomItemIds.Add(item.Id);
            }

            CurrentProject = project;
            MapShowroomItemsToSlots(project, replaceAll: true);
            RevalidateCompositionIfSafe();
            EnsureInitialProposal();
            return project;
        }

        /// <summary>Adiciona um item ao showroom do projeto atual.</summary>
        public void AddShowroomItem(string itemId)
        {
            if (_currentProject == null || string.IsNullOrEmpty(itemId)) return;
            if (_currentProject.ShowroomItemIds.Contains(itemId)) return;
            _currentProject.ShowroomItemIds.Add(itemId);
            MapShowroomItemsToSlots(_currentProject, replaceAll: false);
            SyncActiveProposalFromProject();
            OnSessionChanged?.Invoke();
        }

        /// <summary>Remove um item do showroom do projeto atual.</summary>
        public void RemoveShowroomItem(string itemId)
        {
            if (_currentProject == null || string.IsNullOrEmpty(itemId)) return;
            if (_currentProject.ShowroomItemIds.Remove(itemId))
            {
                _currentProject.ItemSlotAssignments?.RemoveAll(a => a.ShowroomItemId == itemId);
                SyncActiveProposalFromProject();
                OnSessionChanged?.Invoke();
            }
        }

        /// <summary>Associa um item do showroom a um slot do layout (remapeamento manual).</summary>
        public void AssignItemToSlot(string showroomItemId, string slotId)
        {
            if (_currentProject == null || string.IsNullOrEmpty(showroomItemId)) return;
            if (!_currentProject.ShowroomItemIds.Contains(showroomItemId)) return;
            if (_currentProject.ItemSlotAssignments == null)
                _currentProject.ItemSlotAssignments = new List<ItemSlotAssignment>();
            var existing = _currentProject.ItemSlotAssignments.Find(a => a.ShowroomItemId == showroomItemId);
            if (existing != null)
                existing.SlotId = slotId ?? string.Empty;
            else
                _currentProject.ItemSlotAssignments.Add(new ItemSlotAssignment { ShowroomItemId = showroomItemId, SlotId = slotId ?? string.Empty });
            SyncActiveProposalFromProject();
            OnSessionChanged?.Invoke();
        }

        /// <summary>Realoca todos os itens do showroom para slots compatíveis (mapeamento automático).</summary>
        public void ReassignLayout()
        {
            if (_currentProject == null) return;
            MapShowroomItemsToSlots(_currentProject, replaceAll: true);
            SyncActiveProposalFromProject();
            OnSessionChanged?.Invoke();
        }

        /// <summary>Valida a composição da proposta atual e armazena o resultado no projeto.</summary>
        public void ValidateComposition()
        {
            if (_currentProject == null) return;
            var service = ServiceRegistry.Instance.Get<IPlacementCompatibilityService>();
            if (service == null) return;
            _currentProject.LastCompositionValidation = service.ValidateComposition(_currentProject);
            SyncActiveProposalFromProject();
            OnSessionChanged?.Invoke();
        }

        /// <summary>Aplica um preset de layout ao projeto atual (substitui itens e mapeamento item → slot).</summary>
        public void ApplyPreset(string presetId)
        {
            if (_currentProject == null || string.IsNullOrEmpty(presetId)) return;
            var presetService = ServiceRegistry.Instance.Get<ILayoutPresetService>();
            var layoutService = ServiceRegistry.Instance.Get<ISpaceLayoutService>();
            var compatibilityService = ServiceRegistry.Instance.Get<IPlacementCompatibilityService>();
            if (presetService == null || layoutService == null) return;

            var preset = presetService.GetById(presetId);
            var items = presetService.GetItemsByPresetId(presetId);
            if (preset == null || items == null || items.Count == 0) return;

            _currentProject.ShowroomItemIds = items.Select(i => i.ShowroomItemId).Distinct().ToList();
            _currentProject.ItemSlotAssignments = new List<ItemSlotAssignment>();
            var slots = layoutService.GetBySpaceId(_currentProject.SpaceId);
            foreach (var pi in items)
            {
                var slotId = ResolveSlotIdForSpace(slots, pi.PreferredSlotId, _currentProject.SpaceId);
                if (string.IsNullOrEmpty(slotId) && compatibilityService != null)
                {
                    var showroomService = ServiceRegistry.Instance.Get<IShowroomService>();
                    var item = showroomService?.GetById(pi.ShowroomItemId);
                    if (item != null)
                    {
                        var compatible = compatibilityService.GetCompatibleSlotsForItem(_currentProject.SpaceId, item);
                        slotId = compatible?.FirstOrDefault()?.Id;
                    }
                }
                if (!string.IsNullOrEmpty(slotId))
                    _currentProject.ItemSlotAssignments.Add(new ItemSlotAssignment { ShowroomItemId = pi.ShowroomItemId, SlotId = slotId });
            }
            _currentProject.AppliedPresetId = presetId;
            _currentProject.LastCompositionValidation = null;
            RevalidateCompositionIfSafe();
            SyncActiveProposalFromProject();
            OnSessionChanged?.Invoke();
        }

        /// <summary>Gera a revisão comercial da proposta atual e armazena no projeto.</summary>
        public void ReviewProposal()
        {
            if (_currentProject == null) return;
            var service = ServiceRegistry.Instance.Get<IProposalReviewService>();
            if (service == null) return;
            _currentProject.LastProposalReviewSummary = service.GenerateReview(_currentProject);
            SyncActiveProposalFromProject();
            OnSessionChanged?.Invoke();
        }

        /// <summary>Salva um snapshot da proposta ativa atual para comparação (em memória).</summary>
        public void SaveCurrentVersion(string versionName = null)
        {
            if (_currentProject == null) return;
            SyncActiveProposalFromProject();
            var service = ServiceRegistry.Instance.Get<IProposalReviewService>();
            if (service == null) return;
            var snapshot = service.CreateSnapshot(_currentProject, versionName ?? $"Versão {_savedSnapshots.Count + 1}");
            _savedSnapshots.Add(snapshot);
            OnSessionChanged?.Invoke();
        }

        /// <summary>Gera ou regenera a narrativa de apresentação comercial da proposta atual.</summary>
        public void GeneratePresentation()
        {
            if (_currentProject == null) return;
            var service = ServiceRegistry.Instance.Get<IProposalPresentationService>();
            if (service == null) return;
            var steps = service.GeneratePresentation(_currentProject);
            _currentProject.GeneratedPresentationSteps = steps != null ? new List<ProposalPresentationStep>(steps) : new List<ProposalPresentationStep>();
            SyncActiveProposalFromProject();
            OnSessionChanged?.Invoke();
        }

        /// <summary>Aplica um tema visual ao projeto atual (direção estética).</summary>
        public void ApplyTheme(string themeId)
        {
            if (_currentProject == null) return;
            _currentProject.SelectedThemeId = string.IsNullOrEmpty(themeId) ? null : themeId;
            RevalidateCompositionIfSafe();
            SyncActiveProposalFromProject();
            OnSessionChanged?.Invoke();
        }

        /// <summary>Revalida a composição automaticamente quando preset/composição mudou (seguro e limpo).</summary>
        private void RevalidateCompositionIfSafe()
        {
            if (_currentProject == null) return;
            var service = ServiceRegistry.Instance.Get<IPlacementCompatibilityService>();
            if (service != null)
                _currentProject.LastCompositionValidation = service.ValidateComposition(_currentProject);
        }

        /// <summary>Restaura a composição padrão do template (itens padrão + mapeamento automático).</summary>
        public void RestoreDefaultComposition()
        {
            if (_currentProject == null) return;
            var showroomService = ServiceRegistry.Instance.Get<IShowroomService>();
            if (showroomService == null || string.IsNullOrEmpty(_currentProject.EventTemplateId)) return;
            _currentProject.ShowroomItemIds = new List<string>();
            foreach (var item in showroomService.GetByTemplateId(_currentProject.EventTemplateId))
                if (item.IsIncludedByDefault)
                    _currentProject.ShowroomItemIds.Add(item.Id);
            _currentProject.ItemSlotAssignments = new List<ItemSlotAssignment>();
            _currentProject.LastCompositionValidation = null;
            MapShowroomItemsToSlots(_currentProject, replaceAll: true);
            RevalidateCompositionIfSafe();
            SyncActiveProposalFromProject();
            OnSessionChanged?.Invoke();
        }

        /// <summary>Resolve chave de slot (ex.: "stage") para o id real do slot no espaço.</summary>
        private static string ResolveSlotIdForSpace(IReadOnlyList<SpaceLayoutSlot> slots, string preferredSlotKey, string spaceId)
        {
            if (slots == null || string.IsNullOrEmpty(preferredSlotKey)) return null;
            var key = preferredSlotKey.Trim().ToLowerInvariant();
            foreach (var s in slots)
            {
                if (s.SpaceId != spaceId) continue;
                if (s.Id == key || s.Id.EndsWith("-" + key, StringComparison.OrdinalIgnoreCase)) return s.Id;
            }
            return null;
        }

        /// <summary>Atualiza o status do projeto atual (ex.: Marcar proposta como pronta).</summary>
        public void SetProjectStatus(EventProjectStatus status)
        {
            if (_currentProject == null) return;
            _currentProject.Status = status;
            SyncActiveProposalFromProject();
            OnSessionChanged?.Invoke();
        }

        /// <summary>Define ou atualiza o briefing do cliente no projeto atual.</summary>
        public void SetClientBrief(ClientEventBrief brief)
        {
            if (_currentProject == null) return;
            _currentProject.ClientBrief = brief;
            var active = _currentProject.GetActiveProposal();
            if (active != null)
            {
                var scoringService = ServiceRegistry.Instance.Get<IProposalFitScoringService>();
                if (scoringService != null)
                    active.FitScore = scoringService.CalculateScore(_currentProject, active);
            }
            OnSessionChanged?.Invoke();
        }

        /// <summary>Garante que o projeto tenha pelo menos uma proposta (criada a partir do estado atual).</summary>
        public void EnsureInitialProposal()
        {
            if (_currentProject == null) return;
            if (_currentProject.Proposals == null)
                _currentProject.Proposals = new List<EventProjectProposal>();
            if (_currentProject.Proposals.Count > 0)
                return;
            var p = CreateProposalFromCurrentState("Proposta inicial");
            _currentProject.Proposals.Add(p);
            _currentProject.ActiveProposalId = p.Id;
            OnSessionChanged?.Invoke();
        }

        /// <summary>Duplica a proposta ativa e a define como ativa.</summary>
        /// <returns>Nova proposta ou null se não houver projeto/proposta ativa.</returns>
        public EventProjectProposal DuplicateActiveProposal()
        {
            if (_currentProject == null) return null;
            SyncActiveProposalFromProject();
            var active = _currentProject.GetActiveProposal();
            if (active == null) return null;
            var name = "Cópia de " + (string.IsNullOrEmpty(active.Name) ? "Proposta" : active.Name);
            var copy = CreateProposalFromCurrentState(name);
            copy.CreatedAt = DateTime.Now.ToString("yyyy-MM-dd HH:mm");
            _currentProject.Proposals.Add(copy);
            _currentProject.ActiveProposalId = copy.Id;
            LoadProposalIntoProject(copy.Id);
            OnSessionChanged?.Invoke();
            return copy;
        }

        /// <summary>Ativa a proposta pelo id e carrega seu estado no projeto atual.</summary>
        public void ActivateProposal(string proposalId)
        {
            if (_currentProject == null || string.IsNullOrEmpty(proposalId)) return;
            if (_currentProject.Proposals == null) return;
            var p = _currentProject.Proposals.Find(x => x.Id == proposalId);
            if (p == null) return;
            LoadProposalIntoProject(proposalId);
            _currentProject.ActiveProposalId = proposalId;
            OnSessionChanged?.Invoke();
        }

        /// <summary>Renomeia uma proposta pelo id.</summary>
        public void RenameProposal(string proposalId, string name)
        {
            if (_currentProject == null || string.IsNullOrEmpty(proposalId)) return;
            var p = _currentProject.Proposals?.Find(x => x.Id == proposalId);
            if (p != null)
            {
                p.Name = string.IsNullOrEmpty(name) ? p.Name : name;
                OnSessionChanged?.Invoke();
            }
        }

        /// <summary>Sincroniza o estado do projeto para a proposta ativa (preset, tema, pacote comercial, itens, slots, validação, narrativa, revisão), recomputa derivados e espelha de volta no projeto.</summary>
        public void SyncActiveProposalFromProject()
        {
            if (_currentProject == null) return;
            var p = _currentProject.GetActiveProposal();
            if (p == null) return;
            p.PresetId = _currentProject.AppliedPresetId;
            p.ThemeId = _currentProject.SelectedThemeId;
            p.SelectedCommercialPackageId = _currentProject.AppliedCommercialPackageId;
            p.ShowroomItemIds = _currentProject.ShowroomItemIds != null ? new List<string>(_currentProject.ShowroomItemIds) : new List<string>();
            p.ItemSlotAssignments = _currentProject.ItemSlotAssignments != null
                ? _currentProject.ItemSlotAssignments.ConvertAll(a => new ItemSlotAssignment { ShowroomItemId = a.ShowroomItemId, SlotId = a.SlotId })
                : new List<ItemSlotAssignment>();
            p.ValidationSummary = CloneValidationResult(_currentProject.LastCompositionValidation);
            p.PresentationSteps = ClonePresentationSteps(_currentProject.GeneratedPresentationSteps);
            p.ReviewSummary = CloneReviewSummary(_currentProject.LastProposalReviewSummary);
            var pricingService = ServiceRegistry.Instance.Get<IProposalPricingService>();
            var pricing = pricingService?.GetPricingSummary(_currentProject);
            p.TotalEstimatedPrice = pricing?.TotalEstimated ?? 0f;
            p.SupplierCount = pricing?.SupplierCount ?? 0;
            RecomputeDerivedProposalData(p);
            _currentProject.CurrentMomentPlans = CloneMomentPlans(p.MomentPlans);
            _currentProject.CurrentMomentTransitions = CloneMomentTransitions(p.MomentTransitions);
            _currentProject.CurrentGuidedTourSteps = CloneGuidedTourSteps(p.GuidedTourSteps);
            _currentProject.CurrentVisualHotspots = CloneVisualHotspots(p.VisualHotspots);
            _currentProject.CurrentSceneStates = CloneSceneStates(p.SceneStates);
            _currentProject.CurrentMRPresentationPlan = CloneMRPlan(p.MRPresentationPlan);
            _currentProject.AppliedCommercialPackageId = p.SelectedCommercialPackageId;
            var scoringService = ServiceRegistry.Instance.Get<IProposalFitScoringService>();
            if (scoringService != null)
                p.FitScore = scoringService.CalculateScore(_currentProject, p);
        }

        /// <summary>Recomputa derivados, sincroniza a proposta ativa e invalida resumos globais quando necessário (recomendação, decisão, comparação temporal).</summary>
        public void RecomputeAndSyncActiveProposalState()
        {
            if (_currentProject == null) return;
            SyncActiveProposalFromProject();
            _currentProject.LastRecommendationSummary = null;
            _currentProject.LastDecisionSummary = null;
            _currentProject.LastTemporalComparison = null;
            OnSessionChanged?.Invoke();
        }

        /// <summary>Aplica um pacote comercial à proposta ativa (itens, tema sugerido e associação ao pacote). Remapeia itens em slots compatíveis e deixa a proposta consistente.</summary>
        public void ApplyCommercialPackage(string packageId)
        {
            if (_currentProject == null || string.IsNullOrEmpty(packageId)) return;
            var packageService = ServiceRegistry.Instance.Get<ICommercialPackageService>();
            var package = packageService?.GetById(packageId);
            if (package == null) return;
            var active = _currentProject.GetActiveProposal();
            if (active == null) return;
            active.SelectedCommercialPackageId = package.Id;
            if (package.IncludedShowroomItemIds != null && package.IncludedShowroomItemIds.Count > 0)
                active.ShowroomItemIds = new List<string>(package.IncludedShowroomItemIds);
            else
                active.ShowroomItemIds = new List<string>();
            active.ItemSlotAssignments = new List<ItemSlotAssignment>();
            if (package.SuggestedThemeIds != null && package.SuggestedThemeIds.Count > 0)
                active.ThemeId = package.SuggestedThemeIds[0];
            LoadProposalIntoProject(active.Id);
            MapShowroomItemsToSlots(_currentProject, replaceAll: true);
            RevalidateCompositionIfSafe();
            RecomputeAndSyncActiveProposalState();
        }

        /// <summary>Carrega a proposta no estado atual do projeto: preset, tema, pacote comercial, itens, slots, validação, narrativa, revisão e todos os derivados (moment plans, transições, roteiro guiado, hotspots, estados de cena, plano MR). Invalida recommendation/decision/comparison.</summary>
        public void LoadProposalIntoProject(string proposalId)
        {
            if (_currentProject == null || string.IsNullOrEmpty(proposalId)) return;
            var p = _currentProject.Proposals?.Find(x => x.Id == proposalId);
            if (p == null) return;
            _currentProject.ActiveProposalId = proposalId;
            _currentProject.AppliedPresetId = p.PresetId;
            _currentProject.SelectedThemeId = p.ThemeId;
            _currentProject.AppliedCommercialPackageId = p.SelectedCommercialPackageId;
            _currentProject.ShowroomItemIds = p.ShowroomItemIds != null ? new List<string>(p.ShowroomItemIds) : new List<string>();
            _currentProject.ItemSlotAssignments = p.ItemSlotAssignments != null
                ? p.ItemSlotAssignments.ConvertAll(a => new ItemSlotAssignment { ShowroomItemId = a.ShowroomItemId, SlotId = a.SlotId })
                : new List<ItemSlotAssignment>();
            _currentProject.LastCompositionValidation = CloneValidationResult(p.ValidationSummary);
            _currentProject.GeneratedPresentationSteps = ClonePresentationSteps(p.PresentationSteps);
            _currentProject.LastProposalReviewSummary = CloneReviewSummary(p.ReviewSummary);
            _currentProject.CurrentMomentPlans = CloneMomentPlans(p.MomentPlans);
            _currentProject.CurrentMomentTransitions = CloneMomentTransitions(p.MomentTransitions);
            _currentProject.CurrentGuidedTourSteps = CloneGuidedTourSteps(p.GuidedTourSteps);
            _currentProject.CurrentVisualHotspots = CloneVisualHotspots(p.VisualHotspots);
            _currentProject.CurrentSceneStates = CloneSceneStates(p.SceneStates);
            _currentProject.CurrentMRPresentationPlan = CloneMRPlan(p.MRPresentationPlan);
            _currentProject.LastRecommendationSummary = null;
            _currentProject.LastDecisionSummary = null;
            _currentProject.LastTemporalComparison = null;
            var scoringService = ServiceRegistry.Instance.Get<IProposalFitScoringService>();
            if (scoringService != null)
                p.FitScore = scoringService.CalculateScore(_currentProject, p);
            OnSessionChanged?.Invoke();
        }

        private static EventProjectProposal CreateProposalFromCurrentState(EventProject project, string name)
        {
            var active = project.GetActiveProposal();
            var p = new EventProjectProposal
            {
                Id = Guid.NewGuid().ToString(),
                Name = name,
                CreatedAt = DateTime.Now.ToString("yyyy-MM-dd HH:mm"),
                PresetId = project.AppliedPresetId,
                ThemeId = project.SelectedThemeId,
                ShowroomItemIds = project.ShowroomItemIds != null ? new List<string>(project.ShowroomItemIds) : new List<string>(),
                ItemSlotAssignments = project.ItemSlotAssignments != null
                    ? project.ItemSlotAssignments.ConvertAll(a => new ItemSlotAssignment { ShowroomItemId = a.ShowroomItemId, SlotId = a.SlotId })
                    : new List<ItemSlotAssignment>(),
                ValidationSummary = CloneValidationResult(project.LastCompositionValidation),
                PresentationSteps = ClonePresentationSteps(project.GeneratedPresentationSteps),
                ReviewSummary = CloneReviewSummary(project.LastProposalReviewSummary),
                SelectedCommercialPackageId = project.AppliedCommercialPackageId ?? active?.SelectedCommercialPackageId,
                MomentPlans = CloneMomentPlans(project.CurrentMomentPlans?.Count > 0 ? project.CurrentMomentPlans : active?.MomentPlans),
                MomentTransitions = CloneMomentTransitions(project.CurrentMomentTransitions?.Count > 0 ? project.CurrentMomentTransitions : active?.MomentTransitions),
                GuidedTourSteps = CloneGuidedTourSteps(project.CurrentGuidedTourSteps?.Count > 0 ? project.CurrentGuidedTourSteps : active?.GuidedTourSteps),
                VisualHotspots = CloneVisualHotspots(project.CurrentVisualHotspots?.Count > 0 ? project.CurrentVisualHotspots : active?.VisualHotspots),
                SceneStates = CloneSceneStates(project.CurrentSceneStates?.Count > 0 ? project.CurrentSceneStates : active?.SceneStates),
                MRPresentationPlan = CloneMRPlan(project.CurrentMRPresentationPlan ?? active?.MRPresentationPlan)
            };
            var pricingService = ServiceRegistry.Instance.Get<IProposalPricingService>();
            var pricing = pricingService?.GetPricingSummary(project);
            p.TotalEstimatedPrice = pricing?.TotalEstimated ?? 0f;
            p.SupplierCount = pricing?.SupplierCount ?? 0;
            return p;
        }

        private EventProjectProposal CreateProposalFromCurrentState(string name)
        {
            return CreateProposalFromCurrentState(_currentProject, name);
        }

        private static CompositionValidationResult CloneValidationResult(CompositionValidationResult source)
        {
            if (source == null) return null;
            return new CompositionValidationResult
            {
                Status = source.Status,
                Warnings = source.Warnings?.ConvertAll(w => new ValidationMessage { Code = w.Code, Message = w.Message, ItemId = w.ItemId, SlotId = w.SlotId }) ?? new List<ValidationMessage>(),
                Inconsistencies = source.Inconsistencies?.ConvertAll(i => new ValidationMessage { Code = i.Code, Message = i.Message, ItemId = i.ItemId, SlotId = i.SlotId }) ?? new List<ValidationMessage>()
            };
        }

        private static ProposalReviewSummary CloneReviewSummary(ProposalReviewSummary source)
        {
            if (source == null) return null;
            return new ProposalReviewSummary
            {
                IsReadyForPresentation = source.IsReadyForPresentation,
                ValidationStatus = source.ValidationStatus,
                BudgetLevel = source.BudgetLevel,
                ThemeName = source.ThemeName,
                PresetName = source.PresetName,
                PresentationStepCount = source.PresentationStepCount,
                Strengths = source.Strengths != null ? new List<string>(source.Strengths) : new List<string>(),
                Warnings = source.Warnings != null ? new List<string>(source.Warnings) : new List<string>(),
                MissingElements = source.MissingElements != null ? new List<string>(source.MissingElements) : new List<string>(),
                BudgetWithinTarget = source.BudgetWithinTarget,
                MoodMatchesBrief = source.MoodMatchesBrief,
                CapacitySummary = source.CapacitySummary,
                FlowSummary = source.FlowSummary,
                SetupTimelineSummary = source.SetupTimelineSummary,
                SupplierCoverageSummary = source.SupplierCoverageSummary,
                CommercialReadingSummary = source.CommercialReadingSummary,
                JourneySummary = source.JourneySummary,
                MomentPlanCount = source.MomentPlanCount,
                TransitionSummary = source.TransitionSummary,
                TransitionCount = source.TransitionCount,
                AverageSmoothnessScore = source.AverageSmoothnessScore,
                GuidedTourSummary = source.GuidedTourSummary,
                GuidedTourStepCount = source.GuidedTourStepCount,
                ReadyForGuidedPresentation = source.ReadyForGuidedPresentation,
                VisualHotspotSummary = source.VisualHotspotSummary,
                VisualHotspotCount = source.VisualHotspotCount,
                PrimaryHotspotTitle = source.PrimaryHotspotTitle,
                HasPremiumHotspots = source.HasPremiumHotspots,
                SceneStateSummary = source.SceneStateSummary,
                SceneStateCount = source.SceneStateCount,
                HasStructuredVisualEvolution = source.HasStructuredVisualEvolution
            };
        }

        private static List<ProposalVisualHotspot> CloneVisualHotspots(List<ProposalVisualHotspot> source)
        {
            if (source == null || source.Count == 0) return new List<ProposalVisualHotspot>();
            return source.ConvertAll(h => new ProposalVisualHotspot
            {
                Id = h.Id,
                Title = h.Title,
                Description = h.Description,
                RelatedZoneTypes = h.RelatedZoneTypes != null ? new List<string>(h.RelatedZoneTypes) : new List<string>(),
                RelatedShowroomItemIds = h.RelatedShowroomItemIds != null ? new List<string>(h.RelatedShowroomItemIds) : new List<string>(),
                RelatedGuidedTourStepId = h.RelatedGuidedTourStepId,
                HotspotType = h.HotspotType,
                HighlightPriority = h.HighlightPriority,
                SalesValueScore = h.SalesValueScore,
                SuggestedFocusDurationMinutes = h.SuggestedFocusDurationMinutes,
                IsPremiumUpsell = h.IsPremiumUpsell,
                Notes = h.Notes
            });
        }

        private static List<ProposalSceneState> CloneSceneStates(List<ProposalSceneState> source)
        {
            if (source == null || source.Count == 0) return new List<ProposalSceneState>();
            return source.ConvertAll(s => new ProposalSceneState
            {
                Id = s.Id,
                RelatedPhaseId = s.RelatedPhaseId,
                Name = s.Name,
                Description = s.Description,
                ActiveShowroomItemIds = s.ActiveShowroomItemIds != null ? new List<string>(s.ActiveShowroomItemIds) : new List<string>(),
                ActiveZoneTypes = s.ActiveZoneTypes != null ? new List<string>(s.ActiveZoneTypes) : new List<string>(),
                PrimaryHotspotIds = s.PrimaryHotspotIds != null ? new List<string>(s.PrimaryHotspotIds) : new List<string>(),
                VisualMood = s.VisualMood,
                TransitionFromPreviousSummary = s.TransitionFromPreviousSummary,
                DisplayOrder = s.DisplayOrder
            });
        }

        private static List<ProposalGuidedTourStep> CloneGuidedTourSteps(List<ProposalGuidedTourStep> source)
        {
            if (source == null || source.Count == 0) return new List<ProposalGuidedTourStep>();
            return source.ConvertAll(s => new ProposalGuidedTourStep
            {
                Id = s.Id,
                Title = s.Title,
                Description = s.Description,
                RelatedPhaseId = s.RelatedPhaseId,
                RelatedZoneTypes = s.RelatedZoneTypes != null ? new List<string>(s.RelatedZoneTypes) : new List<string>(),
                RelatedShowroomItemIds = s.RelatedShowroomItemIds != null ? new List<string>(s.RelatedShowroomItemIds) : new List<string>(),
                FocusType = s.FocusType,
                SuggestedDurationMinutes = s.SuggestedDurationMinutes,
                SalesMessage = s.SalesMessage,
                DisplayOrder = s.DisplayOrder,
                IsKeyMoment = s.IsKeyMoment
            });
        }

        private static List<EventMomentTransition> CloneMomentTransitions(List<EventMomentTransition> source)
        {
            if (source == null || source.Count == 0) return new List<EventMomentTransition>();
            return source.ConvertAll(t => new EventMomentTransition
            {
                FromPhaseId = t.FromPhaseId,
                ToPhaseId = t.ToPhaseId,
                TransitionType = t.TransitionType,
                EstimatedTransitionMinutes = t.EstimatedTransitionMinutes,
                RequiresSpaceReset = t.RequiresSpaceReset,
                RequiresSupplierCoordination = t.RequiresSupplierCoordination,
                SmoothnessScore = t.SmoothnessScore,
                Notes = t.Notes
            });
        }

        private static List<ProposalMomentPlan> CloneMomentPlans(List<ProposalMomentPlan> source)
        {
            if (source == null || source.Count == 0) return new List<ProposalMomentPlan>();
            return source.ConvertAll(m => new ProposalMomentPlan
            {
                PhaseId = m.PhaseId,
                RelatedShowroomItemIds = m.RelatedShowroomItemIds != null ? new List<string>(m.RelatedShowroomItemIds) : new List<string>(),
                RelatedZoneTypes = m.RelatedZoneTypes != null ? new List<string>(m.RelatedZoneTypes) : new List<string>(),
                KeySupplierIds = m.KeySupplierIds != null ? new List<string>(m.KeySupplierIds) : new List<string>(),
                EstimatedDurationMinutes = m.EstimatedDurationMinutes,
                HighlightText = m.HighlightText
            });
        }

        private static ProposalMRPresentationPlan CloneMRPlan(ProposalMRPresentationPlan source)
        {
            if (source == null) return null;
            return new ProposalMRPresentationPlan
            {
                ProposalId = source.ProposalId,
                PresentationTitle = source.PresentationTitle,
                TargetExperienceMode = source.TargetExperienceMode,
                GuidedSequenceStepIds = source.GuidedSequenceStepIds != null ? new List<string>(source.GuidedSequenceStepIds) : new List<string>(),
                HotspotIds = source.HotspotIds != null ? new List<string>(source.HotspotIds) : new List<string>(),
                SceneStateIds = source.SceneStateIds != null ? new List<string>(source.SceneStateIds) : new List<string>(),
                PriorityZones = source.PriorityZones != null ? new List<string>(source.PriorityZones) : new List<string>(),
                KeySellingMoments = source.KeySellingMoments != null ? new List<string>(source.KeySellingMoments) : new List<string>(),
                RecommendedSessionDurationMinutes = source.RecommendedSessionDurationMinutes,
                ExecutivePresentationGoal = source.ExecutivePresentationGoal,
                ReadyForMRMapping = source.ReadyForMRMapping
            };
        }

        /// <summary>Recomputa dados derivados da proposta (moment plans, transições, roteiro guiado, hotspots, estados de cena, plano MR) a partir do estado actual do projeto.</summary>
        private void RecomputeDerivedProposalData(EventProjectProposal p)
        {
            if (_currentProject == null || p == null) return;
            var momentPlanning = ServiceRegistry.Instance.Get<IEventMomentPlanningService>();
            var transitionService = ServiceRegistry.Instance.Get<IEventMomentTransitionService>();
            var guidedTourService = ServiceRegistry.Instance.Get<IProposalGuidedTourService>();
            var hotspotService = ServiceRegistry.Instance.Get<IProposalVisualHotspotService>();
            var sceneStateService = ServiceRegistry.Instance.Get<IProposalSceneStateService>();
            var mrPlanningService = ServiceRegistry.Instance.Get<IMRPresentationPlanningService>();

            var plans = momentPlanning?.GenerateMomentPlan(_currentProject, p);
            p.MomentPlans = plans != null ? new List<ProposalMomentPlan>(plans) : new List<ProposalMomentPlan>();

            var transitions = transitionService?.GenerateTransitions(_currentProject, p);
            p.MomentTransitions = transitions != null ? new List<EventMomentTransition>(transitions) : new List<EventMomentTransition>();

            var steps = guidedTourService?.GenerateGuidedTour(_currentProject, p);
            p.GuidedTourSteps = steps != null ? new List<ProposalGuidedTourStep>(steps) : new List<ProposalGuidedTourStep>();

            var hotspots = hotspotService?.GenerateHotspots(_currentProject, p);
            p.VisualHotspots = hotspots != null ? new List<ProposalVisualHotspot>(hotspots) : new List<ProposalVisualHotspot>();

            var states = sceneStateService?.GenerateSceneStates(_currentProject, p);
            p.SceneStates = states != null ? new List<ProposalSceneState>(states) : new List<ProposalSceneState>();

            var mrPlan = mrPlanningService?.GeneratePlan(_currentProject, p);
            p.MRPresentationPlan = mrPlan;
        }

        private static List<ProposalPresentationStep> ClonePresentationSteps(List<ProposalPresentationStep> source)
        {
            if (source == null) return null;
            return source.ConvertAll(s => new ProposalPresentationStep
            {
                Id = s.Id,
                Title = s.Title,
                Description = s.Description,
                StepType = s.StepType,
                DisplayOrder = s.DisplayOrder,
                IsHighlight = s.IsHighlight,
                RelatedShowroomItemIds = s.RelatedShowroomItemIds != null ? new List<string>(s.RelatedShowroomItemIds) : new List<string>(),
                RelatedPlacementZones = s.RelatedPlacementZones != null ? new List<PlacementZoneType>(s.RelatedPlacementZones) : new List<PlacementZoneType>()
            });
        }

        /// <summary>Limpa o projeto atual (mantem selecoes).</summary>
        public void ClearProject()
        {
            CurrentProject = null;
        }

        /// <summary>Mapeia itens do showroom para slots do espaço (por zona/ancoragem).</summary>
        private void MapShowroomItemsToSlots(EventProject project, bool replaceAll)
        {
            if (project == null || string.IsNullOrEmpty(project.SpaceId)) return;
            var layoutService = ServiceRegistry.Instance.Get<ISpaceLayoutService>();
            var showroomService = ServiceRegistry.Instance.Get<IShowroomService>();
            if (layoutService == null || showroomService == null) return;

            if (project.ItemSlotAssignments == null)
                project.ItemSlotAssignments = new List<ItemSlotAssignment>();

            if (replaceAll)
                project.ItemSlotAssignments.Clear();

            var assignedIds = new HashSet<string>(project.ItemSlotAssignments.Select(a => a.ShowroomItemId));
            var itemIdsToAssign = project.ShowroomItemIds?.Where(id => !assignedIds.Contains(id)).ToList() ?? new List<string>();
            if (itemIdsToAssign.Count == 0) return;

            var items = itemIdsToAssign
                .Select(id => showroomService.GetById(id))
                .Where(i => i != null)
                .OrderBy(i => i.DisplayOrder)
                .ToList();

            var slotUsage = new Dictionary<string, int>();
            foreach (var slot in layoutService.GetBySpaceId(project.SpaceId))
                slotUsage[slot.Id] = project.ItemSlotAssignments.Count(a => a.SlotId == slot.Id);

            var compatibilityService = ServiceRegistry.Instance.Get<IPlacementCompatibilityService>();
            foreach (var item in items)
            {
                IReadOnlyList<SpaceLayoutSlot> compatible;
                if (compatibilityService != null)
                {
                    compatible = compatibilityService.GetCompatibleSlotsForItem(project.SpaceId, item);
                }
                else
                {
                    compatible = layoutService.GetCompatibleSlots(project.SpaceId, item.PlacementZone, item.SpatialAnchorType);
                    if (compatible == null || compatible.Count == 0)
                        compatible = layoutService.GetCompatibleSlots(project.SpaceId, item.PlacementZone, null);
                }
                if (compatible == null || compatible.Count == 0) continue;
                foreach (var slot in compatible)
                {
                    var used = slotUsage.TryGetValue(slot.Id, out var n) ? n : 0;
                    if (slot.CapacityHint <= 0 || used < slot.CapacityHint)
                    {
                        project.ItemSlotAssignments.Add(new ItemSlotAssignment { ShowroomItemId = item.Id, SlotId = slot.Id });
                        slotUsage[slot.Id] = used + 1;
                        break;
                    }
                }
            }
        }
    }
}
