using AzuresPackager;
using Lentia.Core.Auth.OAuth2;
using Lentia.Core.Constants;
using Lentia.Core.Game;
using Lentia.Utils;
using Photino.NET;
using System.Reflection;
using System.Text;
using System.Text.Json;
using LentRules = Lentia.Core.Auth.Yggdrasil;

namespace Lentia;

class Program {

    private static readonly string __dirname = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
    private static LentRules.AuthenticateResponse? _authenticatedPlayer;
    private static readonly string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
    private static readonly string gameRoot = Path.Combine(appData, ".lentia2");
    private static readonly LentRules.Authenticator _ygg = new();

    [STAThread]
    static void Main(string[] args) {
        SettingsManager.InitSettings(gameRoot);
        CreateLoginWindow().WaitForClose();
    }

    public static PhotinoWindow CreateLoginWindow() {
        AzuresPackage azp = new AzuresPackage(Path.Combine(__dirname, "root.dat"), 66);
        List<string> entries = azp.ListEntries();
        var window = new PhotinoWindow()
            .SetUseOsDefaultLocation(false)
            .SetUseOsDefaultSize(false)
            .SetContextMenuEnabled(false)
            .SetChromeless(true)
            .RegisterCustomSchemeHandler("http", (object sender, string scheme, string url, out string contentType) =>{
                Uri uri = new Uri(url);
                if (uri.Host == "internal") {
                    string internalPath = uri.AbsolutePath;
                    contentType = MimeHelper.GetMimeType(uri.AbsolutePath);
                    byte[] fileData = azp.GetFileBytes(internalPath.TrimStart('/'));
                    return fileData != null ? new MemoryStream(fileData) : null;
                }
                contentType = null!;
                return null;
            });

        window.WindowCreated += (sender, e) => {
            SetupBridge(window);
            WindowHelper.MakeLoginWindow(window);
        };

        
        window.Load("http://internal/login.html");

        return window;
    }

    public static void SetupBridge(PhotinoWindow window) {
        window.RegisterWebMessageReceivedHandler(async (object? sender, string message) => {
            try {
                var json = JsonDocument.Parse(message);
                string requestId = json.RootElement.GetProperty("requestId").GetString()!;
                string method = json.RootElement.GetProperty("functionName").GetString()!;
                var jsonPayload = json.RootElement.GetProperty("payload");
                var pw = (PhotinoWindow)sender!;

                object? payload = null;

                switch (method) {
                    case "launcher::version":
                        payload = "v1.0.0-beta";
                        break;

                    case "window::close":
                        pw.Close();
                        break;

                    case "lentia::dynmap":
                        payload = "http://azures.fr:8123/";
                        break;

                    case "hardware::ram":
                        payload = Hardware.GetRamUsage();
                        break;

                    case "auth::lentia":
                        string user = jsonPayload.GetProperty("username").GetString()!;
                        string pass = jsonPayload.GetProperty("password").GetString()!;
                        var lentiaAuthResult = await _ygg.Login(user, pass);
                        
                        if (lentiaAuthResult.Success) {
                            _authenticatedPlayer = lentiaAuthResult.Player;
                            WindowHelper.MakeStandardWindow(pw, 1356, 720, "http://internal/logged.html");
                            payload = new { success = true };
                        } else {
                            payload = new { success = false, error = lentiaAuthResult.Error };
                        }
                        break;

                    case "auth::logout":
                        _authenticatedPlayer = null;
                        pw.Load("http://internal/login.html");
                        WindowHelper.MakeLoginWindow(pw);
                        payload = true;
                        break;

                    case "dialog::error":
                        string title = jsonPayload.TryGetProperty("title", out var t) ? t.GetString()! : "Lentia";
                        string msg = jsonPayload.GetProperty("message").GetString()!;
                        pw.ShowMessage(title, msg, PhotinoDialogButtons.Ok, PhotinoDialogIcon.Error);
                        payload = new { success = true };
                        break;

                    case "settings::read":
                        payload = SettingsManager.ReadSettings();
                        break;

                    case "settings::set":
                        if (jsonPayload.TryGetProperty("key", out var keyElement) && jsonPayload.TryGetProperty("value", out var valueElement)) {
                            string key = keyElement.GetString()!;

                            object? valueToSet = valueElement.ValueKind switch {
                                JsonValueKind.String => valueElement.GetString(),
                                JsonValueKind.Number => valueElement.TryGetInt32(out int i) ? i : valueElement.GetDouble(),
                                JsonValueKind.True   => true,
                                JsonValueKind.False  => false,
                                JsonValueKind.Null   => null,
                                _                    => valueElement 
                            };

                            if (valueToSet != null) {
                                SettingsManager.Set(key, valueToSet);
                                payload = new { success = true };
                            } else {
                                payload = new { success = false, error = "Incorrect or null value" };
                            }
                        }
                        break;

                    case "dialog::selectSkin":
                        var imageFilter = new (string Name, string[] Extensions)[]  { 
                            ("Portable Network Graphic Image", new[] { "*.png" }) 
                        };

                        string[] files = window.ShowOpenFile("Choisis ton plus beau skin mon reuf", null, false, imageFilter);

                        payload = new { 
                            success = files != null && files.Length > 0, 
                            path = (files != null && files.Length > 0) ? files[0] : null 
                        };
                        break;

                    case "network::uploadSkin":
                        try {
                            string path = jsonPayload.GetProperty("path").GetString()!;
                            string variant = jsonPayload.GetProperty("variant").GetString()!;
                            string token = _authenticatedPlayer?.AccessToken!;

                            string skinResult = await MojangAPI.UploadSkinAsync(path, variant, token);
                            
                            payload = new { success = true };
                        } catch (Exception ex) {
                            payload = new { success = false, error = ex.Message };
                        }
                        break;

                    case "network::getPlayer":
                        try {
                            var profileData = await MojangAPI.GetPlayerProfileAsync(_authenticatedPlayer!.AccessToken);
                            payload = profileData; 
                        } catch (Exception ex) {
                            payload = new { success = false, error = ex.Message };
                        }
                        break;

                    case "network::hideCape":
                        try {
                            var hideCapeResult = await MojangAPI.HideCapeAsync(_authenticatedPlayer!.AccessToken);
                            payload = new { success = true, data = hideCapeResult };
                        } catch (Exception ex) {
                            payload = new { success = false, error = ex.Message };
                        }
                        break;

                    case "network::showCape":
                        try {
                            string token = _authenticatedPlayer?.AccessToken!;                            
                            string capeId = jsonPayload.GetProperty("capeId").GetString()!;

                            var showCapeResult = await MojangAPI.ShowCapeAsync(token, capeId);
                            payload = new { success = true, data = showCapeResult };
                        } catch (Exception ex) {
                            payload = new { success = false, error = ex.Message };
                        }
                        break;

                    case "network::changeUsername":
                        try {
                            string newName = jsonPayload.GetProperty("username").GetString()!;
                            string token = _authenticatedPlayer?.AccessToken!;

                            if (string.IsNullOrEmpty(token)) throw new Exception("Session expirée ou invalide.");

                            var updatedProfile = await MojangAPI.ChangeUsernameAsync(newName, token);
                            
                            if (_authenticatedPlayer != null) {
                                _authenticatedPlayer.User.Username = newName;
                            }

                            payload = new { success = true, profile = updatedProfile };
                        } catch (Exception ex) {
                            payload = new { success = false, error = ex.Message };
                        }
                        break;

                    case "oauth2::discord":
                        BashUtils.OpenUrl("https://yggdrasil.azures.fr/auth/provider/discord/login");
                        string? code = await Discord.ListenForCode();
                        var discordLoginResult = await Discord.LoginWithDiscordOAuth2(code!);

                        if (discordLoginResult.Success) {
                            _authenticatedPlayer = discordLoginResult.Player;
                            WindowHelper.MakeStandardWindow(pw, 1356, 720, "http://internal/logged.html");
                            payload = new { success = true };
                        } else {
                            Console.WriteLine(discordLoginResult);
                            payload = new { success = false, error = discordLoginResult.Error };
                        }
                        break;

                    case "launcher::game":
                        try {
                            await GenericFilesService.FetchAndSyncGenericFiles(gameRoot);
                            var options = new LaunchOptions {
                                Version = new LaunchOptions.VersionOptions {
                                    Number = "1.12.2",
                                    Type = "release"
                                },
                                Memory = new LaunchOptions.MemoryOptions {
                                    Max = $"{SettingsManager.ReadSettings().Ram.Max}M",
                                    Min = "512M"
                                },
                                ModLoader = LaunchOptions.ModLoaderType.Forge,
                                CustomArgs = new List<string> {
                                    $"-javaagent:{Path.Combine(gameRoot, LauncherConstants.AgentsPath.AuthlibInjector)}={LauncherConstants.Urls.YggdrasilServer}"
                                }
                            };
                            MinecraftVersion version = await GameHelper.PrepareGame(options, gameRoot);
                            GameHelper.Launch(version, _authenticatedPlayer!, gameRoot, options, (logLine) => {
                                var logMessage = new { 
                                    requestId = "game::log",
                                    payload = new { message = logLine.ToString() } 
                                };
                                window.SendWebMessage(JsonSerializer.Serialize(logMessage, LauncherConstants._jsonOptions));
                            });
                            payload = new { success = true };
                        } catch (Exception) {
                            payload = new { success = false };
                            throw;
                        }
                        break;
                }

                var response = new { requestId, payload };
                window.SendWebMessage(JsonSerializer.Serialize(response, LauncherConstants._jsonOptions));
            } catch (Exception ex) {
                Console.WriteLine($"Error stacktrace: {ex.StackTrace}");
                Console.WriteLine($"Bridge error: {ex.Message}");
            }
        });
    }
}