using ReAct_PME.WPF_FrameWork;
using Services;
using Services.Models;
using Services.ReActAgent;
using System.Collections.ObjectModel;
using System.Windows;
using ToolsServices;

namespace ReAct_PME.ViewModels
{
    public class ChatRoom_VM : BaseViewModel
    {
        #region Variables
        private string TypeConv = "LLM";
        private readonly IToastNotifier _Notifier;
        #endregion

        #region Propriétés
        // Effectue une RAZ dans les collections Qdrant avant d'ingérer de nouveaux documents
        public bool IsRAZCollections { get; set; } = false;
        
        public string ImageURL { get; set; } = "";

        public ObservableCollection<ChatConversation> Conversations { get; set; } = new();
        public ObservableCollection<ChatMessage> CurrentMessages { get; set; } = new();
        public ObservableCollection<OllamaModel> InstalledModels { get; set; } = new();
        public ObservableCollection<string> SelectedDocumentsPaths { get; set; } = new ObservableCollection<string>();
        public ObservableCollection<string> SelectedDocumentsListe { get; set; } = new ObservableCollection<string>();
        //isWithAssistant

        public bool IsLLM
        {
            get => (!IsGenerateImg && !IsRAG);
        }
        
        private bool _IsGenerateParametres = false;
        public bool IsGenerateParametres
        {
            get => _IsGenerateParametres;
            set
            {
                _IsGenerateParametres = value;
                OnPropertyChanged(nameof(IsGenerateParametres));
            }
        }

        private bool _IsWithAssistant = false;
        public bool IsWithAssistant
        {
            get => _IsWithAssistant;
            set
            {
                _IsWithAssistant = value;
                OnPropertyChanged(nameof(IsWithAssistant));
            }
        }

        private bool _IsOllamaActif = false;
        public bool IsOllamaActif
        {
            get => _IsOllamaActif;
            set
            {
                _IsOllamaActif = value;
                OnPropertyChanged(nameof(IsOllamaActif));
            }
        }

        private bool _IsGenerateImg = false;
        public bool IsGenerateImg
        {
            get => _IsGenerateImg;
            set
            {
                _IsGenerateImg = value;
                OnPropertyChanged(nameof(IsGenerateImg));
                if(_IsGenerateImg)
                {
                    TypeConv = "GenerateImage";
                }
            }
        }

        private bool _IsRAG = false;
        public bool IsRAG
        {
            get => _IsRAG;
            set
            {
                _IsRAG = value;
                OnPropertyChanged(nameof(IsRAG));
                OnPropertyChanged(nameof(IsDisabledWhenRAG));
                if(_IsRAG)
                {
                    TypeConv = "RAG";
                    IsApiExterne = false;
                    OnPropertyChanged(nameof(IsApiExterne));
                    IsVisibleDomain = Visibility.Visible;
                    OnPropertyChanged(nameof(IsVisibleDomain));
                }
            }
        }

        private bool _IsCODER = false;
        public bool IsCODER
        {
            get => _IsCODER;
            set
            {
                _IsCODER = value;
                OnPropertyChanged(nameof(IsCODER));

                if (_IsCODER)
                {
                    TypeConv = "CODER";
                    IsApiExterne = false;
                    OnPropertyChanged(nameof(IsApiExterne));
                }
            }
        }

        private bool _IsApiExterne = false;
        public bool IsApiExterne
        {
            get => _IsApiExterne;
            set
            {
                _IsApiExterne = value;
                OnPropertyChanged(nameof(IsApiExterne));
                if (_IsApiExterne)
                {
                    IsRAG = false;
                    OnPropertyChanged(nameof(IsRAG));
                }
            }
        }

        public bool IsDisabledWhenRAG
        {
            get => !_IsRAG;
        }

        private OllamaModel? _selectedModel = new();
        public OllamaModel? SelectedModel
        {
            get => _selectedModel;
            set { 
                _selectedModel = value; 
                OnPropertyChanged(nameof(SelectedModel)); 
                OnPropertyChanged(nameof(CanChangeModel)); 
            }
        }

        private ChatConversation? _SelectedConversation;
        public ChatConversation? SelectedConversation
        {
            get
            {
                if (_SelectedConversation != null && _SelectedConversation.Model != null)
                {
                    _selectedModel = InstalledModels.Where(e => e.Name == _SelectedConversation!.Model)!.FirstOrDefault()!;
                    if (_selectedModel != null)
                        OnPropertyChanged(nameof(SelectedModel));
                }
                return _SelectedConversation;
            }
            set
            {
                _SelectedConversation = value;
                if (_SelectedConversation != null && _SelectedConversation!.Model != null && _SelectedConversation!.Model != "")
                {
                    if (SelectedModel != null && InstalledModels != null && InstalledModels.Count>0)
                    {
                        SelectedModel = InstalledModels.Where(e => e.Name == _SelectedConversation!.Model)!.FirstOrDefault()!;
                        //SelectedModel.Name = _SelectedConversation!.Model;
                        OnPropertyChanged(nameof(SelectedModel));
                        OnPropertyChanged(nameof(CanChangeModel));
                    }
                }
            }
        }

        public string NewMessage { get; set; } = "";

        private Visibility _IsVisibleDomain = Visibility.Collapsed;
        public Visibility IsVisibleDomain
        {
            get => _IsVisibleDomain;
            set
            {
                _IsVisibleDomain = value;
                OnPropertyChanged(nameof(IsVisibleDomain));
            }
        }

        public string DomainSelected { get; set; } = "";
        public ObservableCollection<string> DomainListe { get; set; } = new ObservableCollection<string>(DomainExtensions.CollectionsNameRestricted);
        #endregion

        #region Constructeur
        public ChatRoom_VM(IToastNotifier notifier)
        {
            _Notifier = notifier;
            InstalledModels = new();
             _= VerifOllama();
            DomainSelected = DomainListe[0];
        }
        #endregion

        #region Méthodes publiques
        public void LoadConversation(ChatConversation conv)
        {
            if (conv == null)
                return;
            SelectedConversation = conv;
            CurrentMessages.Clear();
            foreach (var m in conv.Messages)
                CurrentMessages.Add(m);
            OnPropertyChanged(nameof(CanChangeModel));
        }

        public void LoadModeles()
        {
            _ = GetInstalledModelsAsync();
        }

        public void LoadConversations()
        {
            var all = ChatService.LoadAll(TypeConv);
            all = all.OrderByDescending(c => c.LastUse).ToList();
            foreach (var c in all)
                Conversations.Add(c);
        }

        public bool CanChangeModel => (SelectedConversation?.Messages == null || SelectedConversation?.Messages.Count == 0);

        public void NewConversation()
        {
            CurrentMessages.Clear();
            SelectedModel = null;
            var conv = new ChatConversation();
            //Conversations.Add(conv);
            SelectedConversation = conv;
            OnPropertyChanged(nameof(SelectedModel));
            OnPropertyChanged(nameof(CanChangeModel));

        }

        public void DeleteConversation()
        {
            if (SelectedConversation != null)
            {
                CurrentMessages.Clear();
                ChatService.Delete(SelectedConversation.Id);
                Conversations.Remove(SelectedConversation);
                SelectedConversation = Conversations.FirstOrDefault();
                OnPropertyChanged(nameof(CanChangeModel));
            }
        }

        public async Task<bool> SendMessage()
        {
            NewMessage = NewMessage.Trim();

            if (string.IsNullOrWhiteSpace(NewMessage) && SelectedDocumentsPaths.Count == 0)
            {
                _Notifier.ShowToast("Votre requête est vide, veuillez poser une question à l'assistant IA...");
                return false;
            }

            if (string.IsNullOrWhiteSpace(NewMessage) && SelectedDocumentsPaths.Count > 0)
                NewMessage = "Fais un résumé des documents";

            if (!IsApiExterne && IsGenerateImg)
            {
                var isFooocusActif = await VerifFooocus();
                if (isFooocusActif == false)
                {
                    _Notifier.ShowToast("Fooocus n'est pas démarré. Veuillez lancer le service Fooocus.");
                    return false;
                }
            }

            if (!IsApiExterne || (IsGenerateImg && IsWithAssistant))
            {
                var isOllamaActif = await VerifOllama();
                if (isOllamaActif == false)
                {
                    _Notifier.ShowToast("Ollama n'est pas démarré. Veuillez lancer Ollama Desktop ou le service Ollama.");
                    return false;
                }
            }

            ModelsUseCases.TypeUseCase useCase;
            if (IsRAG)
            {
                useCase = ModelsUseCases.TypeUseCase.RAG; 
            }
            else if (IsGenerateImg)
            { 
                useCase = ModelsUseCases.TypeUseCase.PromptGenerationFooocus; 
            }
            else if (IsCODER)
            {
                useCase = ModelsUseCases.TypeUseCase.LLM_Coder;
            }
            else
            {
                useCase = ModelsUseCases.TypeUseCase.LLM;
            }

            var modelUsed = "";
            if (IsApiExterne)
            {
                modelUsed = "Api Externe";
            }
            else
            {
                if (IsGenerateImg && !IsWithAssistant)
                {
                    modelUsed = "Pas d'assistant IA";
                }
                else
                {
                    if (SelectedModel == null || SelectedModel.Name == null || SelectedModel.Name == "")
                    {
                        modelUsed = ReActAgent.GetModeleIA(useCase);
                    }
                    else
                    {
                        modelUsed = SelectedModel!.Name;
                    }
                }
            }

            var userMessage = new ChatMessage { Role = "user", Content = NewMessage, Model = modelUsed };
            var conv = SelectedConversation;

            if (conv == null || conv.Messages.Count == 0)
            {
                conv = new ChatConversation
                {
                    Type = TypeConv,
                    TitleLong = NewMessage.Length > 100 ? NewMessage[..100] + "..." : NewMessage,
                    Title = NewMessage.Length > 25 ? NewMessage[..25] + "..." : NewMessage,
                    Model = modelUsed
                };
                //Conversations.Add(conv);
                Conversations.Insert(0, conv);

                SelectedConversation = conv;
                OnPropertyChanged(nameof(CanChangeModel));
                OnPropertyChanged(nameof(Conversations));
            }
            conv.LastUse = DateTime.Now;

            if (conv.Model == null || conv.Model == "")
                conv.Model = modelUsed;// SelectedModel!.Name;

            conv.Messages.Add(userMessage);
            CurrentMessages.Add(userMessage);

            var context = BuildContextWindow(conv.Messages);
            string m = "";
            string response = "";
            bool isSuccess = false;
            if (IsRAG)
            {
                LoggerService.LogInfo($"ChatRoom_VM.SendMessage (RAG) {conv.Model}: {conv.TitleLong}");
                LoggerService.LogDebug($"{NewMessage}");

                Domain domain = DomainExtensions.GetDomainByCollectionName(DomainSelected);

                (isSuccess, response, m) = await RAGService.RechercheDansDocuments(domain, userMessage.Content, conv.Model, IsRAZCollections);
            }
            else if (IsGenerateImg && SelectedDocumentsPaths.Count > 0)
            {
                if (IsWithAssistant)
                    modelUsed = conv.Model;

                LoggerService.LogInfo($"ChatRoom_VM.SendMessage (Générer Text to Image avec IP) {modelUsed}: {conv.TitleLong}");
                LoggerService.LogDebug($"{NewMessage}");

                (isSuccess, response, m) = await ChatService.SendMessageAsync(userMessage.Content, modelUsed, IsWithAssistant, SelectedDocumentsPaths.ToList());
            }
            else if(IsGenerateImg && SelectedDocumentsPaths.Count == 0)
            {
                if (IsWithAssistant)
                    modelUsed = conv.Model;

                LoggerService.LogInfo($"ChatRoom_VM.SendMessage (Generate Text to Image) {modelUsed}: {conv.TitleLong}");
                LoggerService.LogDebug($"{NewMessage}");

                (isSuccess, response, m) = await ChatService.SendMessageAsync(userMessage.Content, modelUsed, IsWithAssistant, IsGenerateParametres);
            }
            else if (IsCODER)
            {
                if (SelectedDocumentsPaths.Count > 0) // avec documents
                {
                    LoggerService.LogInfo($"ChatRoom_VM.SendMessage (LLM CODER et documents) {conv.Model}: {conv.TitleLong}");
                    LoggerService.LogDebug($"{NewMessage}");

                    userMessage.Content = string.Join(", ", SelectedDocumentsListe) + " : " + userMessage.Content;
                    (isSuccess, response, m) = await ChatService.SendMessageAsync(ModelsUseCases.TypeUseCase.LLM_Coder, userMessage.Content, conv.Model, SelectedDocumentsPaths.ToList(), IsApiExterne);

                    SelectedDocumentsListe.Clear();
                    SelectedDocumentsPaths.Clear();
                    OnPropertyChanged(nameof(SelectedDocumentsListe));
                    OnPropertyChanged(nameof(SelectedDocumentsPaths));
                }
                else
                {
                    LoggerService.LogInfo($"ChatRoom_VM.SendMessage (LLM CODER) {conv.Model}: {conv.TitleLong}");
                    LoggerService.LogDebug($"Question : {NewMessage}");
                    List<ChatMessage> contextCoder = new();
                    contextCoder.Add(userMessage);
                    (isSuccess, response, m) = await ChatService.SendMessageAsync(ModelsUseCases.TypeUseCase.LLM_Coder, contextCoder, conv.Model, IsApiExterne);

                }

            }
            else // Ni RAG, Ni génération d'image, ni CODER
            {
                if (SelectedDocumentsPaths.Count > 0) // avec documents
                {
                    LoggerService.LogInfo($"ChatRoom_VM.SendMessage (LLM et documents) {conv.Model}: {conv.TitleLong}");
                    LoggerService.LogDebug($"{NewMessage}");

                    userMessage.Content = string.Join(", ", SelectedDocumentsListe) + " : " + userMessage.Content;
                    (isSuccess, response, m) = await ChatService.SendMessageAsync(ModelsUseCases.TypeUseCase.LLM, userMessage.Content, conv.Model, SelectedDocumentsPaths.ToList(), IsApiExterne);

                    SelectedDocumentsListe.Clear();
                    SelectedDocumentsPaths.Clear();
                    OnPropertyChanged(nameof(SelectedDocumentsListe));
                    OnPropertyChanged(nameof(SelectedDocumentsPaths));

                }
                else // sans document
                {
                    LoggerService.LogInfo($"ChatRoom_VM.SendMessage (LLM) {conv.Model}: {conv.TitleLong}");
                    LoggerService.LogDebug($"Question : {NewMessage}");

                    (isSuccess, response, m) = await ChatService.SendMessageAsync(ModelsUseCases.TypeUseCase.LLM, context, conv.Model, IsApiExterne);
                }
            }

            ImageURL = "";
            if (IsGenerateImg)
            {
                ImageURL = response; // la réponse est une URL d'image
                response = $"Image générée : {response}";
            }

            var aiMessage = new ChatMessage { Role = "assistant", Content = response, Model = m };
            LoggerService.LogDebug($"Réponse : {response}");

            conv.Messages.Add(aiMessage);
            CurrentMessages.Add(aiMessage);

            ChatService.SaveConversation(conv);
            NewMessage = "";
            OnPropertyChanged(nameof(NewMessage));

            return true;
        }
        #endregion

        #region Méthodes privées
        private async Task<bool> VerifOllama()
        {
            try
            {
                IsOllamaActif = await ReActAgent.IsOllamaActif(true);
                return IsOllamaActif;
            }
            catch (Exception ex)
            {
                LoggerService.LogError($"ChatRoom_VM.VerifOllama : {ex.Message}");
                _Notifier.ShowToast($"Erreur lors de la vérification du service Ollama : {ex.Message}");
                return false;
            }
        }

        private async Task<bool> VerifFooocus()
        {
            try
            {
                var status = await ReActAgent.IsFooocusActif();
                if (!status)
                {
                    _Notifier.ShowToast("Fooocus n'est pas démarré. Veuillez lancer le service Fooocus.");
                    return false;
                }
                return true;
            }
            catch (Exception ex)
            {
                LoggerService.LogError($"ChatRoom_VM.VerifFooocus : {ex.Message}");
                _Notifier.ShowToast($"Erreur lors de la vérification du service Fooocus : {ex.Message}");
                return false;
            }
        }

        private List<string> GetPreconisedModels()
        {
            ModelsUseCases.TypeUseCase useCase;
            if (IsRAG)
            {
                useCase = ModelsUseCases.TypeUseCase.RAG;
            }
            else if (IsGenerateImg)
            {
                useCase = ModelsUseCases.TypeUseCase.PromptGenerationFooocus;
            }
            else if (IsCODER)
            {
                useCase = ModelsUseCases.TypeUseCase.LLM_Coder;
            }
            else
            {
                useCase = ModelsUseCases.TypeUseCase.LLM;
            }
            var lst = ChatService.GetModelesIA(useCase);
            return lst.ToList();
        }

        private async Task GetInstalledModelsAsync()
        {
            try
            {
                var precoModels = GetPreconisedModels();
                var installedModels = await ChatService.GetInstalledModelsAsync();
                InstalledModels.Clear(); //  Utiliser la propriété
                foreach (var preco in precoModels)
                {
                    foreach(var model in installedModels)
                    {
                        if(preco == model.Name)
                            InstalledModels.Add(model);
                    }
                }
                OnPropertyChanged(nameof(InstalledModels));
            }
            catch (Exception ex)
            {
                LoggerService.LogError($"Erreur lors de la récupération des modèles installés : {ex.Message}");
            }
        }

        private List<ChatMessage> BuildContextWindow(List<ChatMessage> fullHistory, int maxTokens = 3500)
        {
            LoggerService.LogInfo("ChatRoom_VM.BuildContextWindow");
            List<ChatMessage> selectedMessages = new();
            int estimatedTokens = 0;

            foreach (var message in fullHistory.AsEnumerable().Reverse())
            {
                int tokens = EstimateTokens(message.Content);
                if (estimatedTokens + tokens > maxTokens)
                {
                    ChatMessage newChatMessage = new();
                    newChatMessage.Content = message.Content.Substring(0, maxTokens);
                    newChatMessage.Timestamp = message.Timestamp;
                    newChatMessage.Role = message.Role;
                    newChatMessage.Model = message.Model;
                    selectedMessages.Insert(0, newChatMessage);
                    LoggerService.LogDebug($"Message raccouci : {maxTokens} au lieu de {tokens}\n{newChatMessage.Content}");
                    break;
                }
                selectedMessages.Insert(0, message);
                estimatedTokens += tokens;
            }

            return selectedMessages;
        }

        private int EstimateTokens(string text) => text.Length / 4;
        #endregion
    }
}
