using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Xml.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OF_DL.Models;
using OF_DL.Enumerations;
using ArchivedDtos = OF_DL.Models.Dtos.Archived;
using HighlightDtos = OF_DL.Models.Dtos.Highlights;
using ListDtos = OF_DL.Models.Dtos.Lists;
using MessageDtos = OF_DL.Models.Dtos.Messages;
using PostDtos = OF_DL.Models.Dtos.Posts;
using PurchasedDtos = OF_DL.Models.Dtos.Purchased;
using StoriesDtos = OF_DL.Models.Dtos.Stories;
using StreamsDtos = OF_DL.Models.Dtos.Streams;
using UserDtos = OF_DL.Models.Dtos.Users;
using ArchivedEntities = OF_DL.Models.Entities.Archived;
using HighlightEntities = OF_DL.Models.Entities.Highlights;
using ListEntities = OF_DL.Models.Entities.Lists;
using MessageEntities = OF_DL.Models.Entities.Messages;
using PostEntities = OF_DL.Models.Entities.Posts;
using PurchasedEntities = OF_DL.Models.Entities.Purchased;
using StoryEntities = OF_DL.Models.Entities.Stories;
using StreamEntities = OF_DL.Models.Entities.Streams;
using UserEntities = OF_DL.Models.Entities.Users;
using OF_DL.Models.Mappers;
using OF_DL.Models.Subscriptions;
using OF_DL.Widevine;
using Serilog;
using Spectre.Console;
using static OF_DL.Utils.HttpUtil;
using Constants = OF_DL.Helpers.Constants;
using SinglePostCollection = OF_DL.Models.Entities.Posts.SinglePostCollection;

namespace OF_DL.Services;

public class APIService(IAuthService authService, IConfigService configService, IDBService dbService)
    : IAPIService
{
    private const int MaxAttempts = 30;
    private const int DelayBetweenAttempts = 3000;
    private static readonly JsonSerializerSettings m_JsonSerializerSettings;
    private static DateTime? cachedDynamicRulesExpiration;
    private static DynamicRules? cachedDynamicRules;

    static APIService() =>
        m_JsonSerializerSettings = new JsonSerializerSettings { MissingMemberHandling = MissingMemberHandling.Ignore };


    public Dictionary<string, string> GetDynamicHeaders(string path, string queryParams)
    {
        Log.Debug("Calling GetDynamicHeaders");
        Log.Debug($"Path: {path}");
        Log.Debug($"Query Params: {queryParams}");

        DynamicRules? root;

        //Check if we have a cached version of the dynamic rules
        if (cachedDynamicRules != null && cachedDynamicRulesExpiration.HasValue &&
            DateTime.UtcNow < cachedDynamicRulesExpiration)
        {
            Log.Debug("Using cached dynamic rules");
            root = cachedDynamicRules;
        }
        else
        {
            //Get rules from GitHub and fallback to local file
            string? dynamicRulesJSON = GetDynamicRules();
            if (!string.IsNullOrEmpty(dynamicRulesJSON))
            {
                Log.Debug("Using dynamic rules from GitHub");
                root = JsonConvert.DeserializeObject<DynamicRules>(dynamicRulesJSON);

                // Cache the GitHub response for 15 minutes
                cachedDynamicRules = root;
                cachedDynamicRulesExpiration = DateTime.UtcNow.AddMinutes(15);
            }
            else
            {
                Log.Debug("Using dynamic rules from local file");
                root = JsonConvert.DeserializeObject<DynamicRules>(File.ReadAllText("rules.json"));

                // Cache the dynamic rules from local file to prevent unnecessary disk
                // operations and frequent call to GitHub. Since the GitHub dynamic rules
                // are preferred to the local file, the cache time is shorter than when dynamic rules
                // are successfully retrieved from GitHub.
                cachedDynamicRules = root;
                cachedDynamicRulesExpiration = DateTime.UtcNow.AddMinutes(5);
            }
        }

        DateTimeOffset dto = DateTime.UtcNow;
        long timestamp = dto.ToUnixTimeMilliseconds();

        string input = $"{root!.StaticParam}\n{timestamp}\n{path + queryParams}\n{authService.CurrentAuth.USER_ID}";
        byte[] inputBytes = Encoding.UTF8.GetBytes(input);
        byte[] hashBytes = SHA1.HashData(inputBytes);
        string hashString = BitConverter.ToString(hashBytes).Replace("-", "").ToLower();

        int checksum = root.ChecksumIndexes.Aggregate(0, (current, number) => current + hashString[number]) +
                       root.ChecksumConstant!.Value;
        string sign = $"{root.Prefix}:{hashString}:{checksum.ToString("X").ToLower()}:{root.Suffix}";

        Dictionary<string, string> headers = new()
        {
            { "accept", "application/json, text/plain" },
            { "app-token", root.AppToken! },
            { "cookie", authService.CurrentAuth!.COOKIE! },
            { "sign", sign },
            { "time", timestamp.ToString() },
            { "user-id", authService.CurrentAuth!.USER_ID! },
            { "user-agent", authService.CurrentAuth!.USER_AGENT! },
            { "x-bc", authService.CurrentAuth!.X_BC! }
        };
        return headers;
    }


    public async Task<UserEntities.User?> GetUserInfo(string endpoint)
    {
        Log.Debug($"Calling GetUserInfo: {endpoint}");

        try
        {
            UserEntities.User? user = new();
            int post_limit = 50;
            Dictionary<string, string> getParams = new()
            {
                { "limit", post_limit.ToString() }, { "order", "publish_date_asc" }
            };

            HttpClient client = new();
            HttpRequestMessage request = await BuildHttpRequestMessage(getParams, endpoint);

            using HttpResponseMessage response = await client.SendAsync(request);

            if (!response.IsSuccessStatusCode)
            {
                return user;
            }

            response.EnsureSuccessStatusCode();
            string body = await response.Content.ReadAsStringAsync();
            UserDtos.UserDto? userDto =
                JsonConvert.DeserializeObject<UserDtos.UserDto>(body, m_JsonSerializerSettings);
            user = UserMapper.FromDto(userDto) ?? new UserEntities.User();
            return user;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }

    public async Task<JObject> GetUserInfoById(string endpoint)
    {
        try
        {
            HttpClient client = new();
            HttpRequestMessage request = await BuildHttpRequestMessage(new Dictionary<string, string>(), endpoint);

            using HttpResponseMessage response = await client.SendAsync(request);

            response.EnsureSuccessStatusCode();
            string body = await response.Content.ReadAsStringAsync();

            //if the content creator doesnt exist, we get a 200 response, but the content isnt usable
            //so let's not throw an exception, since "content creator no longer exists" is handled elsewhere
            //which means we wont get loads of exceptions
            if (body.Equals("[]"))
            {
                return null;
            }

            JObject jObject = JObject.Parse(body);

            return jObject;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }

    public async Task<Dictionary<string, long>?> GetActiveSubscriptions(string endpoint, bool includeRestricted)
    {
        Dictionary<string, string> getParams = new()
        {
            { "offset", "0" }, { "limit", "50" }, { "type", "active" }, { "format", "infinite" }
        };

        return await GetAllSubscriptions(getParams, endpoint, includeRestricted);
    }


    public async Task<Dictionary<string, long>?> GetExpiredSubscriptions(string endpoint, bool includeRestricted)
    {
        Dictionary<string, string> getParams = new()
        {
            { "offset", "0" }, { "limit", "50" }, { "type", "expired" }, { "format", "infinite" }
        };

        Log.Debug("Calling GetExpiredSubscriptions");

        return await GetAllSubscriptions(getParams, endpoint, includeRestricted);
    }


    public async Task<Dictionary<string, long>> GetLists(string endpoint)
    {
        Log.Debug("Calling GetLists");

        try
        {
            int offset = 0;
            Dictionary<string, string> getParams = new()
            {
                { "offset", offset.ToString() },
                { "skip_users", "all" },
                { "limit", "50" },
                { "format", "infinite" }
            };
            Dictionary<string, long> lists = new();
            while (true)
            {
                string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, new HttpClient());

                if (body == null)
                {
                    break;
                }

                ListDtos.UserListDto? userListDto = JsonConvert.DeserializeObject<ListDtos.UserListDto>(body);
                ListEntities.UserList userList = UserListsMapper.FromDto(userListDto);

                foreach (ListEntities.UserListItem listItem in userList.List)
                {
                    if (IsStringOnlyDigits(listItem.Id) && !lists.ContainsKey(listItem.Name))
                    {
                        lists.Add(listItem.Name, Convert.ToInt32(listItem.Id));
                    }
                }

                if (userList.HasMore)
                {
                    offset += 50;
                    getParams["offset"] = Convert.ToString(offset);
                }
                else
                {
                    break;
                }
            }

            return lists;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }


    public async Task<List<string>?> GetListUsers(string endpoint)
    {
        Log.Debug($"Calling GetListUsers - {endpoint}");

        try
        {
            int offset = 0;
            Dictionary<string, string> getParams = new() { { "offset", offset.ToString() }, { "limit", "50" } };
            List<string> users = new();

            while (true)
            {
                string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, new HttpClient());
                if (body == null)
                {
                    break;
                }

                List<ListDtos.UsersListDto>? usersListDto =
                    JsonConvert.DeserializeObject<List<ListDtos.UsersListDto>>(body);
                List<ListEntities.UsersList> usersList = UserListsMapper.FromDto(usersListDto);

                if (usersList.Count <= 0)
                {
                    break;
                }

                foreach (ListEntities.UsersList ul in usersList)
                {
                    users.Add(ul.Username);
                }

                if (users.Count < 50)
                {
                    break;
                }

                offset += 50;
                getParams["offset"] = Convert.ToString(offset);
            }

            return users;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }


    public async Task<Dictionary<long, string>> GetMedia(MediaType mediatype,
        string endpoint,
        string? username,
        string folder,
        List<long> paid_post_ids)
    {
        Log.Debug($"Calling GetMedia - {username}");

        try
        {
            Dictionary<long, string> return_urls = new();
            int post_limit = 50;
            int limit = 5;
            int offset = 0;

            Dictionary<string, string> getParams = new();

            switch (mediatype)
            {
                case MediaType.Stories:
                    getParams = new Dictionary<string, string>
                    {
                        { "limit", post_limit.ToString() },
                        { "order", "publish_date_desc" },
                        { "skip_users", "all" }
                    };
                    break;

                case MediaType.Highlights:
                    getParams = new Dictionary<string, string>
                    {
                        { "limit", limit.ToString() }, { "offset", offset.ToString() }, { "skip_users", "all" }
                    };
                    break;
            }

            string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, new HttpClient());


            if (mediatype == MediaType.Stories)
            {
                Log.Debug("Media Stories - " + endpoint);

                List<StoriesDtos.StoryDto>? storiesDto =
                    JsonConvert.DeserializeObject<List<StoriesDtos.StoryDto>>(body, m_JsonSerializerSettings);
                List<StoryEntities.Stories> stories = StoriesMapper.FromDto(storiesDto);

                foreach (StoryEntities.Stories story in stories)
                {
                    if (story.Media[0].CreatedAt.HasValue)
                    {
                        await dbService.AddStory(folder, story.Id, string.Empty, "0", false, false,
                            story.Media[0].CreatedAt.Value);
                    }
                    else if (story.CreatedAt.HasValue)
                    {
                        await dbService.AddStory(folder, story.Id, string.Empty, "0", false, false,
                            story.CreatedAt.Value);
                    }
                    else
                    {
                        await dbService.AddStory(folder, story.Id, string.Empty, "0", false, false, DateTime.Now);
                    }

                    if (story.Media != null && story.Media.Count > 0)
                    {
                        foreach (StoryEntities.Medium medium in story.Media)
                        {
                            await dbService.AddMedia(folder, medium.Id, story.Id, medium.Files.Full.Url, null, null,
                                null, "Stories",
                                medium.Type == "photo" ? "Images" :
                                medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                medium.Type == "audio" ? "Audios" : null, false, false, null);
                            if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                            {
                                continue;
                            }

                            if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                            {
                                continue;
                            }

                            if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                            {
                                continue;
                            }

                            if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                            {
                                continue;
                            }

                            if (medium.CanView)
                            {
                                if (!return_urls.ContainsKey(medium.Id))
                                {
                                    return_urls.Add(medium.Id, medium.Files.Full.Url);
                                }
                            }
                        }
                    }
                }
            }
            else if (mediatype == MediaType.Highlights)
            {
                List<string> highlight_ids = new();
                HighlightDtos.HighlightsDto? highlightsDto =
                    JsonConvert.DeserializeObject<HighlightDtos.HighlightsDto>(body, m_JsonSerializerSettings);
                HighlightEntities.Highlights highlights = HighlightsMapper.FromDto(highlightsDto);

                if (highlights.HasMore)
                {
                    offset += 5;
                    getParams["offset"] = offset.ToString();
                    while (true)
                    {
                        Log.Debug("Media Highlights - " + endpoint);

                        string? loopbody = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient());
                        HighlightDtos.HighlightsDto? newHighlightsDto =
                            JsonConvert.DeserializeObject<HighlightDtos.HighlightsDto>(loopbody,
                                m_JsonSerializerSettings);
                        HighlightEntities.Highlights newHighlights = HighlightsMapper.FromDto(newHighlightsDto);

                        highlights.List.AddRange(newHighlights.List);
                        if (!newHighlights.HasMore)
                        {
                            break;
                        }

                        offset += 5;
                        getParams["offset"] = offset.ToString();
                    }
                }

                foreach (HighlightEntities.ListItem list in highlights.List)
                {
                    if (!highlight_ids.Contains(list.Id.ToString()))
                    {
                        highlight_ids.Add(list.Id.ToString());
                    }
                }

                foreach (string highlight_id in highlight_ids)
                {
                    Dictionary<string, string> highlight_headers =
                        GetDynamicHeaders("/api2/v2/stories/highlights/" + highlight_id, string.Empty);

                    HttpClient highlight_client = GetHttpClient();

                    HttpRequestMessage highlight_request = new(HttpMethod.Get,
                        $"https://onlyfans.com/api2/v2/stories/highlights/{highlight_id}");

                    foreach (KeyValuePair<string, string> keyValuePair in highlight_headers)
                    {
                        highlight_request.Headers.Add(keyValuePair.Key, keyValuePair.Value);
                    }

                    using HttpResponseMessage highlightResponse = await highlight_client.SendAsync(highlight_request);
                    highlightResponse.EnsureSuccessStatusCode();
                    string highlightBody = await highlightResponse.Content.ReadAsStringAsync();
                    HighlightDtos.HighlightMediaDto? highlightMediaDto =
                        JsonConvert.DeserializeObject<HighlightDtos.HighlightMediaDto>(highlightBody,
                            m_JsonSerializerSettings);
                    HighlightEntities.HighlightMedia highlightMedia = HighlightsMapper.FromDto(highlightMediaDto);

                    foreach (HighlightEntities.Story item in highlightMedia.Stories)
                    {
                        if (item.Media != null && item.Media.Count > 0 && item.Media[0].CreatedAt.HasValue)
                        {
                            await dbService.AddStory(folder, item.Id, string.Empty, "0", false, false,
                                item.Media[0].CreatedAt.Value);
                        }
                        else if (item.CreatedAt.HasValue)
                        {
                            await dbService.AddStory(folder, item.Id, string.Empty, "0", false, false,
                                item.CreatedAt.Value);
                        }
                        else
                        {
                            await dbService.AddStory(folder, item.Id, string.Empty, "0", false, false,
                                DateTime.Now);
                        }

                        if (item.Media != null && item.Media.Count > 0 && item.Media[0].CanView)
                        {
                            foreach (HighlightEntities.Medium medium in item.Media)
                            {
                                string storyUrl = item.Media[0].Files?.Full?.Url ?? string.Empty;
                                await dbService.AddMedia(folder, medium.Id, item.Id, storyUrl, null, null, null,
                                    "Stories",
                                    medium.Type == "photo" ? "Images" :
                                    medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                    medium.Type == "audio" ? "Audios" : null, false, false, null);
                                if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                                {
                                    continue;
                                }

                                if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                                {
                                    continue;
                                }

                                if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                                {
                                    continue;
                                }

                                if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                                {
                                    continue;
                                }

                                if (!return_urls.ContainsKey(medium.Id) && !string.IsNullOrEmpty(storyUrl))
                                {
                                    return_urls.Add(medium.Id, storyUrl);
                                }
                            }
                        }
                    }
                }
            }

            return return_urls;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }


    public async Task<PurchasedEntities.PaidPostCollection> GetPaidPosts(string endpoint, string folder,
        string username,
        List<long> paid_post_ids, StatusContext ctx)
    {
        Log.Debug($"Calling GetPaidPosts - {username}");

        try
        {
            PurchasedEntities.Purchased paidPosts = new();
            PurchasedEntities.PaidPostCollection paidPostCollection = new();
            int post_limit = 50;
            Dictionary<string, string> getParams = new()
            {
                { "limit", post_limit.ToString() },
                { "skip_users", "all" },
                { "order", "publish_date_desc" },
                { "format", "infinite" },
                { "author", username }
            };

            string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient());
            PurchasedDtos.PurchasedDto? paidPostsDto =
                JsonConvert.DeserializeObject<PurchasedDtos.PurchasedDto>(body, m_JsonSerializerSettings);
            paidPosts = PurchasedMapper.FromDto(paidPostsDto);
            ctx.Status($"[red]Getting Paid Posts\n[/] [red]Found {paidPosts.List.Count}[/]");
            ctx.Spinner(Spinner.Known.Dots);
            ctx.SpinnerStyle(Style.Parse("blue"));
            if (paidPosts != null && paidPosts.HasMore)
            {
                getParams["offset"] = paidPosts.List.Count.ToString();
                while (true)
                {
                    PurchasedEntities.Purchased newPaidPosts = new();

                    string? loopbody = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient());
                    PurchasedDtos.PurchasedDto? newPaidPostsDto =
                        JsonConvert.DeserializeObject<PurchasedDtos.PurchasedDto>(loopbody, m_JsonSerializerSettings);
                    newPaidPosts = PurchasedMapper.FromDto(newPaidPostsDto);

                    paidPosts.List.AddRange(newPaidPosts.List);
                    ctx.Status($"[red]Getting Paid Posts\n[/] [red]Found {paidPosts.List.Count}[/]");
                    ctx.Spinner(Spinner.Known.Dots);
                    ctx.SpinnerStyle(Style.Parse("blue"));
                    if (!newPaidPosts.HasMore)
                    {
                        break;
                    }

                    getParams["offset"] = Convert.ToString(Convert.ToInt32(getParams["offset"]) + post_limit);
                }
            }

            foreach (PurchasedEntities.ListItem purchase in paidPosts.List)
            {
                if (purchase.ResponseType == "post" && purchase.Media != null && purchase.Media.Count > 0)
                {
                    List<long> previewids = new();
                    if (purchase.Previews != null)
                    {
                        for (int i = 0; i < purchase.Previews.Count; i++)
                        {
                            if (purchase.Previews[i] is long previewId)
                            {
                                if (!previewids.Contains(previewId))
                                {
                                    previewids.Add(previewId);
                                }
                            }
                        }
                    }
                    else if (purchase.Preview != null)
                    {
                        for (int i = 0; i < purchase.Preview.Count; i++)
                        {
                            if (purchase.Preview[i] is long previewId)
                            {
                                if (!previewids.Contains(previewId))
                                {
                                    previewids.Add(previewId);
                                }
                            }
                        }
                    }

                    await dbService.AddPost(folder, purchase.Id, purchase.Text != null ? purchase.Text : string.Empty,
                        purchase.Price != null ? purchase.Price : "0",
                        purchase.Price != null && purchase.IsOpened ? true : false,
                        purchase.IsArchived.HasValue ? purchase.IsArchived.Value : false,
                        purchase.CreatedAt != null ? purchase.CreatedAt.Value : purchase.PostedAt.Value);
                    paidPostCollection.PaidPostObjects.Add(purchase);
                    foreach (MessageEntities.Medium medium in purchase.Media)
                    {
                        if (!previewids.Contains(medium.Id))
                        {
                            paid_post_ids.Add(medium.Id);
                        }

                        if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                        {
                            continue;
                        }

                        if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                        {
                            continue;
                        }

                        if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                        {
                            continue;
                        }

                        if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                        {
                            continue;
                        }

                        if (previewids.Count > 0)
                        {
                            bool has = previewids.Any(cus => cus.Equals(medium.Id));
                            if (!has && medium.CanView && medium.Files != null && medium.Files.Full != null &&
                                !string.IsNullOrEmpty(medium.Files.Full.Url))
                            {
                                if (!paidPostCollection.PaidPosts.ContainsKey(medium.Id))
                                {
                                    await dbService.AddMedia(folder, medium.Id, purchase.Id, medium.Files.Full.Url,
                                        null, null, null, "Posts",
                                        medium.Type == "photo" ? "Images" :
                                        medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                        medium.Type == "audio" ? "Audios" : null,
                                        previewids.Contains(medium.Id), false, null);
                                    paidPostCollection.PaidPosts.Add(medium.Id, medium.Files.Full.Url);
                                    paidPostCollection.PaidPostMedia.Add(medium);
                                }
                            }
                            else if (!has && medium.CanView && medium.Files != null && medium.Files.Drm != null)
                            {
                                if (!paidPostCollection.PaidPosts.ContainsKey(medium.Id))
                                {
                                    await dbService.AddMedia(folder, medium.Id, purchase.Id,
                                        medium.Files.Drm.Manifest.Dash, null, null, null, "Posts",
                                        medium.Type == "photo" ? "Images" :
                                        medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                        medium.Type == "audio" ? "Audios" : null,
                                        previewids.Contains(medium.Id), false, null);
                                    paidPostCollection.PaidPosts.Add(medium.Id,
                                        $"{medium.Files.Drm.Manifest.Dash},{medium.Files.Drm.Signature.Dash.CloudFrontPolicy},{medium.Files.Drm.Signature.Dash.CloudFrontSignature},{medium.Files.Drm.Signature.Dash.CloudFrontKeyPairId},{medium.Id},{purchase.Id}");
                                    paidPostCollection.PaidPostMedia.Add(medium);
                                }
                            }
                        }
                        else
                        {
                            if (medium.CanView && medium.Files != null && medium.Files.Full != null &&
                                !string.IsNullOrEmpty(medium.Files.Full.Url))
                            {
                                if (!paidPostCollection.PaidPosts.ContainsKey(medium.Id))
                                {
                                    await dbService.AddMedia(folder, medium.Id, purchase.Id, medium.Files.Full.Url,
                                        null, null, null, "Posts",
                                        medium.Type == "photo" ? "Images" :
                                        medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                        medium.Type == "audio" ? "Audios" : null,
                                        previewids.Contains(medium.Id), false, null);
                                    paidPostCollection.PaidPosts.Add(medium.Id, medium.Files.Full.Url);
                                    paidPostCollection.PaidPostMedia.Add(medium);
                                }
                            }
                            else if (medium.CanView && medium.Files != null && medium.Files.Drm != null)
                            {
                                if (!paidPostCollection.PaidPosts.ContainsKey(medium.Id))
                                {
                                    await dbService.AddMedia(folder, medium.Id, purchase.Id,
                                        medium.Files.Drm.Manifest.Dash, null, null, null, "Posts",
                                        medium.Type == "photo" ? "Images" :
                                        medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                        medium.Type == "audio" ? "Audios" : null,
                                        previewids.Contains(medium.Id), false, null);
                                    paidPostCollection.PaidPosts.Add(medium.Id,
                                        $"{medium.Files.Drm.Manifest.Dash},{medium.Files.Drm.Signature.Dash.CloudFrontPolicy},{medium.Files.Drm.Signature.Dash.CloudFrontSignature},{medium.Files.Drm.Signature.Dash.CloudFrontKeyPairId},{medium.Id},{purchase.Id}");
                                    paidPostCollection.PaidPostMedia.Add(medium);
                                }
                            }
                        }
                    }
                }
            }

            return paidPostCollection;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }


    public async Task<PostEntities.PostCollection> GetPosts(string endpoint, string folder, List<long> paid_post_ids,
        StatusContext ctx)
    {
        Log.Debug($"Calling GetPosts - {endpoint}");

        try
        {
            PostEntities.Post posts = new();
            PostEntities.PostCollection postCollection = new();
            int post_limit = 50;
            Dictionary<string, string> getParams = new()
            {
                { "limit", post_limit.ToString() },
                { "order", "publish_date_desc" },
                { "format", "infinite" },
                { "skip_users", "all" }
            };

            DownloadDateSelection downloadDateSelection = DownloadDateSelection.before;
            DateTime? downloadAsOf = null;

            if (configService.CurrentConfig.DownloadOnlySpecificDates &&
                configService.CurrentConfig.CustomDate.HasValue)
            {
                downloadDateSelection = configService.CurrentConfig.DownloadDateSelection;
                downloadAsOf = configService.CurrentConfig.CustomDate;
            }
            else if (configService.CurrentConfig.DownloadPostsIncrementally)
            {
                DateTime? mostRecentPostDate = await dbService.GetMostRecentPostDate(folder);
                if (mostRecentPostDate.HasValue)
                {
                    downloadDateSelection = DownloadDateSelection.after;
                    downloadAsOf = mostRecentPostDate.Value.AddMinutes(-5); // Back track a little for a margin of error
                }
            }

            UpdateGetParamsForDateSelection(
                downloadDateSelection,
                ref getParams,
                downloadAsOf);

            string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, new HttpClient());
            PostDtos.PostDto? postsDto =
                JsonConvert.DeserializeObject<PostDtos.PostDto>(body, m_JsonSerializerSettings);
            posts = PostMapper.FromDto(postsDto);
            ctx.Status(
                $"[red]Getting Posts (this may take a long time, depending on the number of Posts the creator has)\n[/] [red]Found {posts.List.Count}[/]");
            ctx.Spinner(Spinner.Known.Dots);
            ctx.SpinnerStyle(Style.Parse("blue"));
            if (posts != null && posts.HasMore)
            {
                UpdateGetParamsForDateSelection(
                    downloadDateSelection,
                    ref getParams,
                    posts.TailMarker);

                while (true)
                {
                    PostEntities.Post newposts = new();

                    string? loopbody = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient());
                    PostDtos.PostDto? newPostsDto =
                        JsonConvert.DeserializeObject<PostDtos.PostDto>(loopbody, m_JsonSerializerSettings);
                    newposts = PostMapper.FromDto(newPostsDto);

                    posts.List.AddRange(newposts.List);
                    ctx.Status(
                        $"[red]Getting Posts (this may take a long time, depending on the number of Posts the creator has)\n[/] [red]Found {posts.List.Count}[/]");
                    ctx.Spinner(Spinner.Known.Dots);
                    ctx.SpinnerStyle(Style.Parse("blue"));
                    if (!newposts.HasMore)
                    {
                        break;
                    }

                    UpdateGetParamsForDateSelection(
                        downloadDateSelection,
                        ref getParams,
                        newposts.TailMarker);
                }
            }

            foreach (PostEntities.ListItem post in posts.List)
            {
                if (configService.CurrentConfig.SkipAds)
                {
                    if (post.RawText != null && (post.RawText.Contains("#ad") || post.RawText.Contains("/trial/") ||
                                                 post.RawText.Contains("#announcement")))
                    {
                        continue;
                    }

                    if (post.Text != null && (post.Text.Contains("#ad") || post.Text.Contains("/trial/") ||
                                              post.Text.Contains("#announcement")))
                    {
                        continue;
                    }
                }

                List<long> postPreviewIds = new();
                if (post.Preview != null && post.Preview.Count > 0)
                {
                    for (int i = 0; i < post.Preview.Count; i++)
                    {
                        if (post.Preview[i] is long previewId)
                        {
                            if (!postPreviewIds.Contains(previewId))
                            {
                                postPreviewIds.Add(previewId);
                            }
                        }
                    }
                }

                await dbService.AddPost(folder, post.Id, post.RawText != null ? post.RawText : string.Empty,
                    post.Price != null ? post.Price : "0", post.Price != null && post.IsOpened ? true : false,
                    post.IsArchived, post.PostedAt);
                postCollection.PostObjects.Add(post);
                if (post.Media != null && post.Media.Count > 0)
                {
                    foreach (PostEntities.Medium medium in post.Media)
                    {
                        if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                        {
                            continue;
                        }

                        if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                        {
                            continue;
                        }

                        if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                        {
                            continue;
                        }

                        if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                        {
                            continue;
                        }

                        if (medium.CanView && medium.Files?.Drm == null)
                        {
                            bool has = paid_post_ids.Any(cus => cus.Equals(medium.Id));
                            if (medium.Files!.Full != null && !string.IsNullOrEmpty(medium.Files!.Full.Url))
                            {
                                if (!has)
                                {
                                    if (!postCollection.Posts.ContainsKey(medium.Id))
                                    {
                                        await dbService.AddMedia(folder, medium.Id, post.Id, medium.Files!.Full.Url,
                                            null, null, null, "Posts",
                                            medium.Type == "photo" ? "Images" :
                                            medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                            medium.Type == "audio" ? "Audios" : null,
                                            postPreviewIds.Contains(medium.Id) ? true : false, false, null);
                                        postCollection.Posts.Add(medium.Id, medium.Files!.Full.Url);
                                        postCollection.PostMedia.Add(medium);
                                    }
                                }
                            }
                            else if (medium.Files.Preview != null && medium.Files!.Full == null)
                            {
                                if (!has)
                                {
                                    if (!postCollection.Posts.ContainsKey(medium.Id))
                                    {
                                        await dbService.AddMedia(folder, medium.Id, post.Id, medium.Files.Preview.Url,
                                            null, null, null, "Posts",
                                            medium.Type == "photo" ? "Images" :
                                            medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                            medium.Type == "audio" ? "Audios" : null,
                                            postPreviewIds.Contains(medium.Id) ? true : false, false, null);
                                        postCollection.Posts.Add(medium.Id, medium.Files.Preview.Url);
                                        postCollection.PostMedia.Add(medium);
                                    }
                                }
                            }
                        }
                        else if (medium.CanView && medium.Files != null && medium.Files.Drm != null)
                        {
                            bool has = paid_post_ids.Any(cus => cus.Equals(medium.Id));
                            if (!has && medium.Files != null && medium.Files.Drm != null)
                            {
                                if (!postCollection.Posts.ContainsKey(medium.Id))
                                {
                                    await dbService.AddMedia(folder, medium.Id, post.Id, medium.Files.Drm.Manifest.Dash,
                                        null, null, null, "Posts",
                                        medium.Type == "photo" ? "Images" :
                                        medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                        medium.Type == "audio" ? "Audios" : null,
                                        postPreviewIds.Contains(medium.Id) ? true : false, false, null);
                                    postCollection.Posts.Add(medium.Id,
                                        $"{medium.Files.Drm.Manifest.Dash},{medium.Files.Drm.Signature.Dash.CloudFrontPolicy},{medium.Files.Drm.Signature.Dash.CloudFrontSignature},{medium.Files.Drm.Signature.Dash.CloudFrontKeyPairId},{medium.Id},{post.Id}");
                                    postCollection.PostMedia.Add(medium);
                                }
                            }
                        }
                    }
                }
            }

            return postCollection;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }

    public async Task<SinglePostCollection> GetPost(string endpoint, string folder)
    {
        Log.Debug($"Calling GetPost - {endpoint}");

        try
        {
            PostEntities.SinglePost singlePost = new();
            SinglePostCollection singlePostCollection = new();
            Dictionary<string, string> getParams = new() { { "skip_users", "all" } };

            string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, new HttpClient());
            PostDtos.SinglePostDto? singlePostDto =
                JsonConvert.DeserializeObject<PostDtos.SinglePostDto>(body, m_JsonSerializerSettings);
            singlePost = PostMapper.FromDto(singlePostDto);

            if (singlePostDto != null)
            {
                List<long> postPreviewIds = new();
                if (singlePost.Preview != null && singlePost.Preview.Count > 0)
                {
                    for (int i = 0; i < singlePost.Preview.Count; i++)
                    {
                        if (singlePost.Preview[i] is long previewId)
                        {
                            if (!postPreviewIds.Contains(previewId))
                            {
                                postPreviewIds.Add(previewId);
                            }
                        }
                    }
                }

                await dbService.AddPost(folder, singlePost.Id, singlePost.Text != null ? singlePost.Text : string.Empty,
                    singlePost.Price != null ? singlePost.Price : "0",
                    singlePost.Price != null && singlePost.IsOpened ? true : false, singlePost.IsArchived,
                    singlePost.PostedAt);
                singlePostCollection.SinglePostObjects.Add(singlePost);
                if (singlePost.Media != null && singlePost.Media.Count > 0)
                {
                    foreach (PostEntities.Medium medium in singlePost.Media)
                    {
                        if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                        {
                            continue;
                        }

                        if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                        {
                            continue;
                        }

                        if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                        {
                            continue;
                        }

                        if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                        {
                            continue;
                        }

                        if (medium.CanView && medium.Files?.Drm == null)
                        {
                            switch (configService.CurrentConfig.DownloadVideoResolution)
                            {
                                case VideoResolution.source:
                                    if (medium.Files!.Full != null && !string.IsNullOrEmpty(medium.Files!.Full.Url))
                                    {
                                        if (!singlePostCollection.SinglePosts.ContainsKey(medium.Id))
                                        {
                                            await dbService.AddMedia(folder, medium.Id, singlePost.Id,
                                                medium.Files!.Full.Url, null, null, null, "Posts",
                                                medium.Type == "photo" ? "Images" :
                                                medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                                medium.Type == "audio" ? "Audios" : null,
                                                postPreviewIds.Contains(medium.Id) ? true : false, false, null);
                                            singlePostCollection.SinglePosts.Add(medium.Id, medium.Files!.Full.Url);
                                            singlePostCollection.SinglePostMedia.Add(medium);
                                        }
                                    }

                                    break;
                                case VideoResolution._240:
                                    if (medium.VideoSources != null)
                                    {
                                        if (!string.IsNullOrEmpty(medium.VideoSources._240))
                                        {
                                            if (!singlePostCollection.SinglePosts.ContainsKey(medium.Id))
                                            {
                                                await dbService.AddMedia(folder, medium.Id, singlePost.Id,
                                                    medium.VideoSources._240, null, null, null, "Posts",
                                                    medium.Type == "photo" ? "Images" :
                                                    medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                                    medium.Type == "audio" ? "Audios" : null,
                                                    postPreviewIds.Contains(medium.Id) ? true : false, false, null);
                                                singlePostCollection.SinglePosts.Add(medium.Id,
                                                    medium.VideoSources._240);
                                                singlePostCollection.SinglePostMedia.Add(medium);
                                            }
                                        }
                                    }

                                    break;
                                case VideoResolution._720:
                                    if (medium.VideoSources != null)
                                    {
                                        if (!string.IsNullOrEmpty(medium.VideoSources._720))
                                        {
                                            if (!singlePostCollection.SinglePosts.ContainsKey(medium.Id))
                                            {
                                                await dbService.AddMedia(folder, medium.Id, singlePost.Id,
                                                    medium.VideoSources._720, null, null, null, "Posts",
                                                    medium.Type == "photo" ? "Images" :
                                                    medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                                    medium.Type == "audio" ? "Audios" : null,
                                                    postPreviewIds.Contains(medium.Id) ? true : false, false, null);
                                                singlePostCollection.SinglePosts.Add(medium.Id,
                                                    medium.VideoSources._720);
                                                singlePostCollection.SinglePostMedia.Add(medium);
                                            }
                                        }
                                    }

                                    break;
                            }
                        }
                        else if (medium.CanView && medium.Files != null && medium.Files.Drm != null)
                        {
                            if (medium.Files != null && medium.Files.Drm != null)
                            {
                                if (!singlePostCollection.SinglePosts.ContainsKey(medium.Id))
                                {
                                    await dbService.AddMedia(folder, medium.Id, singlePost.Id,
                                        medium.Files.Drm.Manifest.Dash, null, null, null, "Posts",
                                        medium.Type == "photo" ? "Images" :
                                        medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                        medium.Type == "audio" ? "Audios" : null,
                                        postPreviewIds.Contains(medium.Id) ? true : false, false, null);
                                    singlePostCollection.SinglePosts.Add(medium.Id,
                                        $"{medium.Files.Drm.Manifest.Dash},{medium.Files.Drm.Signature.Dash.CloudFrontPolicy},{medium.Files.Drm.Signature.Dash.CloudFrontSignature},{medium.Files.Drm.Signature.Dash.CloudFrontKeyPairId},{medium.Id},{singlePost.Id}");
                                    singlePostCollection.SinglePostMedia.Add(medium);
                                }
                            }
                        }
                        else if (medium.Files.Preview != null && medium.Files!.Full == null)
                        {
                            if (!singlePostCollection.SinglePosts.ContainsKey(medium.Id))
                            {
                                await dbService.AddMedia(folder, medium.Id, singlePost.Id, medium.Files.Preview.Url,
                                    null, null, null, "Posts",
                                    medium.Type == "photo" ? "Images" :
                                    medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                    medium.Type == "audio" ? "Audios" : null,
                                    postPreviewIds.Contains(medium.Id) ? true : false, false, null);
                                singlePostCollection.SinglePosts.Add(medium.Id, medium.Files.Preview.Url);
                                singlePostCollection.SinglePostMedia.Add(medium);
                            }
                        }
                    }
                }
            }

            return singlePostCollection;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }

    public async Task<StreamEntities.StreamsCollection> GetStreams(string endpoint, string folder,
        List<long> paid_post_ids,
        StatusContext ctx)
    {
        Log.Debug($"Calling GetStreams - {endpoint}");

        try
        {
            StreamEntities.Streams streams = new();
            StreamEntities.StreamsCollection streamsCollection = new();
            int post_limit = 50;
            Dictionary<string, string> getParams = new()
            {
                { "limit", post_limit.ToString() },
                { "order", "publish_date_desc" },
                { "format", "infinite" },
                { "skip_users", "all" }
            };

            DownloadDateSelection downloadDateSelection = DownloadDateSelection.before;
            if (configService.CurrentConfig.DownloadOnlySpecificDates &&
                configService.CurrentConfig.CustomDate.HasValue)
            {
                downloadDateSelection = configService.CurrentConfig.DownloadDateSelection;
            }

            UpdateGetParamsForDateSelection(
                downloadDateSelection,
                ref getParams,
                configService.CurrentConfig.CustomDate);

            string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, new HttpClient());
            StreamsDtos.StreamsDto? streamsDto =
                JsonConvert.DeserializeObject<StreamsDtos.StreamsDto>(body, m_JsonSerializerSettings);
            streams = StreamsMapper.FromDto(streamsDto);
            ctx.Status($"[red]Getting Streams\n[/] [red]Found {streams.List.Count}[/]");
            ctx.Spinner(Spinner.Known.Dots);
            ctx.SpinnerStyle(Style.Parse("blue"));
            if (streams != null && streams.HasMore)
            {
                UpdateGetParamsForDateSelection(
                    downloadDateSelection,
                    ref getParams,
                    streams.TailMarker);

                while (true)
                {
                    StreamEntities.Streams newstreams = new();

                    string? loopbody = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient());
                    StreamsDtos.StreamsDto? newStreamsDto =
                        JsonConvert.DeserializeObject<StreamsDtos.StreamsDto>(loopbody, m_JsonSerializerSettings);
                    newstreams = StreamsMapper.FromDto(newStreamsDto);

                    streams.List.AddRange(newstreams.List);
                    ctx.Status($"[red]Getting Streams\n[/] [red]Found {streams.List.Count}[/]");
                    ctx.Spinner(Spinner.Known.Dots);
                    ctx.SpinnerStyle(Style.Parse("blue"));
                    if (!newstreams.HasMore)
                    {
                        break;
                    }

                    UpdateGetParamsForDateSelection(
                        downloadDateSelection,
                        ref getParams,
                        newstreams.TailMarker);
                }
            }

            foreach (StreamEntities.ListItem stream in streams.List)
            {
                List<long> streamPreviewIds = new();
                if (stream.Preview != null && stream.Preview.Count > 0)
                {
                    for (int i = 0; i < stream.Preview.Count; i++)
                    {
                        if (stream.Preview[i] is long previewId)
                        {
                            if (!streamPreviewIds.Contains(previewId))
                            {
                                streamPreviewIds.Add(previewId);
                            }
                        }
                    }
                }

                await dbService.AddPost(folder, stream.Id, stream.Text != null ? stream.Text : string.Empty,
                    stream.Price != null ? stream.Price : "0", stream.Price != null && stream.IsOpened ? true : false,
                    stream.IsArchived, stream.PostedAt);
                streamsCollection.StreamObjects.Add(stream);
                if (stream.Media != null && stream.Media.Count > 0)
                {
                    foreach (StreamEntities.Medium medium in stream.Media)
                    {
                        if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                        {
                            continue;
                        }

                        if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                        {
                            continue;
                        }

                        if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                        {
                            continue;
                        }

                        if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                        {
                            continue;
                        }

                        if (medium.CanView && medium.Files?.Drm == null)
                        {
                            bool has = paid_post_ids.Any(cus => cus.Equals(medium.Id));
                            if (!has && medium.CanView && medium.Files != null && medium.Files.Full != null &&
                                !string.IsNullOrEmpty(medium.Files.Full.Url))
                            {
                                if (!streamsCollection.Streams.ContainsKey(medium.Id))
                                {
                                    await dbService.AddMedia(folder, medium.Id, stream.Id, medium.Files.Full.Url, null,
                                        null, null, "Posts",
                                        medium.Type == "photo" ? "Images" :
                                        medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                        medium.Type == "audio" ? "Audios" : null,
                                        streamPreviewIds.Contains(medium.Id) ? true : false, false, null);
                                    streamsCollection.Streams.Add(medium.Id, medium.Files.Full.Url);
                                    streamsCollection.StreamMedia.Add(medium);
                                }
                            }
                        }
                        else if (medium.CanView && medium.Files != null && medium.Files.Drm != null)
                        {
                            bool has = paid_post_ids.Any(cus => cus.Equals(medium.Id));
                            if (!has && medium.Files != null && medium.Files.Drm != null)
                            {
                                if (!streamsCollection.Streams.ContainsKey(medium.Id))
                                {
                                    await dbService.AddMedia(folder, medium.Id, stream.Id,
                                        medium.Files.Drm.Manifest.Dash, null, null, null, "Posts",
                                        medium.Type == "photo" ? "Images" :
                                        medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                        medium.Type == "audio" ? "Audios" : null,
                                        streamPreviewIds.Contains(medium.Id) ? true : false, false, null);
                                    streamsCollection.Streams.Add(medium.Id,
                                        $"{medium.Files.Drm.Manifest.Dash},{medium.Files.Drm.Signature.Dash.CloudFrontPolicy},{medium.Files.Drm.Signature.Dash.CloudFrontSignature},{medium.Files.Drm.Signature.Dash.CloudFrontKeyPairId},{medium.Id},{stream.Id}");
                                    streamsCollection.StreamMedia.Add(medium);
                                }
                            }
                        }
                    }
                }
            }

            return streamsCollection;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }


    public async Task<ArchivedEntities.ArchivedCollection> GetArchived(string endpoint, string folder,
        StatusContext ctx)
    {
        Log.Debug($"Calling GetArchived - {endpoint}");

        try
        {
            ArchivedEntities.Archived archived = new();
            ArchivedEntities.ArchivedCollection archivedCollection = new();
            int post_limit = 50;
            Dictionary<string, string> getParams = new()
            {
                { "limit", post_limit.ToString() },
                { "order", "publish_date_desc" },
                { "skip_users", "all" },
                { "format", "infinite" },
                { "label", "archived" },
                { "counters", "1" }
            };

            DownloadDateSelection downloadDateSelection = DownloadDateSelection.before;
            if (configService.CurrentConfig.DownloadOnlySpecificDates &&
                configService.CurrentConfig.CustomDate.HasValue)
            {
                downloadDateSelection = configService.CurrentConfig.DownloadDateSelection;
            }

            UpdateGetParamsForDateSelection(
                downloadDateSelection,
                ref getParams,
                configService.CurrentConfig.CustomDate);

            string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient());
            ArchivedDtos.ArchivedDto archivedDto =
                JsonConvert.DeserializeObject<ArchivedDtos.ArchivedDto>(body, m_JsonSerializerSettings);
            archived = ArchivedMapper.FromDto(archivedDto);
            ctx.Status($"[red]Getting Archived Posts\n[/] [red]Found {archived.List.Count}[/]");
            ctx.Spinner(Spinner.Known.Dots);
            ctx.SpinnerStyle(Style.Parse("blue"));
            if (archived != null && archived.HasMore)
            {
                UpdateGetParamsForDateSelection(
                    downloadDateSelection,
                    ref getParams,
                    archived.TailMarker);
                while (true)
                {
                    ArchivedEntities.Archived newarchived = new();

                    string? loopbody = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient());
                    ArchivedDtos.ArchivedDto newarchivedDto =
                        JsonConvert.DeserializeObject<ArchivedDtos.ArchivedDto>(loopbody, m_JsonSerializerSettings);
                    newarchived = ArchivedMapper.FromDto(newarchivedDto);

                    archived.List.AddRange(newarchived.List);
                    ctx.Status($"[red]Getting Archived Posts\n[/] [red]Found {archived.List.Count}[/]");
                    ctx.Spinner(Spinner.Known.Dots);
                    ctx.SpinnerStyle(Style.Parse("blue"));
                    if (!newarchived.HasMore)
                    {
                        break;
                    }

                    UpdateGetParamsForDateSelection(
                        downloadDateSelection,
                        ref getParams,
                        newarchived.TailMarker);
                }
            }

            foreach (ArchivedEntities.ListItem archive in archived.List)
            {
                List<long> previewids = new();
                if (archive.Preview != null)
                {
                    for (int i = 0; i < archive.Preview.Count; i++)
                    {
                        if (archive.Preview[i] is long previewId)
                        {
                            if (!previewids.Contains(previewId))
                            {
                                previewids.Add(previewId);
                            }
                        }
                    }
                }

                await dbService.AddPost(folder, archive.Id, archive.Text != null ? archive.Text : string.Empty,
                    archive.Price != null ? archive.Price : "0",
                    archive.Price != null && archive.IsOpened ? true : false, archive.IsArchived, archive.PostedAt);
                archivedCollection.ArchivedPostObjects.Add(archive);
                if (archive.Media != null && archive.Media.Count > 0)
                {
                    foreach (ArchivedEntities.Medium medium in archive.Media)
                    {
                        if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                        {
                            continue;
                        }

                        if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                        {
                            continue;
                        }

                        if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                        {
                            continue;
                        }

                        if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                        {
                            continue;
                        }

                        if (medium.CanView && medium.Files != null && medium.Files.Full != null &&
                            !string.IsNullOrEmpty(medium.Files.Full.Url))
                        {
                            if (!archivedCollection.ArchivedPosts.ContainsKey(medium.Id))
                            {
                                await dbService.AddMedia(folder, medium.Id, archive.Id, medium.Files.Full.Url, null,
                                    null, null, "Posts",
                                    medium.Type == "photo" ? "Images" :
                                    medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                    medium.Type == "audio" ? "Audios" : null,
                                    previewids.Contains(medium.Id) ? true : false, false, null);
                                archivedCollection.ArchivedPosts.Add(medium.Id, medium.Files.Full.Url);
                                archivedCollection.ArchivedPostMedia.Add(medium);
                            }
                        }
                        else if (medium.CanView && medium.Files != null && medium.Files.Drm != null)
                        {
                            if (!archivedCollection.ArchivedPosts.ContainsKey(medium.Id))
                            {
                                await dbService.AddMedia(folder, medium.Id, archive.Id, medium.Files.Drm.Manifest.Dash,
                                    null, null, null, "Posts",
                                    medium.Type == "photo" ? "Images" :
                                    medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                    medium.Type == "audio" ? "Audios" : null,
                                    previewids.Contains(medium.Id) ? true : false, false, null);
                                archivedCollection.ArchivedPosts.Add(medium.Id,
                                    $"{medium.Files.Drm.Manifest.Dash},{medium.Files.Drm.Signature.Dash.CloudFrontPolicy},{medium.Files.Drm.Signature.Dash.CloudFrontSignature},{medium.Files.Drm.Signature.Dash.CloudFrontKeyPairId},{medium.Id},{archive.Id}");
                                archivedCollection.ArchivedPostMedia.Add(medium);
                            }
                        }
                    }
                }
            }

            return archivedCollection;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }


    public async Task<MessageEntities.MessageCollection> GetMessages(string endpoint, string folder, StatusContext ctx)
    {
        Log.Debug($"Calling GetMessages - {endpoint}");

        try
        {
            MessageEntities.Messages messages = new();
            MessageEntities.MessageCollection messageCollection = new();
            int post_limit = 50;
            Dictionary<string, string> getParams = new()
            {
                { "limit", post_limit.ToString() }, { "order", "desc" }, { "skip_users", "all" }
            };

            string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient());
            MessageDtos.MessagesDto? messagesDto =
                JsonConvert.DeserializeObject<MessageDtos.MessagesDto>(body, m_JsonSerializerSettings);
            messages = MessagesMapper.FromDto(messagesDto);
            ctx.Status($"[red]Getting Messages\n[/] [red]Found {messages.List.Count}[/]");
            ctx.Spinner(Spinner.Known.Dots);
            ctx.SpinnerStyle(Style.Parse("blue"));
            if (messages.HasMore)
            {
                getParams["id"] = messages.List[^1].Id.ToString();
                while (true)
                {
                    MessageEntities.Messages newMessages = new();

                    string? loopbody = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient());
                    MessageDtos.MessagesDto? newMessagesDto =
                        JsonConvert.DeserializeObject<MessageDtos.MessagesDto>(loopbody, m_JsonSerializerSettings);
                    newMessages = MessagesMapper.FromDto(newMessagesDto);

                    messages.List.AddRange(newMessages.List);
                    ctx.Status($"[red]Getting Messages\n[/] [red]Found {messages.List.Count}[/]");
                    ctx.Spinner(Spinner.Known.Dots);
                    ctx.SpinnerStyle(Style.Parse("blue"));
                    if (!newMessages.HasMore)
                    {
                        break;
                    }

                    getParams["id"] = newMessages.List[newMessages.List.Count - 1].Id.ToString();
                }
            }

            foreach (MessageEntities.ListItem list in messages.List)
            {
                if (configService.CurrentConfig.SkipAds)
                {
                    if (!string.IsNullOrEmpty(list.Text) &&
                        (list.Text.Contains("#ad") || list.Text.Contains("/trial/")))
                    {
                        continue;
                    }
                }

                List<long> messagePreviewIds = new();
                if (list.Previews != null && list.Previews.Count > 0)
                {
                    for (int i = 0; i < list.Previews.Count; i++)
                    {
                        if (list.Previews[i] is long previewId)
                        {
                            if (!messagePreviewIds.Contains(previewId))
                            {
                                messagePreviewIds.Add(previewId);
                            }
                        }
                    }
                }

                if (!configService.CurrentConfig.IgnoreOwnMessages ||
                    list.FromUser?.Id != Convert.ToInt32(authService.CurrentAuth.USER_ID))
                {
                    await dbService.AddMessage(folder, list.Id, list.Text ?? string.Empty, list.Price ?? "0",
                        list.CanPurchaseReason == "opened" ? true :
                        list.CanPurchaseReason != "opened" ? false : (bool?)null ?? false, false,
                        list.CreatedAt.HasValue ? list.CreatedAt.Value : DateTime.Now,
                        list.FromUser?.Id ?? int.MinValue);
                    messageCollection.MessageObjects.Add(list);
                    if (list.CanPurchaseReason != "opened" && list.Media != null && list.Media.Count > 0)
                    {
                        foreach (MessageEntities.Medium medium in list.Media)
                        {
                            if (medium.CanView && medium.Files != null && medium.Files.Full != null &&
                                !string.IsNullOrEmpty(medium.Files.Full.Url))
                            {
                                if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                                {
                                    continue;
                                }

                                if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                                {
                                    continue;
                                }

                                if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                                {
                                    continue;
                                }

                                if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                                {
                                    continue;
                                }

                                if (!messageCollection.Messages.ContainsKey(medium.Id))
                                {
                                    await dbService.AddMedia(folder, medium.Id, list.Id, medium.Files.Full.Url, null,
                                        null, null, "Messages",
                                        medium.Type == "photo" ? "Images" :
                                        medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                        medium.Type == "audio" ? "Audios" : null,
                                        messagePreviewIds.Contains(medium.Id), false, null);
                                    messageCollection.Messages.Add(medium.Id, medium.Files.Full.Url);
                                    messageCollection.MessageMedia.Add(medium);
                                }
                            }
                            else if (medium.CanView && medium.Files != null && medium.Files.Drm != null)
                            {
                                if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                                {
                                    continue;
                                }

                                if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                                {
                                    continue;
                                }

                                if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                                {
                                    continue;
                                }

                                if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                                {
                                    continue;
                                }

                                if (!messageCollection.Messages.ContainsKey(medium.Id))
                                {
                                    await dbService.AddMedia(folder, medium.Id, list.Id, medium.Files.Drm.Manifest.Dash,
                                        null, null, null, "Messages",
                                        medium.Type == "photo" ? "Images" :
                                        medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                        medium.Type == "audio" ? "Audios" : null,
                                        messagePreviewIds.Contains(medium.Id), false, null);
                                    messageCollection.Messages.Add(medium.Id,
                                        $"{medium.Files.Drm.Manifest.Dash},{medium.Files.Drm.Signature.Dash.CloudFrontPolicy},{medium.Files.Drm.Signature.Dash.CloudFrontSignature},{medium.Files.Drm.Signature.Dash.CloudFrontKeyPairId},{medium.Id},{list.Id}");
                                    messageCollection.MessageMedia.Add(medium);
                                }
                            }
                        }
                    }
                    else if (messagePreviewIds.Count > 0)
                    {
                        foreach (MessageEntities.Medium medium in list.Media)
                        {
                            if (medium.CanView && medium.Files != null && medium.Files.Full != null &&
                                !string.IsNullOrEmpty(medium.Files.Full.Url) && messagePreviewIds.Contains(medium.Id))
                            {
                                if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                                {
                                    continue;
                                }

                                if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                                {
                                    continue;
                                }

                                if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                                {
                                    continue;
                                }

                                if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                                {
                                    continue;
                                }

                                if (!messageCollection.Messages.ContainsKey(medium.Id))
                                {
                                    await dbService.AddMedia(folder, medium.Id, list.Id, medium.Files.Full.Url, null,
                                        null, null, "Messages",
                                        medium.Type == "photo" ? "Images" :
                                        medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                        medium.Type == "audio" ? "Audios" : null,
                                        messagePreviewIds.Contains(medium.Id), false, null);
                                    messageCollection.Messages.Add(medium.Id, medium.Files.Full.Url);
                                    messageCollection.MessageMedia.Add(medium);
                                }
                            }
                            else if (medium.CanView && medium.Files != null && medium.Files.Drm != null &&
                                     messagePreviewIds.Contains(medium.Id))
                            {
                                if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                                {
                                    continue;
                                }

                                if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                                {
                                    continue;
                                }

                                if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                                {
                                    continue;
                                }

                                if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                                {
                                    continue;
                                }

                                if (!messageCollection.Messages.ContainsKey(medium.Id))
                                {
                                    await dbService.AddMedia(folder, medium.Id, list.Id, medium.Files.Drm.Manifest.Dash,
                                        null, null, null, "Messages",
                                        medium.Type == "photo" ? "Images" :
                                        medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                        medium.Type == "audio" ? "Audios" : null,
                                        messagePreviewIds.Contains(medium.Id), false, null);
                                    messageCollection.Messages.Add(medium.Id,
                                        $"{medium.Files.Drm.Manifest.Dash},{medium.Files.Drm.Signature.Dash.CloudFrontPolicy},{medium.Files.Drm.Signature.Dash.CloudFrontSignature},{medium.Files.Drm.Signature.Dash.CloudFrontKeyPairId},{medium.Id},{list.Id}");
                                    messageCollection.MessageMedia.Add(medium);
                                }
                            }
                        }
                    }
                }
            }

            return messageCollection;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }

    public async Task<PurchasedEntities.SinglePaidMessageCollection> GetPaidMessage(string endpoint, string folder)
    {
        Log.Debug($"Calling GetPaidMessage - {endpoint}");

        try
        {
            MessageEntities.SingleMessage message = new();
            PurchasedEntities.SinglePaidMessageCollection singlePaidMessageCollection = new();
            int post_limit = 50;
            Dictionary<string, string> getParams = new() { { "limit", post_limit.ToString() }, { "order", "desc" } };

            string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient());
            MessageDtos.SingleMessageDto? messageDto =
                JsonConvert.DeserializeObject<MessageDtos.SingleMessageDto>(body, m_JsonSerializerSettings);
            message = MessagesMapper.FromDto(messageDto);

            if (!configService.CurrentConfig.IgnoreOwnMessages ||
                message.FromUser?.Id != Convert.ToInt32(authService.CurrentAuth.USER_ID))
            {
                await dbService.AddMessage(folder, message.Id, message.Text ?? string.Empty,
                    message.Price != null ? message.Price.ToString() : "0", true, false,
                    message.CreatedAt.HasValue ? message.CreatedAt.Value : DateTime.Now,
                    message.FromUser?.Id ?? int.MinValue);
                singlePaidMessageCollection.SingleMessageObjects.Add(message);
                List<long> messagePreviewIds = new();
                if (message.Previews != null && message.Previews.Count > 0)
                {
                    for (int i = 0; i < message.Previews.Count; i++)
                    {
                        if (message.Previews[i] is long previewId)
                        {
                            if (!messagePreviewIds.Contains(previewId))
                            {
                                messagePreviewIds.Add(previewId);
                            }
                        }
                    }
                }

                if (message.Media != null && message.Media.Count > 0)
                {
                    foreach (MessageEntities.Medium medium in message.Media)
                    {
                        if (!messagePreviewIds.Contains(medium.Id) && medium.CanView && medium.Files != null &&
                            medium.Files.Full != null && !string.IsNullOrEmpty(medium.Files.Full.Url))
                        {
                            if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                            {
                                continue;
                            }

                            if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                            {
                                continue;
                            }

                            if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                            {
                                continue;
                            }

                            if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                            {
                                continue;
                            }

                            if (!singlePaidMessageCollection.SingleMessages.ContainsKey(medium.Id))
                            {
                                await dbService.AddMedia(folder, medium.Id, message.Id, medium.Files.Full.Url, null,
                                    null, null, "Messages",
                                    medium.Type == "photo" ? "Images" :
                                    medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                    medium.Type == "audio" ? "Audios" : null,
                                    messagePreviewIds.Contains(medium.Id), false, null);
                                singlePaidMessageCollection.SingleMessages.Add(medium.Id, medium.Files.Full.Url);
                                singlePaidMessageCollection.SingleMessageMedia.Add(medium);
                            }
                        }
                        else if (messagePreviewIds.Contains(medium.Id) && medium.CanView && medium.Files != null &&
                                 medium.Files.Full != null && !string.IsNullOrEmpty(medium.Files.Full.Url))
                        {
                            if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                            {
                                continue;
                            }

                            if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                            {
                                continue;
                            }

                            if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                            {
                                continue;
                            }

                            if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                            {
                                continue;
                            }

                            if (!singlePaidMessageCollection.PreviewSingleMessages.ContainsKey(medium.Id))
                            {
                                await dbService.AddMedia(folder, medium.Id, message.Id, medium.Files.Full.Url, null,
                                    null, null, "Messages",
                                    medium.Type == "photo" ? "Images" :
                                    medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                    medium.Type == "audio" ? "Audios" : null,
                                    messagePreviewIds.Contains(medium.Id), false, null);
                                singlePaidMessageCollection.PreviewSingleMessages.Add(medium.Id, medium.Files.Full.Url);
                                singlePaidMessageCollection.PreviewSingleMessageMedia.Add(medium);
                            }
                        }
                        else if (!messagePreviewIds.Contains(medium.Id) && medium.CanView && medium.Files != null &&
                                 medium.Files.Drm != null)
                        {
                            if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                            {
                                continue;
                            }

                            if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                            {
                                continue;
                            }

                            if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                            {
                                continue;
                            }

                            if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                            {
                                continue;
                            }

                            if (!singlePaidMessageCollection.SingleMessages.ContainsKey(medium.Id))
                            {
                                await dbService.AddMedia(folder, medium.Id, message.Id, medium.Files.Drm.Manifest.Dash,
                                    null, null, null, "Messages",
                                    medium.Type == "photo" ? "Images" :
                                    medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                    medium.Type == "audio" ? "Audios" : null,
                                    messagePreviewIds.Contains(medium.Id), false, null);
                                singlePaidMessageCollection.SingleMessages.Add(medium.Id,
                                    $"{medium.Files.Drm.Manifest.Dash},{medium.Files.Drm.Signature.Dash.CloudFrontPolicy},{medium.Files.Drm.Signature.Dash.CloudFrontSignature},{medium.Files.Drm.Signature.Dash.CloudFrontKeyPairId},{medium.Id},{message.Id}");
                                singlePaidMessageCollection.SingleMessageMedia.Add(medium);
                            }
                        }
                        else if (messagePreviewIds.Contains(medium.Id) && medium.CanView && medium.Files != null &&
                                 medium.Files.Drm != null)
                        {
                            if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                            {
                                continue;
                            }

                            if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                            {
                                continue;
                            }

                            if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                            {
                                continue;
                            }

                            if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                            {
                                continue;
                            }

                            if (!singlePaidMessageCollection.PreviewSingleMessages.ContainsKey(medium.Id))
                            {
                                await dbService.AddMedia(folder, medium.Id, message.Id, medium.Files.Drm.Manifest.Dash,
                                    null, null, null, "Messages",
                                    medium.Type == "photo" ? "Images" :
                                    medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                    medium.Type == "audio" ? "Audios" : null,
                                    messagePreviewIds.Contains(medium.Id), false, null);
                                singlePaidMessageCollection.PreviewSingleMessages.Add(medium.Id,
                                    $"{medium.Files.Drm.Manifest.Dash},{medium.Files.Drm.Signature.Dash.CloudFrontPolicy},{medium.Files.Drm.Signature.Dash.CloudFrontSignature},{medium.Files.Drm.Signature.Dash.CloudFrontKeyPairId},{medium.Id},{message.Id}");
                                singlePaidMessageCollection.PreviewSingleMessageMedia.Add(medium);
                            }
                        }
                    }
                }
            }

            return singlePaidMessageCollection;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }


    public async Task<PurchasedEntities.PaidMessageCollection> GetPaidMessages(string endpoint, string folder,
        string username,
        StatusContext ctx)
    {
        Log.Debug($"Calling GetPaidMessages - {username}");

        try
        {
            PurchasedEntities.Purchased paidMessages = new();
            PurchasedEntities.PaidMessageCollection paidMessageCollection = new();
            int post_limit = 50;
            Dictionary<string, string> getParams = new()
            {
                { "limit", post_limit.ToString() },
                { "order", "publish_date_desc" },
                { "format", "infinite" },
                { "author", username },
                { "skip_users", "all" }
            };

            string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient());
            PurchasedDtos.PurchasedDto? paidMessagesDto =
                JsonConvert.DeserializeObject<PurchasedDtos.PurchasedDto>(body, m_JsonSerializerSettings);
            paidMessages = PurchasedMapper.FromDto(paidMessagesDto);
            ctx.Status($"[red]Getting Paid Messages\n[/] [red]Found {paidMessages.List.Count}[/]");
            ctx.Spinner(Spinner.Known.Dots);
            ctx.SpinnerStyle(Style.Parse("blue"));
            if (paidMessages != null && paidMessages.HasMore)
            {
                getParams["offset"] = paidMessages.List.Count.ToString();
                while (true)
                {
                    string loopqueryParams = "?" + string.Join("&", getParams.Select(kvp => $"{kvp.Key}={kvp.Value}"));
                    PurchasedEntities.Purchased newpaidMessages = new();
                    Dictionary<string, string> loopheaders = GetDynamicHeaders("/api2/v2" + endpoint, loopqueryParams);
                    HttpClient loopclient = GetHttpClient();

                    HttpRequestMessage looprequest =
                        new(HttpMethod.Get, $"{Constants.API_URL}{endpoint}{loopqueryParams}");

                    foreach (KeyValuePair<string, string> keyValuePair in loopheaders)
                    {
                        looprequest.Headers.Add(keyValuePair.Key, keyValuePair.Value);
                    }

                    using (HttpResponseMessage loopresponse = await loopclient.SendAsync(looprequest))
                    {
                        loopresponse.EnsureSuccessStatusCode();
                        string loopbody = await loopresponse.Content.ReadAsStringAsync();
                        PurchasedDtos.PurchasedDto? newPaidMessagesDto =
                            JsonConvert.DeserializeObject<PurchasedDtos.PurchasedDto>(loopbody,
                                m_JsonSerializerSettings);
                        newpaidMessages = PurchasedMapper.FromDto(newPaidMessagesDto);
                    }

                    paidMessages.List.AddRange(newpaidMessages.List);
                    ctx.Status($"[red]Getting Paid Messages\n[/] [red]Found {paidMessages.List.Count}[/]");
                    ctx.Spinner(Spinner.Known.Dots);
                    ctx.SpinnerStyle(Style.Parse("blue"));
                    if (!newpaidMessages.HasMore)
                    {
                        break;
                    }

                    getParams["offset"] = Convert.ToString(Convert.ToInt32(getParams["offset"]) + post_limit);
                }
            }

            if (paidMessages.List != null && paidMessages.List.Count > 0)
            {
                foreach (PurchasedEntities.ListItem purchase in paidMessages.List
                             .Where(p => p.ResponseType == "message")
                             .OrderByDescending(p => p.PostedAt ?? p.CreatedAt))
                {
                    if (!configService.CurrentConfig.IgnoreOwnMessages ||
                        purchase.FromUser.Id != Convert.ToInt32(authService.CurrentAuth.USER_ID))
                    {
                        if (purchase.PostedAt != null)
                        {
                            await dbService.AddMessage(folder, purchase.Id,
                                purchase.Text != null ? purchase.Text : string.Empty,
                                purchase.Price != null ? purchase.Price : "0", true, false, purchase.PostedAt.Value,
                                purchase.FromUser.Id);
                        }
                        else
                        {
                            await dbService.AddMessage(folder, purchase.Id,
                                purchase.Text != null ? purchase.Text : string.Empty,
                                purchase.Price != null ? purchase.Price : "0", true, false, purchase.CreatedAt.Value,
                                purchase.FromUser.Id);
                        }

                        paidMessageCollection.PaidMessageObjects.Add(purchase);
                        if (purchase.Media != null && purchase.Media.Count > 0)
                        {
                            List<long> previewids = new();
                            if (purchase.Previews != null)
                            {
                                for (int i = 0; i < purchase.Previews.Count; i++)
                                {
                                    if (purchase.Previews[i] is long previewId)
                                    {
                                        if (!previewids.Contains(previewId))
                                        {
                                            previewids.Add(previewId);
                                        }
                                    }
                                }
                            }
                            else if (purchase.Preview != null)
                            {
                                for (int i = 0; i < purchase.Preview.Count; i++)
                                {
                                    if (purchase.Preview[i] is long previewId)
                                    {
                                        if (!previewids.Contains(previewId))
                                        {
                                            previewids.Add(previewId);
                                        }
                                    }
                                }
                            }

                            foreach (MessageEntities.Medium medium in purchase.Media)
                            {
                                if (previewids.Count > 0)
                                {
                                    bool has = previewids.Any(cus => cus.Equals(medium.Id));
                                    if (!has && medium.CanView && medium.Files != null && medium.Files.Full != null &&
                                        !string.IsNullOrEmpty(medium.Files.Full.Url))
                                    {
                                        if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                                        {
                                            continue;
                                        }

                                        if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                                        {
                                            continue;
                                        }

                                        if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                                        {
                                            continue;
                                        }

                                        if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                                        {
                                            continue;
                                        }

                                        if (!paidMessageCollection.PaidMessages.ContainsKey(medium.Id))
                                        {
                                            await dbService.AddMedia(folder, medium.Id, purchase.Id,
                                                medium.Files.Full.Url, null, null, null, "Messages",
                                                medium.Type == "photo" ? "Images" :
                                                medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                                medium.Type == "audio" ? "Audios" : null,
                                                previewids.Contains(medium.Id), false, null);
                                            paidMessageCollection.PaidMessages.Add(medium.Id, medium.Files.Full.Url);
                                            paidMessageCollection.PaidMessageMedia.Add(medium);
                                        }
                                    }
                                    else if (!has && medium.CanView && medium.Files != null && medium.Files.Drm != null)
                                    {
                                        if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                                        {
                                            continue;
                                        }

                                        if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                                        {
                                            continue;
                                        }

                                        if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                                        {
                                            continue;
                                        }

                                        if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                                        {
                                            continue;
                                        }

                                        if (!paidMessageCollection.PaidMessages.ContainsKey(medium.Id))
                                        {
                                            await dbService.AddMedia(folder, medium.Id, purchase.Id,
                                                medium.Files.Drm.Manifest.Dash, null, null, null, "Messages",
                                                medium.Type == "photo" ? "Images" :
                                                medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                                medium.Type == "audio" ? "Audios" : null,
                                                previewids.Contains(medium.Id), false, null);
                                            paidMessageCollection.PaidMessages.Add(medium.Id,
                                                $"{medium.Files.Drm.Manifest.Dash},{medium.Files.Drm.Signature.Dash.CloudFrontPolicy},{medium.Files.Drm.Signature.Dash.CloudFrontSignature},{medium.Files.Drm.Signature.Dash.CloudFrontKeyPairId},{medium.Id},{purchase.Id}");
                                            paidMessageCollection.PaidMessageMedia.Add(medium);
                                        }
                                    }
                                }
                                else
                                {
                                    if (medium.CanView && medium.Files != null && medium.Files.Full != null &&
                                        !string.IsNullOrEmpty(medium.Files.Full.Url))
                                    {
                                        if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                                        {
                                            continue;
                                        }

                                        if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                                        {
                                            continue;
                                        }

                                        if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                                        {
                                            continue;
                                        }

                                        if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                                        {
                                            continue;
                                        }

                                        if (!paidMessageCollection.PaidMessages.ContainsKey(medium.Id))
                                        {
                                            await dbService.AddMedia(folder, medium.Id, purchase.Id,
                                                medium.Files.Full.Url, null, null, null, "Messages",
                                                medium.Type == "photo" ? "Images" :
                                                medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                                medium.Type == "audio" ? "Audios" : null,
                                                previewids.Contains(medium.Id), false, null);
                                            paidMessageCollection.PaidMessages.Add(medium.Id, medium.Files.Full.Url);
                                            paidMessageCollection.PaidMessageMedia.Add(medium);
                                        }
                                    }
                                    else if (medium.CanView && medium.Files != null && medium.Files.Drm != null)
                                    {
                                        if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                                        {
                                            continue;
                                        }

                                        if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                                        {
                                            continue;
                                        }

                                        if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                                        {
                                            continue;
                                        }

                                        if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                                        {
                                            continue;
                                        }

                                        if (!paidMessageCollection.PaidMessages.ContainsKey(medium.Id))
                                        {
                                            await dbService.AddMedia(folder, medium.Id, purchase.Id,
                                                medium.Files.Drm.Manifest.Dash, null, null, null, "Messages",
                                                medium.Type == "photo" ? "Images" :
                                                medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                                medium.Type == "audio" ? "Audios" : null,
                                                previewids.Contains(medium.Id), false, null);
                                            paidMessageCollection.PaidMessages.Add(medium.Id,
                                                $"{medium.Files.Drm.Manifest.Dash},{medium.Files.Drm.Signature.Dash.CloudFrontPolicy},{medium.Files.Drm.Signature.Dash.CloudFrontSignature},{medium.Files.Drm.Signature.Dash.CloudFrontKeyPairId},{medium.Id},{purchase.Id}");
                                            paidMessageCollection.PaidMessageMedia.Add(medium);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return paidMessageCollection;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }

    public async Task<Dictionary<string, long>> GetPurchasedTabUsers(string endpoint, Dictionary<string, long> users)
    {
        Log.Debug($"Calling GetPurchasedTabUsers - {endpoint}");

        try
        {
            Dictionary<string, long> purchasedTabUsers = new();
            PurchasedEntities.Purchased purchased = new();
            int post_limit = 50;
            Dictionary<string, string> getParams = new()
            {
                { "limit", post_limit.ToString() },
                { "order", "publish_date_desc" },
                { "format", "infinite" },
                { "skip_users", "all" }
            };

            string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient());
            PurchasedDtos.PurchasedDto? purchasedDto =
                JsonConvert.DeserializeObject<PurchasedDtos.PurchasedDto>(body, m_JsonSerializerSettings);
            purchased = PurchasedMapper.FromDto(purchasedDto);
            if (purchased != null && purchased.HasMore)
            {
                getParams["offset"] = purchased.List.Count.ToString();
                while (true)
                {
                    string loopqueryParams = "?" + string.Join("&", getParams.Select(kvp => $"{kvp.Key}={kvp.Value}"));
                    PurchasedEntities.Purchased newPurchased = new();
                    Dictionary<string, string> loopheaders = GetDynamicHeaders("/api2/v2" + endpoint, loopqueryParams);
                    HttpClient loopclient = GetHttpClient();

                    HttpRequestMessage looprequest =
                        new(HttpMethod.Get, $"{Constants.API_URL}{endpoint}{loopqueryParams}");

                    foreach (KeyValuePair<string, string> keyValuePair in loopheaders)
                    {
                        looprequest.Headers.Add(keyValuePair.Key, keyValuePair.Value);
                    }

                    using (HttpResponseMessage loopresponse = await loopclient.SendAsync(looprequest))
                    {
                        loopresponse.EnsureSuccessStatusCode();
                        string loopbody = await loopresponse.Content.ReadAsStringAsync();
                        PurchasedDtos.PurchasedDto? newPurchasedDto =
                            JsonConvert.DeserializeObject<PurchasedDtos.PurchasedDto>(loopbody,
                                m_JsonSerializerSettings);
                        newPurchased = PurchasedMapper.FromDto(newPurchasedDto);
                    }

                    purchased.List.AddRange(newPurchased.List);
                    if (!newPurchased.HasMore)
                    {
                        break;
                    }

                    getParams["offset"] = Convert.ToString(Convert.ToInt32(getParams["offset"]) + post_limit);
                }
            }

            if (purchased.List != null && purchased.List.Count > 0)
            {
                foreach (PurchasedEntities.ListItem purchase in
                         purchased.List.OrderByDescending(p => p.PostedAt ?? p.CreatedAt))
                {
                    if (purchase.FromUser != null)
                    {
                        if (users.Values.Contains(purchase.FromUser.Id))
                        {
                            if (!string.IsNullOrEmpty(users.FirstOrDefault(x => x.Value == purchase.FromUser.Id).Key))
                            {
                                if (!purchasedTabUsers.ContainsKey(users
                                        .FirstOrDefault(x => x.Value == purchase.FromUser.Id).Key))
                                {
                                    purchasedTabUsers.Add(
                                        users.FirstOrDefault(x => x.Value == purchase.FromUser.Id).Key,
                                        purchase.FromUser.Id);
                                }
                            }
                            else
                            {
                                if (!purchasedTabUsers.ContainsKey($"Deleted User - {purchase.FromUser.Id}"))
                                {
                                    purchasedTabUsers.Add($"Deleted User - {purchase.FromUser.Id}",
                                        purchase.FromUser.Id);
                                }
                            }
                        }
                        else
                        {
                            JObject user = await GetUserInfoById($"/users/list?x[]={purchase.FromUser.Id}");

                            if (user is null)
                            {
                                if (!configService.CurrentConfig.BypassContentForCreatorsWhoNoLongerExist)
                                {
                                    if (!purchasedTabUsers.ContainsKey($"Deleted User - {purchase.FromUser.Id}"))
                                    {
                                        purchasedTabUsers.Add($"Deleted User - {purchase.FromUser.Id}",
                                            purchase.FromUser.Id);
                                    }
                                }

                                Log.Debug("Content creator not longer exists - {0}", purchase.FromUser.Id);
                            }
                            else if (!string.IsNullOrEmpty(user[purchase.FromUser.Id.ToString()]["username"]
                                         .ToString()))
                            {
                                if (!purchasedTabUsers.ContainsKey(user[purchase.FromUser.Id.ToString()]["username"]
                                        .ToString()))
                                {
                                    purchasedTabUsers.Add(user[purchase.FromUser.Id.ToString()]["username"].ToString(),
                                        purchase.FromUser.Id);
                                }
                            }
                            else
                            {
                                if (!purchasedTabUsers.ContainsKey($"Deleted User - {purchase.FromUser.Id}"))
                                {
                                    purchasedTabUsers.Add($"Deleted User - {purchase.FromUser.Id}",
                                        purchase.FromUser.Id);
                                }
                            }
                        }
                    }
                    else if (purchase.Author != null)
                    {
                        if (users.Values.Contains(purchase.Author.Id))
                        {
                            if (!string.IsNullOrEmpty(users.FirstOrDefault(x => x.Value == purchase.Author.Id).Key))
                            {
                                if (!purchasedTabUsers.ContainsKey(users
                                        .FirstOrDefault(x => x.Value == purchase.Author.Id).Key) &&
                                    users.ContainsKey(users.FirstOrDefault(x => x.Value == purchase.Author.Id).Key))
                                {
                                    purchasedTabUsers.Add(users.FirstOrDefault(x => x.Value == purchase.Author.Id).Key,
                                        purchase.Author.Id);
                                }
                            }
                            else
                            {
                                if (!purchasedTabUsers.ContainsKey($"Deleted User - {purchase.Author.Id}"))
                                {
                                    purchasedTabUsers.Add($"Deleted User - {purchase.Author.Id}", purchase.Author.Id);
                                }
                            }
                        }
                        else
                        {
                            JObject user = await GetUserInfoById($"/users/list?x[]={purchase.Author.Id}");

                            if (user is null)
                            {
                                if (!configService.CurrentConfig.BypassContentForCreatorsWhoNoLongerExist)
                                {
                                    if (!purchasedTabUsers.ContainsKey($"Deleted User - {purchase.Author.Id}"))
                                    {
                                        purchasedTabUsers.Add($"Deleted User - {purchase.Author.Id}",
                                            purchase.Author.Id);
                                    }
                                }

                                Log.Debug("Content creator not longer exists - {0}", purchase.Author.Id);
                            }
                            else if (!string.IsNullOrEmpty(user[purchase.Author.Id.ToString()]["username"].ToString()))
                            {
                                if (!purchasedTabUsers.ContainsKey(user[purchase.Author.Id.ToString()]["username"]
                                        .ToString()) &&
                                    users.ContainsKey(user[purchase.Author.Id.ToString()]["username"].ToString()))
                                {
                                    purchasedTabUsers.Add(user[purchase.Author.Id.ToString()]["username"].ToString(),
                                        purchase.Author.Id);
                                }
                            }
                            else
                            {
                                if (!purchasedTabUsers.ContainsKey($"Deleted User - {purchase.Author.Id}"))
                                {
                                    purchasedTabUsers.Add($"Deleted User - {purchase.Author.Id}", purchase.Author.Id);
                                }
                            }
                        }
                    }
                }
            }

            return purchasedTabUsers;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }

    public async Task<List<PurchasedEntities.PurchasedTabCollection>> GetPurchasedTab(string endpoint, string folder,
        Dictionary<string, long> users)
    {
        Log.Debug($"Calling GetPurchasedTab - {endpoint}");

        try
        {
            Dictionary<long, List<PurchasedEntities.ListItem>> userPurchases = new();
            List<PurchasedEntities.PurchasedTabCollection> purchasedTabCollections = [];
            PurchasedEntities.Purchased purchased = new();
            int post_limit = 50;
            Dictionary<string, string> getParams = new()
            {
                { "limit", post_limit.ToString() },
                { "order", "publish_date_desc" },
                { "format", "infinite" },
                { "skip_users", "all" }
            };

            string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient());
            PurchasedDtos.PurchasedDto? purchasedDto =
                JsonConvert.DeserializeObject<PurchasedDtos.PurchasedDto>(body, m_JsonSerializerSettings);
            purchased = PurchasedMapper.FromDto(purchasedDto);
            if (purchased != null && purchased.HasMore)
            {
                getParams["offset"] = purchased.List.Count.ToString();
                while (true)
                {
                    string loopqueryParams = "?" + string.Join("&", getParams.Select(kvp => $"{kvp.Key}={kvp.Value}"));
                    PurchasedEntities.Purchased newPurchased = new();
                    Dictionary<string, string> loopheaders = GetDynamicHeaders("/api2/v2" + endpoint, loopqueryParams);
                    HttpClient loopclient = GetHttpClient();

                    HttpRequestMessage looprequest =
                        new(HttpMethod.Get, $"{Constants.API_URL}{endpoint}{loopqueryParams}");

                    foreach (KeyValuePair<string, string> keyValuePair in loopheaders)
                    {
                        looprequest.Headers.Add(keyValuePair.Key, keyValuePair.Value);
                    }

                    using (HttpResponseMessage loopresponse = await loopclient.SendAsync(looprequest))
                    {
                        loopresponse.EnsureSuccessStatusCode();
                        string loopbody = await loopresponse.Content.ReadAsStringAsync();
                        PurchasedDtos.PurchasedDto? newPurchasedDto =
                            JsonConvert.DeserializeObject<PurchasedDtos.PurchasedDto>(loopbody,
                                m_JsonSerializerSettings);
                        newPurchased = PurchasedMapper.FromDto(newPurchasedDto);
                    }

                    purchased.List.AddRange(newPurchased.List);
                    if (!newPurchased.HasMore)
                    {
                        break;
                    }

                    getParams["offset"] = Convert.ToString(Convert.ToInt32(getParams["offset"]) + post_limit);
                }
            }

            if (purchased.List != null && purchased.List.Count > 0)
            {
                foreach (PurchasedEntities.ListItem purchase in
                         purchased.List.OrderByDescending(p => p.PostedAt ?? p.CreatedAt))
                {
                    if (purchase.FromUser != null)
                    {
                        if (!userPurchases.ContainsKey(purchase.FromUser.Id))
                        {
                            userPurchases.Add(purchase.FromUser.Id, new List<PurchasedEntities.ListItem>());
                        }

                        userPurchases[purchase.FromUser.Id].Add(purchase);
                    }
                    else if (purchase.Author != null)
                    {
                        if (!userPurchases.ContainsKey(purchase.Author.Id))
                        {
                            userPurchases.Add(purchase.Author.Id, new List<PurchasedEntities.ListItem>());
                        }

                        userPurchases[purchase.Author.Id].Add(purchase);
                    }
                }
            }

            foreach (KeyValuePair<long, List<PurchasedEntities.ListItem>> user in userPurchases)
            {
                PurchasedEntities.PurchasedTabCollection purchasedTabCollection = new();
                JObject userObject = await GetUserInfoById($"/users/list?x[]={user.Key}");
                purchasedTabCollection.UserId = user.Key;
                purchasedTabCollection.Username =
                    userObject is not null &&
                    !string.IsNullOrEmpty(userObject[user.Key.ToString()]["username"].ToString())
                        ? userObject[user.Key.ToString()]["username"].ToString()
                        : $"Deleted User - {user.Key}";
                string path = Path.Combine(folder, purchasedTabCollection.Username);
                if (Path.Exists(path))
                {
                    foreach (PurchasedEntities.ListItem purchase in user.Value)
                    {
                        if (purchase.Media == null)
                        {
                            Log.Warning(
                                "PurchasedTab purchase media null, setting empty list | userId={UserId} username={Username} purchaseId={PurchaseId} responseType={ResponseType} createdAt={CreatedAt} postedAt={PostedAt}",
                                user.Key, purchasedTabCollection.Username, purchase.Id, purchase.ResponseType,
                                purchase.CreatedAt, purchase.PostedAt);
                            purchase.Media = new List<MessageEntities.Medium>();
                        }

                        switch (purchase.ResponseType)
                        {
                            case "post":
                                List<long> previewids = new();
                                if (purchase.Previews != null)
                                {
                                    for (int i = 0; i < purchase.Previews.Count; i++)
                                    {
                                        if (purchase.Previews[i] is long previewId)
                                        {
                                            if (!previewids.Contains(previewId))
                                            {
                                                previewids.Add(previewId);
                                            }
                                        }
                                    }
                                }
                                else if (purchase.Preview != null)
                                {
                                    for (int i = 0; i < purchase.Preview.Count; i++)
                                    {
                                        if (purchase.Preview[i] is long previewId)
                                        {
                                            if (!previewids.Contains(previewId))
                                            {
                                                previewids.Add(previewId);
                                            }
                                        }
                                    }
                                }

                                await dbService.AddPost(path, purchase.Id,
                                    purchase.Text != null ? purchase.Text : string.Empty,
                                    purchase.Price != null ? purchase.Price : "0",
                                    purchase.Price != null && purchase.IsOpened ? true : false,
                                    purchase.IsArchived.HasValue ? purchase.IsArchived.Value : false,
                                    purchase.CreatedAt != null ? purchase.CreatedAt.Value : purchase.PostedAt.Value);
                                purchasedTabCollection.PaidPosts.PaidPostObjects.Add(purchase);
                                foreach (MessageEntities.Medium medium in purchase.Media)
                                {
                                    if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages)
                                    {
                                        continue;
                                    }

                                    if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos)
                                    {
                                        continue;
                                    }

                                    if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                                    {
                                        continue;
                                    }

                                    if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios)
                                    {
                                        continue;
                                    }

                                    if (previewids.Count > 0)
                                    {
                                        bool has = previewids.Any(cus => cus.Equals(medium.Id));
                                        if (!has && medium.CanView && medium.Files != null &&
                                            medium.Files.Full != null && !string.IsNullOrEmpty(medium.Files.Full.Url))
                                        {
                                            if (!purchasedTabCollection.PaidPosts.PaidPosts.ContainsKey(medium.Id))
                                            {
                                                await dbService.AddMedia(path, medium.Id, purchase.Id,
                                                    medium.Files.Full.Url, null, null, null, "Posts",
                                                    medium.Type == "photo" ? "Images" :
                                                    medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                                    medium.Type == "audio" ? "Audios" : null,
                                                    previewids.Contains(medium.Id), false, null);
                                                purchasedTabCollection.PaidPosts.PaidPosts.Add(medium.Id,
                                                    medium.Files.Full.Url);
                                                purchasedTabCollection.PaidPosts.PaidPostMedia.Add(medium);
                                            }
                                        }
                                        else if (!has && medium.CanView && medium.Files != null &&
                                                 medium.Files.Drm != null)
                                        {
                                            if (!purchasedTabCollection.PaidPosts.PaidPosts.ContainsKey(medium.Id))
                                            {
                                                await dbService.AddMedia(path, medium.Id, purchase.Id,
                                                    medium.Files.Drm.Manifest.Dash, null, null, null, "Posts",
                                                    medium.Type == "photo" ? "Images" :
                                                    medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                                    medium.Type == "audio" ? "Audios" : null,
                                                    previewids.Contains(medium.Id), false, null);
                                                purchasedTabCollection.PaidPosts.PaidPosts.Add(medium.Id,
                                                    $"{medium.Files.Drm.Manifest.Dash},{medium.Files.Drm.Signature.Dash.CloudFrontPolicy},{medium.Files.Drm.Signature.Dash.CloudFrontSignature},{medium.Files.Drm.Signature.Dash.CloudFrontKeyPairId},{medium.Id},{purchase.Id}");
                                                purchasedTabCollection.PaidPosts.PaidPostMedia.Add(medium);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (medium.CanView && medium.Files != null && medium.Files.Full != null &&
                                            !string.IsNullOrEmpty(medium.Files.Full.Url))
                                        {
                                            if (!purchasedTabCollection.PaidPosts.PaidPosts.ContainsKey(medium.Id))
                                            {
                                                await dbService.AddMedia(path, medium.Id, purchase.Id,
                                                    medium.Files.Full.Url, null, null, null, "Posts",
                                                    medium.Type == "photo" ? "Images" :
                                                    medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                                    medium.Type == "audio" ? "Audios" : null,
                                                    previewids.Contains(medium.Id), false, null);
                                                purchasedTabCollection.PaidPosts.PaidPosts.Add(medium.Id,
                                                    medium.Files.Full.Url);
                                                purchasedTabCollection.PaidPosts.PaidPostMedia.Add(medium);
                                            }
                                        }
                                        else if (medium.CanView && medium.Files != null && medium.Files.Drm != null)
                                        {
                                            if (!purchasedTabCollection.PaidPosts.PaidPosts.ContainsKey(medium.Id))
                                            {
                                                await dbService.AddMedia(path, medium.Id, purchase.Id,
                                                    medium.Files.Drm.Manifest.Dash, null, null, null, "Posts",
                                                    medium.Type == "photo" ? "Images" :
                                                    medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                                    medium.Type == "audio" ? "Audios" : null,
                                                    previewids.Contains(medium.Id), false, null);
                                                purchasedTabCollection.PaidPosts.PaidPosts.Add(medium.Id,
                                                    $"{medium.Files.Drm.Manifest.Dash},{medium.Files.Drm.Signature.Dash.CloudFrontPolicy},{medium.Files.Drm.Signature.Dash.CloudFrontSignature},{medium.Files.Drm.Signature.Dash.CloudFrontKeyPairId},{medium.Id},{purchase.Id}");
                                                purchasedTabCollection.PaidPosts.PaidPostMedia.Add(medium);
                                            }
                                        }
                                    }
                                }

                                break;
                            case "message":
                                if (purchase.PostedAt != null)
                                {
                                    await dbService.AddMessage(path, purchase.Id,
                                        purchase.Text != null ? purchase.Text : string.Empty,
                                        purchase.Price != null ? purchase.Price : "0", true, false,
                                        purchase.PostedAt.Value, purchase.FromUser.Id);
                                }
                                else
                                {
                                    await dbService.AddMessage(path, purchase.Id,
                                        purchase.Text != null ? purchase.Text : string.Empty,
                                        purchase.Price != null ? purchase.Price : "0", true, false,
                                        purchase.CreatedAt.Value, purchase.FromUser.Id);
                                }

                                purchasedTabCollection.PaidMessages.PaidMessageObjects.Add(purchase);
                                if (purchase.Media != null && purchase.Media.Count > 0)
                                {
                                    List<long> paidMessagePreviewids = new();
                                    if (purchase.Previews != null)
                                    {
                                        for (int i = 0; i < purchase.Previews.Count; i++)
                                        {
                                            if (purchase.Previews[i] is long previewId)
                                            {
                                                if (!paidMessagePreviewids.Contains(previewId))
                                                {
                                                    paidMessagePreviewids.Add(previewId);
                                                }
                                            }
                                        }
                                    }
                                    else if (purchase.Preview != null)
                                    {
                                        for (int i = 0; i < purchase.Preview.Count; i++)
                                        {
                                            if (purchase.Preview[i] is long previewId)
                                            {
                                                if (!paidMessagePreviewids.Contains(previewId))
                                                {
                                                    paidMessagePreviewids.Add(previewId);
                                                }
                                            }
                                        }
                                    }

                                    foreach (MessageEntities.Medium medium in purchase.Media)
                                    {
                                        if (paidMessagePreviewids.Count > 0)
                                        {
                                            bool has = paidMessagePreviewids.Any(cus => cus.Equals(medium.Id));
                                            if (!has && medium.CanView && medium.Files != null &&
                                                medium.Files.Full != null &&
                                                !string.IsNullOrEmpty(medium.Files.Full.Url))
                                            {
                                                if (medium.Type == "photo" &&
                                                    !configService.CurrentConfig.DownloadImages)
                                                {
                                                    continue;
                                                }

                                                if (medium.Type == "video" &&
                                                    !configService.CurrentConfig.DownloadVideos)
                                                {
                                                    continue;
                                                }

                                                if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                                                {
                                                    continue;
                                                }

                                                if (medium.Type == "audio" &&
                                                    !configService.CurrentConfig.DownloadAudios)
                                                {
                                                    continue;
                                                }

                                                if (!purchasedTabCollection.PaidMessages.PaidMessages.ContainsKey(
                                                        medium.Id))
                                                {
                                                    await dbService.AddMedia(path, medium.Id, purchase.Id,
                                                        medium.Files.Full.Url, null, null, null, "Messages",
                                                        medium.Type == "photo" ? "Images" :
                                                        medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                                        medium.Type == "audio" ? "Audios" : null,
                                                        paidMessagePreviewids.Contains(medium.Id), false,
                                                        null);
                                                    purchasedTabCollection.PaidMessages.PaidMessages.Add(medium.Id,
                                                        medium.Files.Full.Url);
                                                    purchasedTabCollection.PaidMessages.PaidMessageMedia.Add(
                                                        medium);
                                                }
                                            }
                                            else if (!has && medium.CanView && medium.Files != null &&
                                                     medium.Files.Drm != null)
                                            {
                                                if (medium.Type == "photo" &&
                                                    !configService.CurrentConfig.DownloadImages)
                                                {
                                                    continue;
                                                }

                                                if (medium.Type == "video" &&
                                                    !configService.CurrentConfig.DownloadVideos)
                                                {
                                                    continue;
                                                }

                                                if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                                                {
                                                    continue;
                                                }

                                                if (medium.Type == "audio" &&
                                                    !configService.CurrentConfig.DownloadAudios)
                                                {
                                                    continue;
                                                }

                                                if (!purchasedTabCollection.PaidMessages.PaidMessages.ContainsKey(
                                                        medium.Id))
                                                {
                                                    await dbService.AddMedia(path, medium.Id, purchase.Id,
                                                        medium.Files.Drm.Manifest.Dash, null, null, null, "Messages",
                                                        medium.Type == "photo" ? "Images" :
                                                        medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                                        medium.Type == "audio" ? "Audios" : null,
                                                        paidMessagePreviewids.Contains(medium.Id), false,
                                                        null);
                                                    purchasedTabCollection.PaidMessages.PaidMessages.Add(medium.Id,
                                                        $"{medium.Files.Drm.Manifest.Dash},{medium.Files.Drm.Signature.Dash.CloudFrontPolicy},{medium.Files.Drm.Signature.Dash.CloudFrontSignature},{medium.Files.Drm.Signature.Dash.CloudFrontKeyPairId},{medium.Id},{purchase.Id}");
                                                    purchasedTabCollection.PaidMessages.PaidMessageMedia.Add(
                                                        medium);
                                                }
                                            }
                                        }
                                        else
                                        {
                                            if (medium.CanView && medium.Files != null && medium.Files.Full != null &&
                                                !string.IsNullOrEmpty(medium.Files.Full.Url))
                                            {
                                                if (medium.Type == "photo" &&
                                                    !configService.CurrentConfig.DownloadImages)
                                                {
                                                    continue;
                                                }

                                                if (medium.Type == "video" &&
                                                    !configService.CurrentConfig.DownloadVideos)
                                                {
                                                    continue;
                                                }

                                                if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                                                {
                                                    continue;
                                                }

                                                if (medium.Type == "audio" &&
                                                    !configService.CurrentConfig.DownloadAudios)
                                                {
                                                    continue;
                                                }

                                                if (!purchasedTabCollection.PaidMessages.PaidMessages.ContainsKey(
                                                        medium.Id))
                                                {
                                                    await dbService.AddMedia(path, medium.Id, purchase.Id,
                                                        medium.Files.Full.Url, null, null, null, "Messages",
                                                        medium.Type == "photo" ? "Images" :
                                                        medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                                        medium.Type == "audio" ? "Audios" : null,
                                                        paidMessagePreviewids.Contains(medium.Id), false,
                                                        null);
                                                    purchasedTabCollection.PaidMessages.PaidMessages.Add(medium.Id,
                                                        medium.Files.Full.Url);
                                                    purchasedTabCollection.PaidMessages.PaidMessageMedia.Add(
                                                        medium);
                                                }
                                            }
                                            else if (medium.CanView && medium.Files != null && medium.Files.Drm != null)
                                            {
                                                if (medium.Type == "photo" &&
                                                    !configService.CurrentConfig.DownloadImages)
                                                {
                                                    continue;
                                                }

                                                if (medium.Type == "video" &&
                                                    !configService.CurrentConfig.DownloadVideos)
                                                {
                                                    continue;
                                                }

                                                if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos)
                                                {
                                                    continue;
                                                }

                                                if (medium.Type == "audio" &&
                                                    !configService.CurrentConfig.DownloadAudios)
                                                {
                                                    continue;
                                                }

                                                if (!purchasedTabCollection.PaidMessages.PaidMessages.ContainsKey(
                                                        medium.Id))
                                                {
                                                    await dbService.AddMedia(path, medium.Id, purchase.Id,
                                                        medium.Files.Drm.Manifest.Dash, null, null, null, "Messages",
                                                        medium.Type == "photo" ? "Images" :
                                                        medium.Type == "video" || medium.Type == "gif" ? "Videos" :
                                                        medium.Type == "audio" ? "Audios" : null,
                                                        paidMessagePreviewids.Contains(medium.Id), false,
                                                        null);
                                                    purchasedTabCollection.PaidMessages.PaidMessages.Add(medium.Id,
                                                        $"{medium.Files.Drm.Manifest.Dash},{medium.Files.Drm.Signature.Dash.CloudFrontPolicy},{medium.Files.Drm.Signature.Dash.CloudFrontSignature},{medium.Files.Drm.Signature.Dash.CloudFrontKeyPairId},{medium.Id},{purchase.Id}");
                                                    purchasedTabCollection.PaidMessages.PaidMessageMedia.Add(
                                                        medium);
                                                }
                                            }
                                        }
                                    }
                                }

                                break;
                        }
                    }

                    purchasedTabCollections.Add(purchasedTabCollection);
                }
            }

            return purchasedTabCollections;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }


    public async Task<string> GetDRMMPDPSSH(string mpdUrl, string policy, string signature, string kvp)
    {
        try
        {
            string pssh = null;

            HttpClient client = new();
            HttpRequestMessage request = new(HttpMethod.Get, mpdUrl);
            request.Headers.Add("user-agent", authService.CurrentAuth.USER_AGENT);
            request.Headers.Add("Accept", "*/*");
            request.Headers.Add("Cookie",
                $"CloudFront-Policy={policy}; CloudFront-Signature={signature}; CloudFront-Key-Pair-Id={kvp}; {authService.CurrentAuth.COOKIE};");
            using (HttpResponseMessage response = await client.SendAsync(request))
            {
                response.EnsureSuccessStatusCode();
                string body = await response.Content.ReadAsStringAsync();
                XNamespace ns = "urn:mpeg:dash:schema:mpd:2011";
                XNamespace cenc = "urn:mpeg:cenc:2013";
                XDocument xmlDoc = XDocument.Parse(body);
                IEnumerable<XElement> psshElements = xmlDoc.Descendants(cenc + "pssh");
                pssh = psshElements.ElementAt(1).Value;
            }

            return pssh;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }


    public async Task<DateTime> GetDRMMPDLastModified(string mpdUrl, string policy, string signature, string kvp)
    {
        Log.Debug("Calling GetDRMMPDLastModified");
        Log.Debug($"mpdUrl: {mpdUrl}");
        Log.Debug($"policy: {policy}");
        Log.Debug($"signature: {signature}");
        Log.Debug($"kvp: {kvp}");

        try
        {
            DateTime lastmodified;

            HttpClient client = new();
            HttpRequestMessage request = new(HttpMethod.Get, mpdUrl);
            request.Headers.Add("user-agent", authService.CurrentAuth.USER_AGENT);
            request.Headers.Add("Accept", "*/*");
            request.Headers.Add("Cookie",
                $"CloudFront-Policy={policy}; CloudFront-Signature={signature}; CloudFront-Key-Pair-Id={kvp}; {authService.CurrentAuth.COOKIE};");
            using (HttpResponseMessage response =
                   await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead))
            {
                response.EnsureSuccessStatusCode();
                lastmodified = response.Content.Headers.LastModified?.LocalDateTime ?? DateTime.Now;

                Log.Debug($"Last modified: {lastmodified}");
            }

            return lastmodified;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return DateTime.Now;
    }

    public async Task<string> GetDecryptionKeyCDRMProject(Dictionary<string, string> drmHeaders, string licenceURL,
        string pssh)
    {
        Log.Debug("Calling GetDecryptionKey");

        int attempt = 0;

        try
        {
            string dcValue = string.Empty;
            HttpClient client = new();

            CDRMProjectRequest cdrmProjectRequest = new()
            {
                PSSH = pssh,
                LicenseURL = licenceURL,
                Headers = JsonConvert.SerializeObject(drmHeaders),
                Cookies = "",
                Data = ""
            };

            string json = JsonConvert.SerializeObject(cdrmProjectRequest);

            Log.Debug($"Posting to CDRM Project: {json}");

            while (attempt < MaxAttempts)
            {
                attempt++;

                HttpRequestMessage request = new(HttpMethod.Post, "https://cdrm-project.com/api/decrypt")
                {
                    Content = new StringContent(json, Encoding.UTF8, "application/json")
                };

                using HttpResponseMessage response = await client.SendAsync(request);

                Log.Debug($"CDRM Project Response (Attempt {attempt}): {response.Content.ReadAsStringAsync().Result}");

                response.EnsureSuccessStatusCode();
                string body = await response.Content.ReadAsStringAsync();
                JsonDocument doc = JsonDocument.Parse(body);

                if (doc.RootElement.TryGetProperty("status", out JsonElement status))
                {
                    if (status.ToString().Trim().Equals("success", StringComparison.OrdinalIgnoreCase))
                    {
                        dcValue = doc.RootElement.GetProperty("message").GetString().Trim();
                        return dcValue;
                    }

                    Log.Debug($"CDRM response status not successful. Retrying... Attempt {attempt} of {MaxAttempts}");
                    if (attempt < MaxAttempts)
                    {
                        await Task.Delay(DelayBetweenAttempts);
                    }
                }
                else
                {
                    Log.Debug($"Status not in CDRM response. Retrying... Attempt {attempt} of {MaxAttempts}");
                    if (attempt < MaxAttempts)
                    {
                        await Task.Delay(DelayBetweenAttempts);
                    }
                }
            }

            throw new Exception("Maximum retry attempts reached. Unable to get a valid decryption key.");
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }

    public async Task<string> GetDecryptionKeyOFDL(Dictionary<string, string> drmHeaders, string licenceURL,
        string pssh)
    {
        Log.Debug("Calling GetDecryptionOFDL");

        try
        {
            HttpClient client = new();
            int attempt = 0;

            OFDLRequest ofdlRequest = new()
            {
                PSSH = pssh, LicenseURL = licenceURL, Headers = JsonConvert.SerializeObject(drmHeaders)
            };

            string json = JsonConvert.SerializeObject(ofdlRequest);

            Log.Debug($"Posting to ofdl.tools: {json}");

            while (attempt < MaxAttempts)
            {
                attempt++;

                HttpRequestMessage request = new(HttpMethod.Post, "https://ofdl.tools/WV")
                {
                    Content = new StringContent(json, Encoding.UTF8, "application/json")
                };

                using HttpResponseMessage response = await client.SendAsync(request);

                if (!response.IsSuccessStatusCode)
                {
                    continue;
                }

                string body = await response.Content.ReadAsStringAsync();

                if (!body.TrimStart().StartsWith('{'))
                {
                    return body;
                }

                Log.Debug($"Received JSON object instead of string. Retrying... Attempt {attempt} of {MaxAttempts}");
                await Task.Delay(DelayBetweenAttempts);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }

    public async Task<string> GetDecryptionKeyCDM(Dictionary<string, string> drmHeaders, string licenceURL, string pssh)
    {
        Log.Debug("Calling GetDecryptionKeyCDM");

        try
        {
            byte[] resp1 = await PostData(licenceURL, drmHeaders, new byte[] { 0x08, 0x04 });
            string certDataB64 = Convert.ToBase64String(resp1);
            CDMApi cdm = new();
            byte[] challenge = cdm.GetChallenge(pssh, certDataB64);
            byte[] resp2 = await PostData(licenceURL, drmHeaders, challenge);
            string licenseB64 = Convert.ToBase64String(resp2);
            Log.Debug($"resp1: {resp1}");
            Log.Debug($"certDataB64: {certDataB64}");
            Log.Debug($"challenge: {challenge}");
            Log.Debug($"resp2: {resp2}");
            Log.Debug($"licenseB64: {licenseB64}");
            cdm.ProvideLicense(licenseB64);
            List<ContentKey> keys = cdm.GetKeys();
            if (keys.Count > 0)
            {
                Log.Debug($"GetDecryptionKeyCDM Key: {keys[0]}");
                return keys[0].ToString();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }


    private async Task<string?> BuildHeaderAndExecuteRequests(Dictionary<string, string> getParams, string endpoint,
        HttpClient client)
    {
        Log.Debug("Calling BuildHeaderAndExecuteRequests");

        HttpRequestMessage request = await BuildHttpRequestMessage(getParams, endpoint);
        using HttpResponseMessage response = await client.SendAsync(request);
        response.EnsureSuccessStatusCode();
        string body = await response.Content.ReadAsStringAsync();

        Log.Debug(body);

        return body;
    }


    private async Task<HttpRequestMessage> BuildHttpRequestMessage(Dictionary<string, string> getParams,
        string endpoint)
    {
        Log.Debug("Calling BuildHttpRequestMessage");

        string queryParams = "?" + string.Join("&", getParams.Select(kvp => $"{kvp.Key}={kvp.Value}"));

        Dictionary<string, string> headers = GetDynamicHeaders($"/api2/v2{endpoint}", queryParams);

        HttpRequestMessage request = new(HttpMethod.Get, $"{Constants.API_URL}{endpoint}{queryParams}");

        Log.Debug($"Full request URL: {Constants.API_URL}{endpoint}{queryParams}");

        foreach (KeyValuePair<string, string> keyValuePair in headers)
        {
            request.Headers.Add(keyValuePair.Key, keyValuePair.Value);
        }

        return request;
    }

    private static double ConvertToUnixTimestampWithMicrosecondPrecision(DateTime date)
    {
        DateTime origin = new(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
        TimeSpan diff = date.ToUniversalTime() - origin;

        return
            diff.TotalSeconds; // This gives the number of seconds. If you need milliseconds, use diff.TotalMilliseconds
    }

    public static bool IsStringOnlyDigits(string input) => input.All(char.IsDigit);


    private HttpClient GetHttpClient()
    {
        HttpClient client = new();
        if (configService.CurrentConfig?.Timeout != null && configService.CurrentConfig.Timeout > 0)
        {
            client.Timeout = TimeSpan.FromSeconds(configService.CurrentConfig.Timeout.Value);
        }

        return client;
    }


    /// <summary>
    ///     this one is used during initialization only
    ///     if the config option is not available then no modificatiotns will be done on the getParams
    /// </summary>
    /// <param name="config"></param>
    /// <param name="getParams"></param>
    /// <param name="dt"></param>
    private static void UpdateGetParamsForDateSelection(DownloadDateSelection downloadDateSelection,
        ref Dictionary<string, string> getParams, DateTime? dt)
    {
        //if (config.DownloadOnlySpecificDates && dt.HasValue)
        //{
        if (dt.HasValue)
        {
            UpdateGetParamsForDateSelection(
                downloadDateSelection,
                ref getParams,
                ConvertToUnixTimestampWithMicrosecondPrecision(dt.Value)
                    .ToString("0.000000", CultureInfo.InvariantCulture)
            );
        }
        //}
    }

    private static void UpdateGetParamsForDateSelection(DownloadDateSelection downloadDateSelection,
        ref Dictionary<string, string> getParams, string unixTimeStampInMicrosec)
    {
        switch (downloadDateSelection)
        {
            case DownloadDateSelection.before:
                getParams["beforePublishTime"] = unixTimeStampInMicrosec;
                break;
            case DownloadDateSelection.after:
                getParams["order"] = "publish_date_asc";
                getParams["afterPublishTime"] = unixTimeStampInMicrosec;
                break;
        }
    }


    public async Task<Dictionary<string, long>?> GetAllSubscriptions(Dictionary<string, string> getParams,
        string endpoint, bool includeRestricted)
    {
        try
        {
            Dictionary<string, long> users = new();
            Subscriptions subscriptions = new();

            Log.Debug("Calling GetAllSubscrptions");

            string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, new HttpClient());

            subscriptions = JsonConvert.DeserializeObject<Subscriptions>(body);
            if (subscriptions != null && subscriptions.hasMore)
            {
                getParams["offset"] = subscriptions.list.Count.ToString();

                while (true)
                {
                    Subscriptions newSubscriptions = new();
                    string? loopbody = await BuildHeaderAndExecuteRequests(getParams, endpoint, new HttpClient());

                    if (!string.IsNullOrEmpty(loopbody) && (!loopbody.Contains("[]") || loopbody.Trim() != "[]"))
                    {
                        newSubscriptions =
                            JsonConvert.DeserializeObject<Subscriptions>(loopbody, m_JsonSerializerSettings);
                    }
                    else
                    {
                        break;
                    }

                    subscriptions.list.AddRange(newSubscriptions.list);
                    if (!newSubscriptions.hasMore)
                    {
                        break;
                    }

                    getParams["offset"] = subscriptions.list.Count.ToString();
                }
            }

            foreach (Subscriptions.List subscription in subscriptions.list)
            {
                if ((!(subscription.isRestricted ?? false) ||
                     ((subscription.isRestricted ?? false) && includeRestricted))
                    && !users.ContainsKey(subscription.username))
                {
                    users.Add(subscription.username, subscription.id);
                }
            }

            return users;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }

    public static string? GetDynamicRules()
    {
        Log.Debug("Calling GetDynamicRules");
        try
        {
            HttpClient client = new();
            HttpRequestMessage request = new(HttpMethod.Get,
                "https://git.ofdl.tools/sim0n00ps/dynamic-rules/raw/branch/main/rules.json");
            using HttpResponseMessage response = client.Send(request);

            if (!response.IsSuccessStatusCode)
            {
                Log.Debug("GetDynamicRules did not return a Success Status Code");
                return null;
            }

            string body = response.Content.ReadAsStringAsync().Result;

            Log.Debug("GetDynamicRules Response: ");
            Log.Debug(body);

            return body;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
            {
                Console.WriteLine("\nInner Exception:");
                Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
                Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
                    ex.InnerException.StackTrace);
            }
        }

        return null;
    }
}
