using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Reflection;
using System.IO;

using SharpSevenZip;
using Microsoft.Office.Interop.Word;
using CCNetWrapper;

using _Application = Microsoft.Office.Interop.Word._Application;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using DocumentFormat.OpenXml.Office.Word;
using System.Xml.Serialization;
using System.Xml;
using DocumentFormat.OpenXml.Packaging;


namespace CCNetLogReader
{
    public partial class Publish : Form
    {
        MainWindow parent { get; set; }
        Configuration configuration = new Configuration();
        Configuration.SoftwarePackage packageConfig;
        string PublishVersion = "";

        public Publish(MainWindow mainWindowParent, Configuration configuration)
        {
            InitializeComponent();
            parent = mainWindowParent;
            this.configuration = configuration;
        }

        private void Publish_Load(object sender, EventArgs e)
        {
            PublishVersion = parent._versionNumber;
            packageConfig = configuration.SoftwarePackages
                .First(a => a.ProjectName == parent._projName);
            lblProjectName.Text = packageConfig.Name;
            lblVersion.Text = $"{parent._projName} | {parent._versionNumber}";
            lblPriorVersion.Text = $"{packageConfig.PublishedBuild} ({packageConfig.PublishedDate.ToString("yyyy-MM-dd")})";

            lblHCArticle.Text = $"https://support.dtsweb.com/hc/en-us/articles/{packageConfig.ZDArticleID}";
            lblBuildFolder.Text = Path.Combine(
                packageConfig.ProjectBuildFolder,
                PublishVersion
                );
            lblLocalPublish.Text = Path.Combine(
                configuration.LocalPublishRoot,
                packageConfig.Name
                );

            /* Steps to publish:
             * choose project and version
             * if there is a config in xml, enable publish command
             * on publish command:
             * determine last release
             * build release notes since then
             * insure build dir contains software
             * zip build folder to local publish
             * place release notes in local publish
             * upload zip+pdf to remote
             * update zendesk notes
             * check consistency
             * delete old published builds
             */
        }

        private static ManualResetEvent mre = new ManualResetEvent(false);
        private void ThreadSpinWait(int ms, ManualResetEvent wait = null)
        {
            if (wait == null)
            {
                // Use our own
                wait = new ManualResetEvent(false);
            }
            System.Threading.Tasks.Task.Run(() =>
            {
                wait.WaitOne(ms);
                wait.Set();
            });
            wait.WaitOne();
        }
        private void btnPublish_Click(object sender, EventArgs e)
        {
            var localPublishFolder = Path.Combine(
                configuration.LocalPublishRoot,
                packageConfig.Name
                );

            // Insure LocalPublish Folders exist
            dgvTasks.Rows.Add("Prepare", ".");
            if (!Directory.Exists(localPublishFolder))
                Directory.CreateDirectory(localPublishFolder);

            var existingLocalPublishFiles = Directory.GetFiles(localPublishFolder);

            var remoteBuildFolder = Path.Combine(
                packageConfig.ProjectBuildFolder,
                PublishVersion
                );
            dgvTasks.Rows[0].Cells[1].Value = "Complete";

            //var tasks = new List<System.Threading.Tasks.Task>();
            var tasks = new List<System.Threading.Tasks.Task>();

            // Zip Build Folder to local publish folder
            dgvTasks.Rows.Add("Zip Installer", ".");

            var localZipFileName = Path.Combine(localPublishFolder, $"DataPRO_Installer-{PublishVersion}.zip");
            if (File.Exists(localZipFileName))
                File.Delete(localZipFileName);

            tasks.Add( System.Threading.Tasks.Task.Factory.StartNew(() =>
                ZipFolderToLocation(remoteBuildFolder, localZipFileName))
            );

            // Build Release Notes from template to publish folder
            dgvTasks.Rows.Add("Build Release Notes", ".");
            
            var localDocFileName = Path.Combine(localPublishFolder, $"DataPRO_Release_Notes-{PublishVersion}.pdf");

            tasks.Add( System.Threading.Tasks.Task.Factory.StartNew(() =>
                CreateReleaseNotesToFile(localDocFileName))
            );

            System.Threading.Tasks.Task.WaitAll(tasks.ToArray());
            tasks.Clear();

            // NDB DEBUG
            //return;

            // Upload to S3 Bucket
            dgvTasks.Rows.Add("Upload Installer", ".");
            
            var s3 = new s3Client(
                new s3Config()
                {
                    accessKey = configuration.S3Config.AccessKey,
                    secretKey = configuration.S3Config.SecretKey,
                    bucket = configuration.S3Config.Bucket,
                    cloudfrontDomainName = configuration.S3Config.CloudfrontDomain,
                    region = configuration.S3Config.Region,
                });
            tasks.Add( System.Threading.Tasks.Task.Factory.StartNew(() =>
                s3.uploadAndClearFolder(packageConfig.Name, new List<string>() { localZipFileName, localDocFileName }))
            );
            System.Threading.Tasks.Task.WaitAll(tasks.ToArray());

            dgvTasks.Rows[3].Cells[1].Value = "Complete";

            // Update ZD Article:
            dgvTasks.Rows.Add("Update HC Article", ".");
            var zdc = new ZendeskClient();
            var SWURL = configuration.S3Config.CloudfrontDomain
                + packageConfig.Name;
            var PDFURL = SWURL;
            SWURL = SWURL + "/" + Path.GetFileName(localZipFileName);
            PDFURL = PDFURL + "/" + Path.GetFileName(localDocFileName);
            SWURL = $"<a href={SWURL}>{Path.GetFileName(localZipFileName)}</a>";
            PDFURL = $"<a href={PDFURL}>{Path.GetFileName(localDocFileName)}</a>";

            var zdText = packageConfig.ZDArticleText;
            zdText = zdText
                .Replace("{{SWURL}}", SWURL)
                .Replace("{{PDFURL}}", PDFURL)
                .Replace("{{VERSION}}", PublishVersion);

            zdc.UpdateArticle(packageConfig.ZDArticleID, zdText);
            dgvTasks.Rows[4].Cells[1].Value = "Complete";

            // Remove prior build from local publish folder
            foreach (var f in existingLocalPublishFiles)
            {
                if (f != localDocFileName && f != localZipFileName)
                    File.Delete(f);
            }
        }

        public void ZipFolderToLocation(string sourceFolder, string destFile)
        {
            SharpSevenZipBase.SetLibraryPath(
                Path.Combine(
                    Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
                    "7z.dll")
                );

            SharpSevenZipCompressor compressor = new SharpSevenZipCompressor()
            {
                ArchiveFormat = OutArchiveFormat.Zip,
                TempFolderPath = Path.GetTempPath(),
                CompressionMode = CompressionMode.Create,
                CompressionLevel = CompressionLevel.Normal,
                CompressionMethod = CompressionMethod.Lzma,
                ZipEncryptionMethod = ZipEncryptionMethod.Aes256,
                EventSynchronization = EventSynchronizationStrategy.AlwaysAsynchronous,
                FastCompression = false,
            };

            compressor.FileCompressionStarted += (s, e) =>
            {
                dgvTasks.Rows[1].Cells[1].Value = $"{e.PercentDone}% | {e.FileName}";
            };
            compressor.CompressionFinished += (s, e) =>
            {
                dgvTasks.Rows[1].Cells[1].Value = "Complete";
            };

              compressor.CompressDirectory(sourceFolder, destFile);
        }

        public void CreateReleaseNotesToFile(string docFileName)
        {
            var templateFile = packageConfig.ReleaseNoteDocxTemplate;
            var newtfName = Path.GetFileName(templateFile);
            var tfFolder = Path.GetDirectoryName(templateFile);

            if (File.Exists(templateFile))
            {
                if (File.Exists(docFileName))
                    File.Delete(docFileName);
                newtfName = Path.Combine(tfFolder, "newdoc.doc");
                File.Copy(templateFile, newtfName, overwrite: true);
            }
            else
                return;

            templateFile = newtfName;

            string projectHeading = "";
            object oMissing = System.Reflection.Missing.Value;
            object oFileName = templateFile;
            object oEndOfDoc = "\\endofdoc"; /* \endofdoc is a predefined bookmark */
            object oStartofDoc = "\\startofdoc"; /* \startofdoc is a predefined bookmark */
            object oProjectName = new object();

            var wd = WordprocessingDocument.CreateFromTemplate(templateFile);

            //Start Word and create a new document.
            _Application oWord;
            _Document oDoc;
            try
            {
                dgvTasks.Rows[2].Cells[1].Value = "Starting Word";
                oWord = new Microsoft.Office.Interop.Word.Application();
                oWord.Visible = true;
            }
            catch
            {
                MessageBox.Show("An error occured opening Word.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            try
            {
                dgvTasks.Rows[2].Cells[1].Value = "Opening Template";
                oDoc = oWord.Documents.Open(ref oFileName, ref oMissing, ref oMissing, ref oMissing,
                            ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing,
                            ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"An error occured opening the release notes DOCX file: {ex.ToString()}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                oWord.Quit(ref oMissing, ref oMissing, ref oMissing);
                return;
            }

            var wdHead = wd.MainDocumentPart.Document;
            var wdPara = wdHead.Body.Descendants<DocumentFormat.OpenXml.Wordprocessing.Paragraph>().First();

            //Heading paragraph
            dgvTasks.Rows[2].Cells[1].Value = "Creating Document";
            Paragraph oPara1;
            Object oStartRange = oDoc.Bookmarks.get_Item(ref oStartofDoc).Range;
            oPara1 = oDoc.Content.Paragraphs.Add(ref oStartRange);
            object oStyle = WdBuiltinStyle.wdStyleHeading1;  // set HEADING1 built-in style
            oPara1.Range.set_Style(ref oStyle);
            oPara1.Range.Text = projectHeading + " " + PublishVersion +
                " – Released " + DateTime.Now.ToString("yyyy-MM-dd") + "\n";

            var wdParagraph = new DocumentFormat.OpenXml.Wordprocessing.Paragraph();
            var textRun = new DocumentFormat.OpenXml.Wordprocessing.Run();
            //textRun.

            //wdPara.Elements().First(). = projectHeading + " " + PublishVersion +
            //    " – Released " + DateTime.Now.ToString("yyyy-MM-dd") + "\n";

            //Release notes paragraph
            Paragraph oPara2;
            oStartRange = oPara1.Range;
            oPara2 = oDoc.Content.Paragraphs.Add(ref oStartRange);
            oStyle = WdBuiltinStyle.wdStyleListNumber; // set NUMBER LIST built-in style            
            oPara2.Format.set_Style(ref oStyle);

            dgvTasks.Rows[2].Cells[1].Value = "Generating Notes Text";
            oPara2.Range.Text = GenerateNotesText(); //inject xml output into paragraph

            dgvTasks.Rows[2].Cells[1].Value = "Writing to PDF";
            oDoc.ExportAsFixedFormat(docFileName, WdExportFormat.wdExportFormatPDF);

            oDoc.Close();
            oWord.Quit();

            File.Delete(newtfName);

            dgvTasks.Rows[2].Cells[1].Value = "Complete";
        }

        public string GenerateNotesText()
        {
            var logDir = MainWindow.PROJECT_LOG_DIR;
            var projLogs = new List<ProjectLog>();
            var max = int.MinValue;
            var min = int.MaxValue;
            var buildNumber = string.Empty;

            var outputStringBuilder = new StringBuilder();
            var fbs = new List<int>();

            dgvTasks.Rows[2].Cells[1].Value = "Checking for local cache";
            ProjectLog curProjLog;

            if (File.Exists($"{logDir}{packageConfig.ProjectName}.xml"))
            {
                dgvTasks.Rows[2].Cells[1].Value = "Deserializing";
                //var curProjLog = parent.DeSerializeObject<CCNetWrapper.ProjectLog>($"{logDir}{packageConfig.ProjectName}.xml");
                {
                    XmlDocument xmlDocument = new XmlDocument();
                    xmlDocument.Load($"{logDir}{packageConfig.ProjectName}.xml");
                    string xmlString = xmlDocument.OuterXml;

                    using (StringReader read = new StringReader(xmlString))
                    {
                        Type outType = typeof(CCNetWrapper.ProjectLog );

                        XmlSerializer serializer = new XmlSerializer(outType);
                        using (XmlReader reader = new XmlTextReader(read))
                        {
                            curProjLog = (CCNetWrapper.ProjectLog)serializer.Deserialize(reader);
                        }
                    }
                }

                foreach (var x in curProjLog.BuildLogs)
                {
                    dgvTasks.Rows[2].Cells[1].Value = $"{x}";
                    if (x.GetMinorBuildNumber() < min)
                    {
                        min = x.GetMinorBuildNumber();
                    }
                    if (x.GetMinorBuildNumber() > max)
                    {
                        buildNumber = x.BuildNumber;
                        max = x.GetMinorBuildNumber();
                    }

                    if (!string.IsNullOrEmpty(x.Fogbugz) && !string.IsNullOrEmpty(x.ReleaseNote))
                    {
                        var fbNum = int.Parse(x.Fogbugz);
                        if (!fbs.Contains(fbNum))
                        {
                            fbs.Add(fbNum);
                            outputStringBuilder.AppendLine($"MS{x.Fogbugz}. {x.ReleaseNote}");
                        }
                    }
                };
                projLogs.Add(curProjLog);
            }
            
            return outputStringBuilder.ToString();
        }

        private void lblBuildFolder_Click(object sender, EventArgs e)
        {
            Process.Start("explorer.exe", lblBuildFolder.Text);
        }

        private void lblLocalPublish_Click(object sender, EventArgs e)
        {
            Process.Start("explorer.exe", lblLocalPublish.Text);
        }

        private void lblHCArticle_Click(object sender, EventArgs e)
        {
            var fileopener = new Process()
            {
                StartInfo = new ProcessStartInfo()
                {
                    FileName = "explorer",
                    Arguments = "\"" + lblHCArticle.Text + "\""
                }
            };
            
            fileopener.Start();
            
        }
    }
}
