using Reranker;
using System.Text;
using ToolsServices;

namespace Services
{

    public static class RAGService
    {
        #region Variables
        private static Services.ReActAgent.ReActAgent _Agent = new();
        private static string NomFichierData = FichiersInternesService.ParamsRAG;//"paramsRAG.txt";
        #endregion

        #region Méthodes publiques
        
        #region Gestion des paramètres
        public static ParametresRAGService LoadParametres()
        {
            LoggerService.LogInfo("RAGService.LoadParametres");

            ParametresRAGService SelectedItem = new();

            try
            {
                if (File.Exists(NomFichierData))
                {

                    string[] lignes = File.ReadAllLines(NomFichierData);

                    if (lignes.Length > 0)
                        SelectedItem.RAG_Path_ToVectoralize = lignes[0];

                    if (lignes.Length > 1)
                        SelectedItem.RAG_Path_Vectoralized = lignes[1];

                }
                return SelectedItem;

            }
            catch
            {
                return new();
            }
        }

        public static bool SaveParametres(ParametresRAGService selectedItem)
        {
            LoggerService.LogInfo("RAGService.SaveParametres");

            try
            {
                StringBuilder sb = new();
                sb.AppendLine(selectedItem.RAG_Path_ToVectoralize);
                sb.AppendLine(selectedItem.RAG_Path_Vectoralized);

                File.WriteAllText(NomFichierData, sb.ToString());

                return true;
            }
            catch
            {
                return false;
            }

        }
        #endregion

        private static async Task ChargementDocuments(QdrantService qdrant, bool isRAZCollections)
        {
            LoggerService.LogInfo("RAGService.ChargementDocuments");

            ParametresRAGService? ragParam = LoadParametres();
            // Chargement documents
            await IngestDocuments(ragParam.RAG_Path_ToVectoralize, ragParam.RAG_Path_Vectoralized, qdrant, isRAZCollections);

        }

        /*
        public static async Task<(bool, string, string)> RechercheDansDocuments(string query, string model) 
        {
            LoggerService.LogInfo("RAGService.RechercheDansDocuments");

            await ChargementDocuments();

            // Cela inclut pour quey : le split en chunck + tokenisation + vectorisation 
            LoggerService.LogDebug("Debut de RAGService.RechercheDansDocuments");
            var (reponse, m) = await _Agent.RechercheRAG(query, "Recherche RAG", model);
            LoggerService.LogDebug("Fin de RAGService.RechercheDansDocuments");
            return (true, reponse, m);
        }
        */
        public static async Task<string> SaveDocumentAsync(byte[] data, string fileName, Domain domain)
        {
            ParametresRAGService? ragParam = LoadParametres();
            // Sécurisation du filename pour éviter ../ etc.
            var safeName = Path.GetFileName(fileName);

            // Chemin final
            var fullPath = Path.Combine(ragParam.RAG_Path_ToVectoralize, domain.ToCollectionName(), safeName);

            // Écriture du fichier
            await File.WriteAllBytesAsync(fullPath, data);

            return fullPath;
        }
        /// <summary>
        /// Recherche dans la base vectorielle Qdrant
        /// </summary>
        /// <param name="query"></param>
        /// <param name="model"></param>
        /// <returns></returns>
        public static async Task<(bool, string, string)> RechercheDansDocuments(Domain domain, string query, string model, bool isRAZCollections)
        {
            LoggerService.LogInfo("RAGService.RechercheDansDocuments");
            var qdrant = new QdrantService();
            await ChargementDocuments(qdrant, isRAZCollections);

            // Cela inclut pour quey : le split en chunck + tokenisation + vectorisation 
            LoggerService.LogDebug("Debut de RAGService.RechercheDansDocuments");
            (bool b, float[]? queryEmbedding) = await _Agent.GetEmbeddingAsync(query, true);
            if(b && queryEmbedding != null)
            {
                LoggerService.LogDebug("Embedding de la requête OK");
            }
            else
            {
                LoggerService.LogWarning("Problème lors de la vectorisation de la requête");
                return (false, "Problème lors de la vectorisation de la requête", "");
            }

            var results = await qdrant.SearchAsync(domain, queryEmbedding, 0.1f, topK: 20);
            /*
            var retrievedChunks = results
                .Select(r => (r.Text + " - source : " + r.Nom_Fichier))
                .ToArray();
            */

            // --- Reranking : Charger tokenizer et cross-encoder ONNX
            //var reranker = new BgeReranker();
            var ranked = BgeReranker.Rerank(query, results, topK: 5);

            if (ranked.Count() > 0)
            {
                //var (reponse, m) = await _Agent.RechercheRAG(query, "Recheche RAG", model, retrievedChunks);
                var (reponse, m) = await _Agent.RechercheRAG(query, "Recheche RAG", model, ranked, domain.ToCollectionName());
                LoggerService.LogDebug("Fin de RAGService.RechercheDansDocuments avec demande au LLM");
                return (true, reponse, m);
            }
            else
            {
                LoggerService.LogDebug("Fin de RAGService.RechercheDansDocuments sans demande au LLM");
                return (true, "Pas de réponse contenue dans la base vectorielle", "Pas de LLM sollicité");
            }
        }
        
        /// <summary>
        /// Recherche dans les collections CV ou Emails
        /// </summary>
        /// <param name="domain"></param>
        /// <param name="query"></param>
        /// <returns></returns>
        public static async Task<string> Search(Domain domain, string query, string adresseMail="")
        {
            LoggerService.LogInfo("RAGService.Search");
            var qdrant = new QdrantService();
            //await ChargementDocuments(qdrant, false);

            // Cela inclut pour quey : le split en chunck + tokenisation + vectorisation 
            (bool b, float[]? queryEmbedding) = await _Agent.GetEmbeddingAsync(query, true);
            if (b && queryEmbedding != null)
            {
                LoggerService.LogDebug("Embedding de la requête OK");
            }
            else
            {
                LoggerService.LogWarning("Problème lors de la vectorisation de la requête");
                return ("");
            }
            StringBuilder sb = new();

            var results = await qdrant.SearchAsync(domain, queryEmbedding,0.1f, topK: 20, adresseMail: adresseMail);
            /*
            var retrievedChunks = results
                .Select(r => r.Text)
                .ToArray();
            */
            var ranked = BgeReranker.Rerank(query, results, topK: 5);

            if (ranked.Count() > 0)
            {
                foreach (var entry in ranked)
                {
                    sb.AppendLine($"Document: {entry.Nom_Fichier}");
                    /*
                    if (entry.Payload.TryGetValue("nom_fichier", out var value2))
                    {
                        if (value2.StringValue != "")
                        {
                            sb.AppendLine($"Email: {value2.StringValue}");
                        }
                    }
                    */
                    sb.AppendLine("------");
                }
                return (sb.ToString());
            }
            else
            {
                LoggerService.LogDebug("Fin de RAGService.RechercheDansDocuments sans demande au LLM");
                return ("");
            }
        }

        // Documents pas forcément RAG donc Domain en fonction du contexte
        public static async Task<bool> IngestDocument(Domain domain,bool isOnDemand, bool isRAZCollections, string content, string filename, string emailObject = "", string proprietaire = "", string niveauAcces="")
        {
            LoggerService.LogInfo("RAGService.IngestDocument");

            try
            {
                QdrantService qdrant = new();
                var chunks = ChunkText(content);

                await qdrant.InitializeCollectionsAsync(isRAZCollections);

                for (int i = 0; i < chunks.Length; i++)
                {
                    var chunk = chunks[i];
                    (bool b, float[]? embedding) = await _Agent.GetEmbeddingAsync(chunk, isOnDemand);
                    if (b && embedding != null)
                    {
                        var id = $"{Path.GetFileName(filename)}_chunk_{i}";
                        await qdrant.IngestDocument(domain, embedding, filename, id, chunk, emailObject, proprietaire, niveauAcces);
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                LoggerService.LogError($"RAGService.IngestDocument : {ex.Message}");
                return false;
            }
        }

        public static string[] ChunkText(string text, int chunkSize = 250, int overlap = 50)
        {
            var words = text.Split(' ', StringSplitOptions.RemoveEmptyEntries);
            var chunks = new List<string>();
            /*
            for (int i = 0; i < words.Length; i += (chunkSize - overlap))
            {
                var chunkWords = words.Skip(i).Take(chunkSize);
                chunks.Add(string.Join(' ', chunkWords));
            }
            */
            int start = 0;

            while (start < words.Length)
            {
                int length = Math.Min(chunkSize, words.Length - start);
                var chunk = string.Join(' ', words.Skip(start).Take(length));
                chunks.Add(chunk);
                start += chunkSize - overlap;
            }

            return chunks.ToArray();
        }
        #endregion

        #region Méthodes privées
        // Pour l'instant Documents RAG 
        private static async Task<bool> IngestDocuments(string pathDocs, string pathDestinationDocs, QdrantService qdrant, bool isRAZCollections)
        {
            LoggerService.LogInfo("RAGService.IngestDocuments");

            // boucler sur les documents et envoyer à _Agent.IngestDocumentAsync
            // Cela inclut le split en chunck + tokenisation + vectorisation + sauvegarde des vecteurs
            try
            {
                if (!System.IO.Directory.Exists(pathDocs))
                {
                    LoggerService.LogWarning($"Le dossier n'existe pas : {pathDocs}");
                    return false;
                }

                await qdrant.InitializeCollectionsAsync(isRAZCollections);


                List<string> filesToDeplace = new();
                var files = Directory.GetFiles(pathDocs, "*.*", SearchOption.AllDirectories);
                foreach (var file in files)
                {
                    var relativePath = System.IO.Path.GetRelativePath(pathDocs, file);
                    var content = FilesService.ExtractText(file);
                    if (content != string.Empty)
                    {
                        var domain = DomainExtensions.GetDomainByPath(file);
                        // Si ce document y est déjà, le contenu de la base vectorielle sera supprimé avant d'être ingéré de nouveau
                        await qdrant.DeleteDocumentInQdrant(domain, Path.GetFileName(file));

                        var chunks = ChunkText(content);

                        for (int i = 0; i < chunks.Length; i++)
                        {
                            var chunk = chunks[i];
                            (bool b, float[]? embedding) = await _Agent.GetEmbeddingAsync(chunk, true);
                            if (b && embedding != null)
                            {


                                var id = $"{Path.GetFileName(file)}_chunk_{i}";
                                await qdrant.IngestDocument(domain, embedding, Path.GetFileName(file), id, chunk);
                            }
                        }
                        filesToDeplace.Add(file);
                    }
                }
                
                foreach (var file in filesToDeplace)
                {
                    // on déplace les fichiers une fois vectorisés
                    // Chemin relatif pour recréer les sous-dossiers
                    var relativePath = System.IO.Path.GetRelativePath(pathDocs, file);
                    var destPath = System.IO.Path.Combine(pathDestinationDocs, relativePath);

                    // Crée le dossier de destination si nécessaire
                    var destDir = System.IO.Path.GetDirectoryName(destPath);
                    if (destDir != null && !Directory.Exists(destDir))
                    {
                        Directory.CreateDirectory(destDir);
                    }

                    // Déplace le fichier
                    if(System.IO.File.Exists(file))
                        File.Move(file, destPath, overwrite: true);
                }

                LoggerService.LogDebug($"Fin de RAGService.IngestDocument : {files.Length} document(s)");

                return true;
            }
            catch (Exception ex)
            {
                LoggerService.LogError($"RAGService.IngestDocument : {ex.Message}");
                return false;
            }
        }

        #endregion
    }
}
