using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Argotic.Common;
using Argotic.Extensions.Core;
using Argotic.Syndication;
using SharpRss.Models;
using ToolQit;
using ToolQit.Logging;

namespace SharpRss
{
    public struct SyndicationContainer
    {
        public GenericSyndicationFeed SyndicationFeed { get; set; }
        public CategoryModel Category { get; set; }
        public SyndicationModel SyndicationModel { get; set; }
        public HashSet<SyndicationItemModel> SyndicationItems { get; set; }
        public bool Fetched;
    }
    public static class SyndicationManager
    {
        private static ILog _log = LogManager.CreateLogger(typeof(SyndicationManager));
        public static SyndicationContainer CreateSyndication(string feedUrl)
        {
            GenericSyndicationFeed? syndicationFeed = null;
            Uri feedUri = new Uri(feedUrl);
            try
            {
                _log.Information("Fetching syndication: {SyndicationUri}", feedUri.ToString());
                syndicationFeed = GenericSyndicationFeed.Create(feedUri);
            }
            catch (Exception e)
            {
                _log.Error(e,"Could not get syndication: {SyndicationUrl}", feedUrl);
            }
            return ConstructSyndicationContainer(syndicationFeed, feedUrl);
        }

        public static Stream SyndicationToStream(GenericSyndicationFeed syndicationFeed)
        {
            MemoryStream memStream = new MemoryStream();
            syndicationFeed.Resource.Save(memStream);
            if (memStream.Length <= 0)
                _log.Warning("Failed to serialize {SyndicationType} feed: {SyndicationUri}", syndicationFeed.Format.ToString(), syndicationFeed.Title);
            memStream.Position = 0;
            return memStream;
        }

        private static SyndicationContainer ConstructSyndicationContainer(GenericSyndicationFeed? syndicationFeed, string syndicationUrl)
        {
            SyndicationContainer container = new SyndicationContainer();
            if (syndicationFeed == null)
            {
                _log.Warning("Could not construct syndication container!");
                return container;
            }
            container.SyndicationFeed = syndicationFeed;
            container.SyndicationModel = new SyndicationModel();
            container.SyndicationItems = new HashSet<SyndicationItemModel>();
            switch (syndicationFeed.Resource.Format)
            {
                case SyndicationContentFormat.Rss:
                    RssFeed rssFeed = (RssFeed)container.SyndicationFeed.Resource;
                    if (rssFeed.Channel == null) break;
                    container.SyndicationModel.EncodedUrl = EncodeUrlToBase64(syndicationUrl);
                    container.SyndicationModel.Title = rssFeed.Channel.Title ?? string.Empty;
                    container.SyndicationModel.SyndicationType = rssFeed.Format.ToString() ?? string.Empty;
                    container.SyndicationModel.Version = rssFeed.Version?.ToString() ?? string.Empty;
                    container.SyndicationModel.Description = rssFeed.Channel.Description ?? string.Empty;
                    container.SyndicationModel.Language = rssFeed.Channel.Language?.ToString() ?? string.Empty;
                    container.SyndicationModel.Copyright = rssFeed.Channel.Copyright ?? string.Empty;
                    container.SyndicationModel.PublicationDate = rssFeed.Channel.PublicationDate is { Ticks: > 0 } ? new DateTimeOffset(rssFeed.Channel.PublicationDate) : DateTimeOffset.MinValue;
                    container.SyndicationModel.Categories = rssFeed.Channel.Categories?.Select(x => x.Value).ToArray() ?? Array.Empty<string>();
                    container.SyndicationModel.ImageUrl = rssFeed.Channel.Image?.Url.ToString() ?? string.Empty;
                    foreach (var rssItem in rssFeed.Channel.Items)
                    {
                        SyndicationItemModel itemModel = new SyndicationItemModel()
                        {
                            Link = rssItem.Link?.ToString() ?? string.Empty,
                            EncodedSyndicationUrl = container.SyndicationModel.EncodedUrl ?? string.Empty,
                            Type = container.SyndicationModel.SyndicationType ?? string.Empty,
                            Title = rssItem.Title ?? string.Empty,
                            Description = rssItem.Description ?? string.Empty,
                            PublishingDate = rssItem.PublicationDate is not { Ticks: <= 0 } ? new DateTimeOffset(rssItem.PublicationDate) : DateTimeOffset.MinValue,
                            Authors = rssItem.Author != null ? new []{ rssItem.Author } : Array.Empty<string>(),
                            Categories = rssItem.Categories?.Select(x => x.Value).ToArray() ?? Array.Empty<string>(),
                            Content = rssItem.Extensions?.Where(x => x is SiteSummaryContentSyndicationExtension).Select(x => (x as SiteSummaryContentSyndicationExtension)?.Context.Encoded).FirstOrDefault() ?? string.Empty,
                            CommentsUrl = rssItem.Extensions?.Where(x => x is WellFormedWebCommentsSyndicationExtension).Select(x => (x as WellFormedWebCommentsSyndicationExtension)?.Context.CommentsFeed.ToString()).FirstOrDefault() ?? string.Empty
                        };
                        container.SyndicationItems.Add(itemModel);
                    }
                    break;
                case SyndicationContentFormat.Atom:
                    AtomFeed atomFeed = (AtomFeed)container.SyndicationFeed.Resource;
                    container.SyndicationModel.EncodedUrl = EncodeUrlToBase64(syndicationUrl);
                    container.SyndicationModel.Title = atomFeed.Title?.Content ?? string.Empty;
                    container.SyndicationModel.SyndicationType = atomFeed.Format.ToString() ?? string.Empty;
                    container.SyndicationModel.Version = atomFeed.Version?.ToString() ?? string.Empty;
                    container.SyndicationModel.Description = atomFeed.Subtitle?.Content ?? string.Empty;
                    container.SyndicationModel.Language = atomFeed.Language?.ToString() ?? string.Empty;
                    container.SyndicationModel.Copyright = atomFeed.Rights?.Content ?? string.Empty;
                    container.SyndicationModel.PublicationDate = atomFeed.UpdatedOn is { Ticks: > 0 } ? new DateTimeOffset(atomFeed.UpdatedOn) : DateTimeOffset.MinValue;
                    container.SyndicationModel.Categories = atomFeed.Categories?.Select(x => x.Label).ToArray() ?? Array.Empty<string>();
                    container.SyndicationModel.ImageUrl = atomFeed.Icon?.Uri.ToString() ?? string.Empty;
                    foreach (var entry in atomFeed.Entries)
                    {
                        SyndicationItemModel itemModel = new SyndicationItemModel()
                        {
                            Link = entry.Id?.Uri.ToString() ?? string.Empty,
                            EncodedSyndicationUrl = container.SyndicationModel?.EncodedUrl ?? string.Empty,
                            Type = container.SyndicationModel?.SyndicationType ?? string.Empty,
                            Title = entry.Title?.Content ?? string.Empty,
                            Description = entry.Summary?.Content ?? string.Empty,
                            ItemUpdatedDate = entry.UpdatedOn is { Ticks: > 0 } ? new DateTimeOffset(entry.UpdatedOn) : DateTimeOffset.Now,
                            PublishingDate = entry.PublishedOn is { Ticks: > 0 } ? new DateTimeOffset(entry.PublishedOn) : DateTimeOffset.MinValue,
                            Authors = entry.Authors?.Select(auth => auth.Name).ToArray() ?? Array.Empty<string>(),
                            Categories = entry.Categories?.Select(cat => cat.Label).ToArray() ?? Array.Empty<string>(),
                            Content = entry.Content?.Content ?? string.Empty
                        };
                        container.SyndicationItems.Add(itemModel);
                    }
                    break;
                default:
                    _log.Warning("Syndication implementation missing!");
                    break;
            }
            container.Fetched = true;
            return container;
        }
        public static string EncodeUrlToBase64(string url) => Convert.ToBase64String(Encoding.UTF8.GetBytes(url));
        public static string DecodeUrlFromBase64(string base64)
        {
            byte[] bytes = Convert.FromBase64String(base64);
            return Encoding.Default.GetString(bytes);
        }
        public static string FormatStringArrayToString(string[] sArr) => string.Join('|', sArr);
        public static string[] StringToStringArray(string stringFormat) => stringFormat.Split('|');

        public static void Init()
        {
            DbAccess.Initialize();
        }
    }
}