using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml.Wordprocessing;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.IO;
using A = DocumentFormat.OpenXml.Drawing;
using DW = DocumentFormat.OpenXml.Drawing.Wordprocessing;
using PIC = DocumentFormat.OpenXml.Drawing.Pictures;

namespace ToolsServices
{
    public static class DocxService
    {
        #region Méthodes publiques
        public static string ExtractDocxFromBytes(byte[] bytes)
        {
            using var ms = new MemoryStream(bytes);
            using var doc = WordprocessingDocument.Open(ms, false);

            var body = doc.MainDocumentPart.Document.Body;

            return body.InnerText;
        }


        public static string ExtractTextFromDocx(string fileFullname)
        {
            LoggerService.LogInfo($"DocxService.ExtractTextFromDocx : {fileFullname}");

            using var doc = WordprocessingDocument.Open(fileFullname, false);
            if (doc == null || doc.MainDocumentPart == null || doc.MainDocumentPart.Document == null)
                return string.Empty;

            return string.Join(" ", doc.MainDocumentPart.Document.Descendants<DocumentFormat.OpenXml.Wordprocessing.Text>().Select(t => t.Text));
        }

        public static string ExtractTextFromRtf(string fileFullname)
        {
            LoggerService.LogInfo($"DocxService.ExtractTextFromRtf : {fileFullname}");

            string rtfContent = File.ReadAllText(fileFullname);

            // Supprimer les balises RTF simples
            rtfContent = Regex.Replace(rtfContent, @"\\[a-z]+\d*", string.Empty);
            rtfContent = Regex.Replace(rtfContent, @"{\\[^}]+}", string.Empty);
            rtfContent = Regex.Replace(rtfContent, @"[{}]", string.Empty);
            rtfContent = Regex.Replace(rtfContent, @"\r\n|\n", " ");



            // Convertir les caractères encodés (\'xx) en Unicode
            rtfContent = Regex.Replace(rtfContent, @"\\'([0-9a-fA-F]{2})", match =>
            {
                int value = Convert.ToInt32(match.Groups[1].Value, 16);
                return Encoding.Default.GetString(new byte[] { (byte)value });
            });

            // Remplacer les sauts de ligne RTF
            rtfContent = rtfContent.Replace(@"\par", "\n").Replace(@"\tab", "\t");

            // Supprimer les balises RTF
            rtfContent = Regex.Replace(rtfContent, @"\\[a-zA-Z]+\d*", string.Empty);

            // Supprimer les groupes de styles inutiles
            rtfContent = Regex.Replace(rtfContent, @"{\\[^}]+}", string.Empty);

            // Supprimer les accolades et le reste des balises
            rtfContent = Regex.Replace(rtfContent, @"[{}]", string.Empty);

            // Nettoyage final
            return rtfContent.Trim();


        }

        public static bool Genere_Docx(string fullFilenameTemplate, string fullFilenameXML, string fullFilenameOutputDocx, string titreCV, int nbrMaxCompetences, bool isCVAnonyme)
        {
            LoggerService.LogInfo($"DocxService.Genere_Docx : {fullFilenameOutputDocx}");

            try
            {
                if (System.IO.File.Exists(fullFilenameOutputDocx))
                {
                    System.IO.File.Delete(fullFilenameOutputDocx);
                }

                return CreateDocx(fullFilenameTemplate, fullFilenameXML, fullFilenameOutputDocx, titreCV, nbrMaxCompetences, isCVAnonyme);
            }
            catch (Exception ex)
            {
                LoggerService.LogError($"Erreur dans la génération d'un CV au format Word (DocxService.Genere_Docx) : {ex.Message}");
                return false;
            }
        }
        #endregion

        #region Propriétés privées
        private static string Font_Name = "Calibri";
        private static int Font_Size_int = 11;// "22"; // 11pt = 22 demi-points
        private static string Font_Size
        {
            get
            {
                return (Font_Size_int * 2).ToString(); ;
            }
        }
        private static string Font_Size_Minus
        {
            get
            {
                return ((Font_Size_int - 2) * 2).ToString(); ;
            }
        }
        #endregion

        #region Méthodes privées
        private static bool CreateDocx(string templatePath, string xmlPath, string outputPath, string _TitreCV, int _NbrMaxCompetences, bool isCVAnonyme)
        {
            LoggerService.LogInfo($"DocxService.CreateDocx : {templatePath}");

            try
            {
                if (File.Exists(outputPath))
                    File.Delete(outputPath);

                File.Copy(templatePath, outputPath, true); // Copie le modèle

                #region Lire le XML
                var cv = XDocument.Load(xmlPath).Root;

                var placeholders = new Dictionary<string, string>();

                #region Identité
                var identite = cv!.Element("Identite");
                if(isCVAnonyme)
                {
                    var trigramme = "AAA";
                    if (identite!.Element("Prenom")?.Value.Length > 1)
                    {
                        trigramme = identite!.Element("Prenom")?.Value.Substring(0, 1).ToUpper();
                    }
                    if (identite!.Element("Nom")?.Value.Length > 2)
                    {
                        trigramme += identite!.Element("Nom")?.Value.Substring(0, 2).ToUpper();
                    }

                    placeholders["{{Nom}}"] = trigramme!;
                    placeholders["{{Prenom}}"] = "";
                }
                else
                {
                    placeholders["{{Nom}}"] = identite!.Element("Nom")?.Value ?? "";
                    placeholders["{{Prenom}}"] = identite!.Element("Prenom")?.Value ?? "";
                }
                placeholders["{{Email}}"] = identite!.Element("Email")?.Value ?? "";
                placeholders["{{Telephone}}"] = identite!.Element("Telephone")?.Value ?? "";
                #endregion

                #region profil
                // Profil : première phrase en gras, le reste plus petit sur lignes séparées
                string profil = cv.Element("Profil")?.Value ?? "";
                string profilAccroche = "";
                string profilDetails = "";

                var phrases = profil.Split('.').Select(p => p.Trim()).Where(p => !string.IsNullOrEmpty(p)).ToList();
                if (phrases.Count > 0)
                {
                    if (_TitreCV != "")
                    {
                        profilAccroche = _TitreCV;
                        profilDetails = string.Join("\n", phrases.Select(p => p));
                    }
                    else
                    {
                        profilAccroche = phrases[0];
                        profilDetails = string.Join("\n", phrases.Skip(1).Select(p => p));
                    }

                }
                #endregion

                #region Langues
                var langues = cv.Element("Langues")?.Elements("Langue") ?? Enumerable.Empty<XElement>();
                var languesTextList = langues.Select(l => $"{l.Attribute("nom")?.Value} ({l.Attribute("niveau")?.Value})").ToList();
                string languesText = string.Join(", ", languesTextList);
                #endregion

                #region Formations
                var formations = cv.Element("Formations")?.Elements("Formation") ?? Enumerable.Empty<XElement>();
                var formationsTextList = formations.Select(f => $"{f.Element("Diplome")?.Value} à {f.Element("Etablissement")?.Value} ({f.Element("DateFin")?.Value})").ToList();
                string formationsText = string.Join("\n", formationsTextList);
                #endregion

                #region Certifications
                var certifications = cv.Element("Certifications")?.Elements("Certification") ?? Enumerable.Empty<XElement>();
                var certificationsTextList = formations.Select(f => $"{f.Element("Nom")?.Value} à {f.Element("Organisme")?.Value} ({f.Element("Date")?.Value})").ToList();
                //var certificationsTextList = formations.Select(f => $"{f.Element("Nom")?.Value}").ToList();
                string certificationsText = string.Join("\n", certificationsTextList);
                #endregion

                #region Compétences : x premières
                var competences = cv.Element("Competences")?
                    .Elements("Competence")
                    .Select(c => c.Attribute("nom")?.Value)
                    .Where(n => !string.IsNullOrEmpty(n))
                    .Take(_NbrMaxCompetences)
                    .ToList() ?? new List<string>()!;
                string competencesText = string.Join(", ", competences);
                #endregion

                #region Expériences : chaque bloc avec première ligne en gras
                var experiences = cv.Element("Experiences")?.Elements("Experience") ?? Enumerable.Empty<XElement>();
                var xpParagraphs = new List<Paragraph>();
                foreach (var xp in experiences)
                {
                    string datefin = xp.Element("DateFin")?.Value! != "" ? xp.Element("DateFin")?.Value! : "Aujourd’hui";
                    string titre = $"{xp.Element("Poste")?.Value} chez {xp.Element("Entreprise")?.Value} à {xp.Element("Lieu")?.Value}";
                    string dates = $"{xp.Element("DateDebut")?.Value} à {datefin}";
                    string desc = xp.Element("Description")?.Value ?? "";

                    var paraTitre = new Paragraph(new DocumentFormat.OpenXml.Wordprocessing.Run(new DocumentFormat.OpenXml.Wordprocessing.Text(titre)));
                    paraTitre.GetFirstChild<DocumentFormat.OpenXml.Wordprocessing.Run>()!.RunProperties = new DocumentFormat.OpenXml.Wordprocessing.RunProperties(
                        new RunFonts() { Ascii = Font_Name, HighAnsi = Font_Name },
                        new DocumentFormat.OpenXml.Wordprocessing.FontSize() { Val = Font_Size },
                        new DocumentFormat.OpenXml.Wordprocessing.Bold()
                        );

                    var runDates = new DocumentFormat.OpenXml.Wordprocessing.Run(new DocumentFormat.OpenXml.Wordprocessing.Text(dates));
                    runDates.RunProperties = new DocumentFormat.OpenXml.Wordprocessing.RunProperties(
                        new RunFonts() { Ascii = Font_Name, HighAnsi = Font_Name },
                        new DocumentFormat.OpenXml.Wordprocessing.FontSize() { Val = Font_Size },
                        new DocumentFormat.OpenXml.Wordprocessing.Italic()
                    );
                    var paraDates = new Paragraph(runDates);

                    var runDesc = new DocumentFormat.OpenXml.Wordprocessing.Run(new DocumentFormat.OpenXml.Wordprocessing.Text(desc));
                    runDesc.RunProperties = new DocumentFormat.OpenXml.Wordprocessing.RunProperties(
                        new RunFonts() { Ascii = Font_Name, HighAnsi = Font_Name },
                        new DocumentFormat.OpenXml.Wordprocessing.FontSize() { Val = Font_Size_Minus }
                    );
                    var paraDesc = new Paragraph(runDesc);

                    xpParagraphs.Add(paraTitre);
                    xpParagraphs.Add(paraDates);
                    xpParagraphs.Add(paraDesc);
                    xpParagraphs.Add(new Paragraph(new DocumentFormat.OpenXml.Wordprocessing.Run(new DocumentFormat.OpenXml.Wordprocessing.Text("")))); // Ligne vide entre les expériences
                }
                #endregion

                #endregion

                #region Ecrire dans le document
                using (var doc = WordprocessingDocument.Open(outputPath, true))
                {
                    var body = doc!.MainDocumentPart!.Document.Body;

                    #region Ajouter un style de puces s’il n'existe pas
                    var numberingPart = doc!.MainDocumentPart!.NumberingDefinitionsPart;
                    if (numberingPart == null)
                        numberingPart = doc.MainDocumentPart.AddNewPart<NumberingDefinitionsPart>();

                    numberingPart.Numbering = new Numbering(
                        new AbstractNum(
                            new Level(
                                new DocumentFormat.OpenXml.Wordprocessing.NumberingFormat() { Val = NumberFormatValues.Bullet },
                                new LevelText() { Val = "•" },
                                new LevelJustification() { Val = LevelJustificationValues.Left }
                            )
                            { LevelIndex = 0 }
                        )
                        { AbstractNumberId = 1 },

                        new NumberingInstance(
                            new AbstractNumId() { Val = 1 }
                        )
                        { NumberID = 1 }
                    );

                    #endregion

                    #region Profil (mise en forme particulière)
                    foreach (var para in body!.Descendants<Paragraph>())
                    {
                        if (para.InnerText.Contains("{{Profil}}"))
                        {
                            para.RemoveAllChildren<DocumentFormat.OpenXml.Wordprocessing.Run>();

                            var runBold = new DocumentFormat.OpenXml.Wordprocessing.Run(new DocumentFormat.OpenXml.Wordprocessing.Text(profilAccroche));
                            runBold.RunProperties = new DocumentFormat.OpenXml.Wordprocessing.RunProperties(
                                new RunFonts() { Ascii = Font_Name, HighAnsi = Font_Name },
                                new DocumentFormat.OpenXml.Wordprocessing.FontSize() { Val = "24" },
                                new DocumentFormat.OpenXml.Wordprocessing.Bold()
                                );

                            para.Append(runBold);

                            foreach (var line in profilDetails.Split('\n'))
                            {
                                var run = new DocumentFormat.OpenXml.Wordprocessing.Run(new DocumentFormat.OpenXml.Wordprocessing.Text(line));
                                var smallPara = new Paragraph(run);
                                var props = new DocumentFormat.OpenXml.Wordprocessing.RunProperties(new RunFonts() { Ascii = Font_Name, HighAnsi = Font_Name }, new DocumentFormat.OpenXml.Wordprocessing.FontSize { Val = "18" }); // 9pt
                                smallPara!.GetFirstChild<DocumentFormat.OpenXml.Wordprocessing.Run>()!.RunProperties = props;
                                smallPara!.ParagraphProperties = new ParagraphProperties(new Justification { Val = JustificationValues.Center });
                                para.InsertAfterSelf(smallPara);
                            }

                            break;
                        }
                    }
                    #endregion

                    #region Experiences
                    foreach (var para in body.Descendants<Paragraph>().ToList())
                    {
                        if (para.InnerText.Contains("{{Experiences}}"))
                        {
                            var parent = para.Parent;
                            foreach (var xpPara in xpParagraphs)
                            {
                                parent!.InsertBefore(xpPara.CloneNode(true), para);
                            }
                            para.Remove();
                            break;
                        }
                    }
                    #endregion 

                    #region Langues
                    if (body.InnerText.Contains("{{Langues}}"))
                    {
                        var paraToReplace = body.Descendants<Paragraph>()
                                                .FirstOrDefault(p => p.InnerText.Contains("{{Langues}}"));
                        if (paraToReplace != null)
                        {
                            var parent = paraToReplace.Parent;
                            var lstLangues = cv.Element("Langues")?.Elements("Langue")
                                            .Select(l =>
                                            {
                                                string sReturn = "";
                                                string nom = l.Attribute("nom")?.Value ?? "";
                                                string niveau = l.Attribute("niveau")?.Value ?? "";

                                                sReturn = $"{nom}";
                                                if (niveau != null && niveau != "") sReturn += $" ({niveau})";
                                                return sReturn;
                                            })
                                            .ToList() ?? new List<string>();


                            foreach (var langue in lstLangues)
                            {
                                var run = new DocumentFormat.OpenXml.Wordprocessing.Run(new DocumentFormat.OpenXml.Wordprocessing.Text(langue));
                                run.RunProperties = new DocumentFormat.OpenXml.Wordprocessing.RunProperties(
                                    new RunFonts() { Ascii = Font_Name, HighAnsi = Font_Name },
                                    new DocumentFormat.OpenXml.Wordprocessing.FontSize() { Val = Font_Size } // 11pt = 22 demi-points
                                );

                                var para = new Paragraph(
                                    new ParagraphProperties(
                                        new NumberingProperties(
                                            new NumberingLevelReference() { Val = 0 },
                                            new NumberingId() { Val = 1 })
                                    ),
                                    run
                                );
                                parent!.InsertAfter(para, paraToReplace);
                            }

                            paraToReplace.Remove();
                        }
                    }
                    #endregion

                    #region Formations
                    if (body.InnerText.Contains("{{Formations}}"))
                    {
                        var paraToReplace = body.Descendants<Paragraph>()
                                                .FirstOrDefault(p => p.InnerText.Contains("{{Formations}}"));
                        if (paraToReplace != null)
                        {
                            var parent = paraToReplace.Parent;
                            var lstFormations = cv.Element("Formations")?.Elements("Formation")
                                               .Select(f =>
                                               {
                                                   string sReturn = "";

                                                   string intitule = f.Element("Diplome")?.Value ?? "";
                                                   string lieu = f.Element("Etablissement")?.Value ?? "";
                                                   string annee = f.Element("DateFin")?.Value ?? "";
                                                   sReturn = $"{intitule}";
                                                   if (lieu != null && lieu != "") sReturn += $" - {lieu}";
                                                   if (annee != null && annee != "") sReturn += $" ({annee})";
                                                   return sReturn;// $"{intitule} - {lieu} ({annee})";
                                               })
                                               .ToList() ?? new List<string>();

                            foreach (var formation in lstFormations)
                            {
                                var run = new DocumentFormat.OpenXml.Wordprocessing.Run(new DocumentFormat.OpenXml.Wordprocessing.Text(formation));
                                run.RunProperties = new DocumentFormat.OpenXml.Wordprocessing.RunProperties(
                                    new RunFonts() { Ascii = Font_Name, HighAnsi = Font_Name },
                                    new DocumentFormat.OpenXml.Wordprocessing.FontSize() { Val = Font_Size } // 11pt = 22 demi-points
                                );

                                var para = new Paragraph(
                                    new ParagraphProperties(
                                        new NumberingProperties(
                                            new NumberingLevelReference() { Val = 0 },
                                            new NumberingId() { Val = 1 })
                                    ),
                                    run
                                );
                                parent!.InsertAfter(para, paraToReplace);
                            }

                            paraToReplace.Remove();
                        }
                    }
                    #endregion

                    #region Certifications
                    if (body.InnerText.Contains("{{Certifications}}"))
                    {
                        var paraToReplace = body.Descendants<Paragraph>()
                                                .FirstOrDefault(p => p.InnerText.Contains("{{Certifications}}"));
                        if (paraToReplace != null)
                        {
                            var parent = paraToReplace.Parent;
                            var lstCertifications = cv.Element("Certifications")?.Elements("Certification")
                                               .Select(f =>
                                               {
                                                   string sReturn = "";
                                                   string intitule = f.Element("Nom")?.Value ?? "";
                                                   string lieu = f.Element("Organisme")?.Value ?? "";
                                                   string annee = f.Element("Date")?.Value ?? "";

                                                   sReturn = $"{intitule}";
                                                   if (lieu != null && lieu != "") sReturn += $" - {lieu}";
                                                   if (annee != null && annee != "") sReturn += $" ({annee})";
                                                   return sReturn;
                                               })
                                               .ToList() ?? new List<string>();

                            foreach (var certification in lstCertifications)
                            {
                                var run = new DocumentFormat.OpenXml.Wordprocessing.Run(new DocumentFormat.OpenXml.Wordprocessing.Text(certification));
                                run.RunProperties = new DocumentFormat.OpenXml.Wordprocessing.RunProperties(
                                    new RunFonts() { Ascii = Font_Name, HighAnsi = Font_Name },
                                    new DocumentFormat.OpenXml.Wordprocessing.FontSize() { Val = Font_Size } // 11pt = 22 demi-points
                                );

                                var para = new Paragraph(
                                    new ParagraphProperties(
                                        new NumberingProperties(
                                            new NumberingLevelReference() { Val = 0 },
                                            new NumberingId() { Val = 1 })
                                    ),
                                    run
                                );
                                parent!.InsertAfter(para, paraToReplace);
                            }

                            paraToReplace.Remove();
                        }
                    }
                    #endregion

                    #region Le reste
                    foreach (var para in body!.Descendants<Paragraph>())
                    {
                        foreach (var run in para.Descendants<DocumentFormat.OpenXml.Wordprocessing.Run>())
                        {
                            /*
                            run.RunProperties = new RunProperties(
                                new RunFonts() { Ascii = Font_Name, HighAnsi = Font_Name },
                                new FontSize() { Val = Font_Size });
                            */
                            var text = run.GetFirstChild<DocumentFormat.OpenXml.Wordprocessing.Text>();
                            if (text == null) continue;

                            if (text.Text.Contains("{{Nom}}")) text.Text = text.Text.Replace("{{Nom}}", placeholders["{{Nom}}"]);
                            if (text.Text.Contains("{{Prenom}}")) text.Text = text.Text.Replace("{{Prenom}}", placeholders["{{Prenom}}"]);
                            if (text.Text.Contains("{{Telephone}}")) text.Text = text.Text.Replace("{{Telephone}}", placeholders["{{Telephone}}"]);
                            if (text.Text.Contains("{{Email}}")) text.Text = text.Text.Replace("{{Email}}", placeholders["{{Email}}"]);
                            if (text.Text.Contains("{{Competences}}")) text.Text = text.Text.Replace("{{Competences}}", competencesText);
                            //if (text.Text.Contains("{{Formations}}")) text.Text = text.Text.Replace("{{Formations}}", formationsText);
                            //if (text.Text.Contains("{{Langues}}")) text.Text = text.Text.Replace("{{Langues}}", languesText);
                        }
                    }
                    #endregion

                    doc.MainDocumentPart.Document.Save();
                }
                #endregion

                return true;
            }
            catch (Exception ex)
            {
                LoggerService.LogError($"Erreur dans la génération d'un CV au format Word (DocxService.CreateDocx) : {ex.Message}");
                return false;
            }
        }

        #endregion
    }
}
