using BukiVedi.Shared.Models;
using FB2Library;
using FB2Library.HeaderItems;
using System.Diagnostics;
using System.IO.Compression;
using System.Text;
using System.Xml;

namespace BukiVedi.Shared.Services
{
    public class ZipLibraryReader
    {
        private readonly string _zipFilesPath;        

        public ZipLibraryReader(string zipFilesPath)
        {
            if (string.IsNullOrWhiteSpace(zipFilesPath)) throw new ArgumentNullException(nameof(zipFilesPath));
            if (Directory.Exists(zipFilesPath) == false) throw new DirectoryNotFoundException($"Directory {zipFilesPath} not found");
            _zipFilesPath = zipFilesPath;
        }

        public BookDto ReadBookInfo(string archivePath, int index)
        {
            using (var zip = ZipFile.OpenRead(archivePath))
            {
                var entry = zip.Entries[index];
                BookDto book = null;
                if (entry.Name.EndsWith(".fb2", StringComparison.OrdinalIgnoreCase))
                {
                    using (var s = entry.Open())
                    {
                        book = ReadFromFb2(s, entry.Name, archivePath, index);
                    }
                }                
            }
            return null!;
        }

        public IEnumerable<BookDto> ReadBooks()
        {
            foreach (var zipFile in Directory.EnumerateFiles(_zipFilesPath, "*.zip"))
            {
                using (var zip = ZipFile.OpenRead(zipFile))
                {
                    var index = 0;
                    foreach (var e in zip.Entries)
                    {
                        BookDto book = null;
                        if (e.Name.EndsWith(".fb2", StringComparison.OrdinalIgnoreCase))
                        {
                            using (var s = e.Open())
                            {
                                book = ReadFromFb2(s, e.Name, zipFile, index);
                            }
                        }
                        /*else if (e.Name.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase))
                        {
                            using (var s = e.Open())
                            {
                                book = ReadFromPDF(s, e.Name, zipFile, index);
                            }
                        }
                        else if (e.Name.EndsWith(".epub", StringComparison.OrdinalIgnoreCase))
                        {
                            using (var s = e.Open())
                            {
                                book = ReadFromEPUB(s, e.Name, zipFile, index);
                            }
                        }*/
                        if (book != null)
                        {
                            // book.TitleLemmas = _provider.ExtractLexTokens(book.Title).Select(t => t.Token).ToArray(); // TODO
                            yield return book;
                        }
                        index++;
                    }
                }
            }
        }

        public async Task Download(string zipFile, int index, Stream stream)
        {
            using (var zip = ZipFile.OpenRead(zipFile))
            {
                var entry = zip.Entries[index];
                using (var s = entry.Open())
                {
                    await s.CopyToAsync(stream);
                    await stream.FlushAsync();
                }
            }
        }

        public async Task<string> ReadContent(string zipFile, int index)
        {
            using (var zip = ZipFile.OpenRead(zipFile))
            {
                var entry = zip.Entries[index];
                using (var s = entry.Open())
                {
                    FB2File file = new FB2Reader().ReadAsync(s, _fbSettings).Result;
                    var sb = new StringBuilder();
                    foreach (var b in file.Bodies)
                    {
                        var text = b.ToString();
                        sb.Append(text);
                    }
                    return sb.ToString();
                }
            }
        }

        #region FB2

        private static XmlLoadSettings _fbSettings = new XmlLoadSettings(new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore });
        private static BookDto ReadFromFb2(Stream s, string name, string path, int index)
        {
            try
            {
                // reading
                FB2File file = new FB2Reader().ReadAsync(s, _fbSettings).Result;
                if (file != null)
                {
                    var authors = Authors(file.TitleInfo.BookAuthors);
                    var title = file.TitleInfo.BookTitle?.Text;
                    var genres = file.TitleInfo?.Genres?.Select(g => g.Genre)?.ToArray();
                    return new BookDto
                    {
                        ArchiveIndex = index,
                        ArchivePath = path,
                        Authors = authors,
                        Genres = genres!,
                        Description = file.TitleInfo?.Annotation?.ToString() ?? string.Empty,
                        Title = string.IsNullOrWhiteSpace(title) ? name : title,
                        Format = BookFormat.FB2,
                        Year = file.DocumentInfo?.DocumentDate?.DateValue.Year ?? 1970
                    };
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(string.Format("Error loading file : {0}", ex.Message));
            }
            return null!;
        }
        private static string[] Authors(IEnumerable<AuthorType> authors)
        {
            if (authors == null || authors.Any() == false)
                return new string[1] { "unknown" };
            var l = new List<string>(authors.Count());
            foreach (var a in authors)
            {
                l.Add($"{a.LastName?.Text ?? string.Empty} {a.FirstName?.Text ?? string.Empty} {a.MiddleName?.Text ?? string.Empty}");
            }
            return l.ToArray();
        }
        #endregion
    }
}
