using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using System.Reflection;
using System.Security.Cryptography;
using Newtonsoft.Json;
using System.IO;
using System.Threading;

namespace JellyfinRPC
{
    class JellyfinAPI
    {
        public static async Task<string> Jellyfin()
        {
            
            while (true)
            {
                try
                {
                    var playingInfo = await GetCurrentlyPlaying();
                    if (playingInfo != null)
                    {
                        JToken nowPlaying = playingInfo.NowPlayingItem;

                        string largeImageKey = playingInfo.IsMusic
                            ? GetAlbumCover(nowPlaying)
                            : await GetJellyfinLogo();
                        string largeImageText = playingInfo.IsMusic
                            ? nowPlaying["Album"]?.ToString() ?? "Unknown Album"
                            : "Jellyfin";

                        string details = playingInfo.IsMusic
                            ? $"{playingInfo.Title}"
                            : nowPlaying["SeriesName"] != null
                                ? $"Watching {nowPlaying["SeriesName"]} - {playingInfo.Title}"
                                : $"Watching {playingInfo.Title}";

                        string state = playingInfo.IsMusic
                            ? $"{playingInfo.Artist}"
                            : nowPlaying["SeriesName"] != null
                                ? $"Season {playingInfo.Season}, Episode {playingInfo.Episode}"
                                : "";
                        DateTime duration = playingInfo.Duration;

                        DateTime progress = playingInfo.Progress;
    
                        return $"{largeImageKey}|{largeImageText}|{details}|{state}|{duration}|{progress}";
                    }
                }
                catch (Exception ex)
                {
                    ConsoleManager.WriteToConsole($"Error fetching Jellyfin data: {ex.Message}");
                }
            }
        }


        public static string AssemblyVersion
        {
            get
            {
                return Assembly.GetExecutingAssembly().GetName().Version.ToString();
            }
        }
        private static async Task<string> GetJellyfinLogo()
        {
            using var httpClient = new HttpClient();
            try
            {
                if (ConfigManager.GetEntry("JellyfinToken") == "")
                {


                    var response = await httpClient.GetAsync($"{ConfigManager.GetEntry("ServerURL")}/System/Info?api_key={ConfigManager.GetEntry("APIKey")}");
                    response.EnsureSuccessStatusCode();

                    var jsonResponse = await response.Content.ReadAsStringAsync();
                    var json = JArray.Parse(jsonResponse);

                    var logoUrl = json["LogoUrl"]?.ToString();

                    return logoUrl ?? "jellyfin_logo";
                }
                else
                {
                    return "jellyfin_logo";
                }
            }
            catch (Exception ex)
            {
                ConsoleManager.WriteToConsole($"Error fetching Jellyfin logo: {ex.Message}");
                return "jellyfin_logo";
            }

        }
        private static async Task<PlayingInfo?> GetCurrentlyPlaying()
        {
            using var httpClient = new HttpClient();
            try
            {
                if (ConfigManager.GetEntry("JellyfinToken") == "")
                {
                    var playingInfoResponse = await httpClient.GetAsync($"{ConfigManager.GetEntry("ServerURL")}/Sessions?api_key={ConfigManager.GetEntry("APIKey")}");
                    playingInfoResponse.EnsureSuccessStatusCode();

                    var jsonResponse = await playingInfoResponse.Content.ReadAsStringAsync();
                    var sessions = JArray.Parse(jsonResponse);

                    foreach (var session in sessions)
                    {
                        if (session["UserId"]?.ToString() == ConfigManager.GetEntry("UserID") && session["NowPlayingItem"] != null)
                        {
                            var nowPlaying = session["NowPlayingItem"];
                            var mediaType = nowPlaying["Type"]?.ToString();
                            bool isMusic = mediaType?.ToLower() == "audio";

                            string albumCover = "";
                            string artist = "Unknown Artist";

                            if (isMusic)
                            {
                                albumCover = GetAlbumCover(nowPlaying);
                                var artists = nowPlaying["Artists"]?.ToObject<JArray>();
                                if (artists != null && artists.Count > 0)
                                {
                                    artist = artists[0].ToString();
                                }
                                else
                                {
                                    artist = "Unknown Artist";
                                }

                            }

                            return new PlayingInfo
                            {
                                Title = nowPlaying["Name"]?.ToString(),
                                Artist = artist,
                                AlbumCover = albumCover,
                                Season = nowPlaying["ParentIndexNumber"]?.ToString() ?? "N/A",
                                Episode = nowPlaying["IndexNumber"]?.ToString() ?? "N/A",
                                Progress = new DateTime((long)session["PlayState"]["PositionTicks"]),
                                //Progress = TimeSpan.FromTicks((long)session["PlayState"]["PositionTicks"]),
                                Duration = new DateTime((long)nowPlaying["RunTimeTicks"]),
                                //Duration = TimeSpan.FromTicks((long)nowPlaying["RunTimeTicks"]),
                                IsMusic = isMusic,
                                NowPlayingItem = nowPlaying
                            };
                        }

                    }
                }
               
            }
            catch (Exception ex)
            {
                ConsoleManager.WriteToConsole($"Error fetching Jellyfin data: {ex.Message}");
            }
            return null;
        }

        private static string GetAlbumCover(JToken nowPlaying)
        {
            var mediaStreams = nowPlaying["MediaStreams"]?.ToObject<JArray>();
            if (mediaStreams != null)
            {
                foreach (var stream in mediaStreams)
                {
                    var imageTag = stream["ImageTag"]?.ToString();
                    if (!string.IsNullOrEmpty(imageTag))
                    {
                        return imageTag;
                    }
                }
            }
            return "album_cover";
        }
        public static string quickConnectToken;
        public static bool formClosed;
        public static bool authSuccess;
        
        public static async Task AuthWithQuickConnect()
        {
            using var httpClient = new HttpClient();
            httpClient.DefaultRequestHeaders.Add("Authorization", $"MediaBrowser Client=\"JellyfinRPC {AssemblyVersion}, \", Device=\"{System.Net.Dns.GetHostName()}\", DeviceId=\"{ConfigManager.GetEntry("DeviceID")}\", Version=\"running on {Environment.OSVersion}\"");
            if (ConfigManager.GetEntry("ServerURL") != "")
            {
                var quickConnectInitResponse = await httpClient.GetAsync($"{ConfigManager.GetEntry("ServerURL")}/QuickConnect/Initiate");
                authSuccess = false;
                if (quickConnectInitResponse.StatusCode is (System.Net.HttpStatusCode)401)
                {
                    System.Windows.Forms.MessageBox.Show("This server does not have Quick Connect enabled.", "Quick Connect Unavailable.", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error, System.Windows.Forms.MessageBoxDefaultButton.Button1);
                }
                else
                {
                    try
                    {
                        quickConnectInitResponse.EnsureSuccessStatusCode();
                        var jsonResponse = await quickConnectInitResponse.Content.ReadAsStringAsync();
                        var jsonObject = JObject.Parse(jsonResponse);
                        

                        if (!jsonObject.HasValues)
                        {
                        }
                        else
                        {
                            string quickConnectCode = jsonObject.Value<string>("Code");
                            if (jsonObject.Value<string>("Secret") != null && jsonObject.Value<string>("Secret") != "")
                            {
                                quickConnectToken = jsonObject.Value<string>("Secret");
                            }
                            else
                            {
                                quickConnectToken = null;
                            }
                            (System.Windows.Forms.Application.OpenForms["ConfigForm"] as ConfigForm).label9.Text = quickConnectCode;
                            while (authSuccess == false && formClosed == false)
                            {
                                await CheckQuickConnectStatus();
                            }

                        }
                    }
                    catch (Exception ex)
                    {
#if DEBUG
                        System.Windows.Forms.MessageBox.Show(ex.ToString());
#endif
                    }

                }
            }
            else
            {
                System.Windows.Forms.MessageBox.Show("No Server URL is set.", "Quick Connect Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Exclamation);
            }
        }

        public static string DeviceID()
        {
            using RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            byte[] randomNumber = new byte[4];
            rng.GetBytes(randomNumber);
            int value = BitConverter.ToInt32(randomNumber, 0);
            byte[] valueBytes = System.Text.Encoding.UTF8.GetBytes(value.ToString());
            return System.Convert.ToBase64String(valueBytes);

        }
        public static async Task GetTokenFromUsernameAndPassword(string Username, string Password)
        {
            using var httpClient = new HttpClient();
            httpClient.DefaultRequestHeaders.Add("Authorization", $"MediaBrowser Client=\"JellyfinRPC {AssemblyVersion}, \", Device=\"{System.Net.Dns.GetHostName()}\", DeviceId=\"{ConfigManager.GetEntry("DeviceID")}\", Version=\"running on {Environment.OSVersion}\"");
            if (ConfigManager.GetEntry("ServerURL") != "")
            {

                string username = Username;
                string password = Password;

                var loginRequest = new Login() { username = username, pw = password };
                var loginJson = JsonConvert.SerializeObject(loginRequest);

                HttpResponseMessage loginWithUsernameandPassResponse = await httpClient.PostAsync($"{ConfigManager.GetEntry("ServerURL")}/Users/AuthenticateByName", new StringContent(loginJson, Encoding.UTF8, "application/json"));
                if (loginWithUsernameandPassResponse.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                {
                    System.Windows.Forms.MessageBox.Show("The Username or Password is incorrect.", "Authentication Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error, System.Windows.Forms.MessageBoxDefaultButton.Button1);
                }
                else
                {
                    loginWithUsernameandPassResponse.EnsureSuccessStatusCode();

                    var jsonResponse = await loginWithUsernameandPassResponse.Content.ReadAsStringAsync();
                    
                    var jsonShit = JObject.Parse(jsonResponse);

                    string thatToken = jsonShit.Value<string>("AccessToken");
                    
                    if (thatToken != null && thatToken != " ")
                    {
                        ConfigManager.SetEntry("JellyfinToken", thatToken);
                    }

                }
            }
        }
        public static async Task CheckQuickConnectStatus()
        {
            using var httpClient = new HttpClient();
            if (quickConnectToken != null && quickConnectToken != "")
            {

                TaskEx.Delay(1000);
                string secret = quickConnectToken.Replace('"', ' ').Trim();
                HttpResponseMessage QuickConnectStatusResponse = await httpClient.GetAsync($"{ConfigManager.GetEntry("ServerURL")}/QuickConnect/Connect?Secret={quickConnectToken}");
                QuickConnectStatusResponse.EnsureSuccessStatusCode();
                var responseAsString = await QuickConnectStatusResponse.Content.ReadAsStringAsync();
                var responseJson = JObject.Parse(responseAsString);
                if (responseJson.Value<bool>("Authenticated") == true)
                {
                    var quickConnectLoginSecret = new JellyfinAuth() { AccessToken = quickConnectToken };
                    var quickConnectLoginJson = JsonConvert.SerializeObject(quickConnectLoginSecret);
                    HttpResponseMessage quickConnectLoginResponse = await httpClient.PostAsync($"{ConfigManager.GetEntry("ServerURL")}/Users/AuthenticateWithQuickConnect", new StringContent(quickConnectLoginJson, Encoding.UTF8, "application/json"));
                    quickConnectLoginResponse.EnsureSuccessStatusCode();
                    var quickConnectLoginResponseAsString = await quickConnectLoginResponse.Content.ReadAsStringAsync();
                    var quickConnectLoginResponseJson = JObject.Parse(quickConnectLoginResponseAsString);
                    if (quickConnectLoginResponseJson.HasValues)
                    {
                        if (quickConnectLoginResponseJson.Value<string>("AccessToken") != null && quickConnectLoginResponseJson.Value<string>("AccessToken") != "")
                        {
                            ConfigManager.SetEntry("JellyfinToken", quickConnectLoginResponseJson.Value<string>("AccessToken"));
                            
                            authSuccess = true;
                            (System.Windows.Forms.Application.OpenForms["ConfigForm"] as ConfigForm).label9.Text = "Authentication Successful.";
                        }
                        else
                        {
                            throw new Exception("The server did not return an access token, this shouldn't happen.");
                        }
                    }
                    else
                    {
                        System.Windows.Forms.MessageBox.Show("The server didn't respond for authentication.", "Authentication Error.", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Exclamation);
                    }
                    
                }
                else
                {
                    authSuccess = false;
                }
            }
        }
    }

    // don't touch!
    #region classes

    public class Login
    {
#pragma warning disable IDE1006 // Naming Styles
        public string username { get; set; }
        public string pw { get; set; }
#pragma warning restore IDE1006 // Naming Styles
    } 
    public class JellyfinAuth
    {
        public User User { get; }
        public string AccessToken { get; set; }
        public string ServerId { get; }
        
    }
    public class User
    {
        public string Name { get; }
        public string ServerId { get; }
        public string Id { get; }
        public string PrimaryImageTag { get; }
        public bool HasPassword { get; }
        public bool HasConfiguredPassword { get; }
        public bool HasConfiguredEasyPassword { get; }
        public bool EnableAutoLogin { get; }
        public string LastLoginDate { get; }
        public string LastActivityDate { get; }
        public Configuration Configuration { get; }
        public Policy Policy { get; }
        public int PrimaryImageAspectRatio { get; }
    }
    public class SessionInfo
    {
        public PlayState PlayState { get; set; }
        public JArray AdditionalUsers { get; }
        public Capabilities Capabilities { get; set; }
        public string RemoteEndPoint { get; }
        public JArray PlayableMediaTypes { get; }
        public string Id { get; }
        public string UserId { get; }
        public string UserName { get; }
        public string Client { get; }
        public string LastActivityDate { get; }
        public string LastPlaybackCheckIn { get; }
        public string LastPausedDate { get; }
        public string DeviceName { get; }
        public string DeviceType { get; }
        public string DeviceId { get; }
        public string ApplicationVersion { get; }
        public bool IsActive { get; }
        public bool SuportsMediaControl { get; }
        public bool SupportsRemoteControl { get; }
        public JArray NowPlayingQueue { get; }
        public JArray NowPlayingQueueFullItems { get; }
        public bool HasCustomDeviceName { get; }
        public string PlaylistItemId { get; }
        public string ServerId { get; }
        public string UserPrimaryImageTag { get; }
        public JArray SupportedCommands { get; }

    }
    public class Configuration
    {
        public string AudioLanguagePreference { get; }
        public bool PlayDefaultAudioTrack { get; }
        public string SubtitleLanguagePreference { get; }
        public bool DisplayMissingEpisodes { get; }
        public JArray GroupedFolders { get; }
        public string SubtitleMode { get; }
        public bool DisplayCollectionsView { get; }
        public bool EnableLocalPassword { get; }
        public JArray OrderedViews { get; }
        public JArray LatestItemsExcludes { get; }
        public JArray MyMediaExcludes { get; }
        public bool HidePlayedInLatest { get; }
        public bool RememberSubtitleSelections { get; }
        public bool EnableNextEpisodeAutoPlay { get; }
        public string CastRecieverId { get; }
    }
    public class Policy
    {
        // we don't actualy care about the policies, but i'm defining them in here just in case
        public bool IsAdministrator { get; }
        public bool IsHidden { get; }
        public bool EnableCollectionManagement { get; }
        public bool EnableSubtitleManagement { get; }
        public bool EnableLyricManagement { get; }
        public bool IsDisabled { get; }
        public int MaxParentalRating { get; }
        public JArray BlockedTags { get; }
        public JArray AllowedTags { get; }
        public bool EnableUserPreferenceAccess { get; }
        public JArray AccessSchedules { get; }
        public JArray BlockUnratedItems { get; }
        public bool EnableRemoteControlOfOtherUsers { get; }
        public bool EnableSharedDeviceControl { get; }
        public bool EnableRemoteAccess { get; }
        public bool EnableLiveTvManagement { get; }
        public bool EnableLiveTvAccess { get; }
        public bool EnableMediaPlayback { get; }
        public bool EnableAudioPlaybackTranscoding { get; }
        public bool EnableVideoPlaybackTranscoding { get; }
        public bool EnablePlaybackRemuxing { get; }
        public bool ForceRemoteSourceTranscoding { get; }
        public bool EnableConetentDeletion { get; }
        public JArray EnableContentDeletionFromFolders { get; }
        public bool EnableContentDownloading { get; }
        public bool EnableSyncTranscoding { get; }
        public bool EnableMediaConverison { get; }
        public JArray EnabledDevices { get; }
        public bool EnableAllDevices { get; }
        public JArray EnabledChannels { get; }
        public bool EnableAllChannels { get; }
        public JArray EnabledFolders { get; }
        public bool EnableAllFolders { get; }
        public int InvalidLoginAttemptCount { get; }
        public int LoginAttemptsBeforeLockout { get; }
        public int MaxActiveSessions { get; }
        public bool EnablePublicSharing { get; }
        public JArray BlockedMediaFolders { get; }
        public JArray BlockedChannels { get; }
        public int RemoteClientBitrateLimit { get; }
        public string AuthenticationProviderId { get; }
        public string PasswordResetProviderId { get; }
        public string SyncPlayAccess { get; }
    }
    public class PlayState
    {
        public int PositionTicks { get; set; }
        public bool CanSeek { get; }
        public bool IsPaused { get; }
        public bool IsMuted { get; set; }
        public int VolumeLevel { get; set; }
        public bool AudioStreamIndex { get; }
        public bool SubtitleStreamIndex { get; }
        public string MediaSourceId { get; }
        public string PlayMethod { get; }
        public string RepeatMode { get; }
        public string PlaybackOrder { get; }
        public string LiveStreamId { get; }

    }
    public class Capabilities
    {
        public JArray PlayableMediaTypes { get; set; }
        public JArray SupportedCommands { get; set; }
        public bool SupportsMediaControl { get; set; }
        public bool SupportsPersistentIdentifier { get; set; }
        public DeviceProfile DeviceProfile { get; set; }
        public string AppStoreUrl { get; set; }
        public string IconUrl { get; set; }
    }
    public class DeviceProfile
    {
        public string Name { get; set; }
        public string Id { get; set; }
        public int MaxStreamingBitrate { get; set; }
        public int MaxStaticBitrate { get; set; }
        public int MusicStreamingTranscodingBitrate { get; set; }
        public JArray DirectPlayProfiles { get; set; }
        public JArray TranscodingProfiles { get; set; }
        public JArray ContainerProfiles { get; set; }
        public JArray CodecProfiles { get; set; }
        public JArray SubtitleProfiles { get; set; }
    }
    #endregion
}
