using System;
using System.Threading.Tasks;
using System.IO;
using Elwig.Helpers;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Linq;

namespace Elwig.Documents {
    public abstract partial class Document : IDisposable {

        private TempFile? _pdfFile = null;
        private string? _renderedHtml = null;

        public bool ShowFoldMarks = App.Config.Debug;

        public string DataPath;
        public int CurrentNextSeason;
        public string? DocumentId;
        public string Title;
        public string Author;
        public string Header;
        public string Footer;
        public DateTime Date;

        public Document(string title) {
            var c = App.Client;
            DataPath = App.DataPath;
            CurrentNextSeason = Utils.CurrentNextSeason;
            Title = title;
            Author = c.NameFull;
            Header = $"<div class='name'>{c.Name}</div><div class='suffix'>{c.NameSuffix}</div>";
            Footer = Utils.GenerateFooter("<br/>", " \u00b7 ")
                .Item(c.NameFull).NextLine()
                .Item(c.Address).Item($"{c.Plz} {c.Ort}").Item("Österreich").Item("Tel.", c.PhoneNr).Item("Fax", c.FaxNr).NextLine()
                .Item(c.EmailAddress).Item(c.Website).Item("Betriebs-Nr.", c.LfbisNr).Item("UID", c.UstIdNr).NextLine()
                .Item("BIC", c.Bic).Item("IBAN", c.Iban)
                .ToString();
            Date = DateTime.Today;
        }

        [GeneratedRegex(@"</body>.*?</footer>\s*</div>", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled)]
        private static partial Regex GeneratedDocumentHeaderRegex();
        private static readonly Regex DocumentHeaderRegex = GeneratedDocumentHeaderRegex();

        [GeneratedRegex(@"<style>.*?/style>", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled)]
        private static partial Regex GeneratedHtmlStyleRegex();
        private static readonly Regex HtmlStyleRegex = GeneratedHtmlStyleRegex();

        [GeneratedRegex(@"<link[^>]*>", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled)]
        private static partial Regex GeneratedHtmlLinkRegex();
        private static readonly Regex HtmlLinkRegex = GeneratedHtmlLinkRegex();

        ~Document() {
            Dispose();
        }

        public void Dispose() {
            _pdfFile?.Dispose();
            _pdfFile = null;
            GC.SuppressFinalize(this);
        }

        public static async Task<Document> Merge(IEnumerable<Document> docs) {
            string html = "";
            var styles = new List<string>();
            foreach (var d in docs) {
                var h = await d.Render();
                var s = HtmlStyleRegex.Matches(h).Select(m => m.Value).ToList();
                var l = HtmlLinkRegex.Matches(h).Select(m => m.Value).ToList();
                if (s.All(styles.Contains)) {
                    h = HtmlStyleRegex.Replace(h, "");
                } else {
                    styles.AddRange(s);
                }
                if (l.All(styles.Contains)) {
                    h = HtmlLinkRegex.Replace(h, "");
                } else {
                    styles.AddRange(l);
                }
                html += h;
            }
            html = DocumentHeaderRegex.Replace(html, "<div class='document-break'/>");
            return new InternalDocument("Mehrere Dokumente") {
                _renderedHtml = html,
            };
        }

        private async Task<string> Render() {
            if (_renderedHtml != null)
                return _renderedHtml;
            string name;
            if (this is BusinessLetter) {
                name = "BusinessLetter";
            } else if (this is DeliveryNote) {
                name = "DeliveryNote";
            } else if (this is CreditNote) {
                name = "CreditNote";
            } else if (this is DeliveryJournal) {
                name = "DeliveryJournal";
            } else if (this is Letterhead) {
                name = "Letterhead";
            } else {
                throw new InvalidOperationException("Invalid document object");
            }
            return await Render(name);
        }

        private async Task<string> Render(string name) {
            _renderedHtml = await Html.CompileRenderAsync(name, this);
            return _renderedHtml;
        }

        public async Task Generate() {
            var pdf = new TempFile("pdf");
            using (var tmpHtml = new TempFile("html")) {
                await File.WriteAllTextAsync(tmpHtml.FilePath, await Render(), Utils.UTF8);
                await Pdf.Convert(tmpHtml.FilePath, pdf.FilePath);
            }
            _pdfFile = pdf;
        }

        public void SaveTo(string pdfPath) {
            if (_pdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
            File.Copy(_pdfFile.FilePath, pdfPath);
        }

        public async Task Print(int copies = 1) {
            if (_pdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
            await Pdf.Print(_pdfFile.FilePath, copies);
        }

        public void Show() {
            if (_pdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
            Pdf.Show(_pdfFile.NewReference(), Title);
        }

        private class InternalDocument : Document {
            public InternalDocument(string title) : base(title) { }
        }
    }
}