using Photino.NET;
using BrikPackager;
using System.Text.Json;
using BrikInstaller.Utils;
using BrikInstaller.Schemas;
using BrikInstaller.Services;
using System.Net.Http.Json;
using System.Reflection;

namespace BrikInstaller;

class Program{
    [STAThread]
    static void Main(string[] args) {
        CreateMainWindow().WaitForClose();
    }

    public static PhotinoWindow CreateMainWindow() { 
        string brikPackagePath;

        if (Constants.Env == "PROD") {
            brikPackagePath = Path.Combine(Path.GetTempPath(), "brik_root.dat");

            var assembly = Assembly.GetExecutingAssembly();
            using (Stream? stream = assembly.GetManifestResourceStream("root.dat")) {
                if (stream == null) throw new Exception("Ressource 'root.dat' introuvable.");
                
                using (FileStream fs = new FileStream(brikPackagePath, FileMode.Create, FileAccess.Write)) {
                    stream.CopyTo(fs);
                }
            }
        } else {
            brikPackagePath = Path.Combine(Constants.BaseDirectory, "root.dat");
        }

        BrikPackage bpkg = new BrikPackage(brikPackagePath, 66);
        List<string> entries = bpkg.ListEntries();
        foreach (string entry in entries) {
            Console.WriteLine($"[File] > {entry}");
        }
        var window = new PhotinoWindow()
            .SetTitle("Brik Installer")
            .SetUseOsDefaultLocation(false)
            .SetUseOsDefaultSize(false)
            .SetChromeless(true)
            .SetDevToolsEnabled(false)
            .SetContextMenuEnabled(false)
            .RegisterCustomSchemeHandler("http", (object sender, string scheme, string url, out string contentType) =>{
                Uri uri = new Uri(url);
                switch (uri.Host){
                    case "internal":
                        string internalPath = uri.AbsolutePath;
                        contentType = MimeHelper.GetMimeType(uri.AbsolutePath);
                        byte[] fileData = bpkg.GetFileBytes(internalPath.TrimStart('/'));
                        return fileData != null ? new MemoryStream(fileData) : null;
                    default:        
                        contentType = null!;
                        return null;
                }
            });

        window.WindowCreated += (sender, e) => {
            string iconPath = Path.Combine(Constants.BaseDirectory, "src", "resources", "build", "icon.ico");
            SetupBridge(window);
            WindowHelper.setSize(window, 465, 290);
            WindowHelper.RemoveTitleBar(window);
            WindowHelper.ForceTaskbarIcon(window, iconPath);
            window.Center();
        };
        
        LoadPage(window, "index.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");

                object? responsePayload = null;
                var pw = (PhotinoWindow)sender!;

                switch (method) {
                    case "network::fetch":
                        responsePayload = await NetworkService.HandleFetchRequest(jsonPayload);
                        break;
                    case "check::requirements":
                        if (jsonPayload.TryGetProperty("soft", out var soft)) {
                            RequirementCheckingResponse requirementChecking = await RequirementsService.CheckRequirementsAsync(pw, soft.GetString()!);
                            if (!requirementChecking.Success) {
                                IPC.sendToRenderer(window, "requirement::failed", null!);
                            } else {
                                IPC.sendToRenderer(window, "requirement::success", null!);
                            }
                        } else {
                            responsePayload = new { success = false, error = "Soft name missing, wtf?" };
                        }
                        break;
                    case "browser::open":
                        if (jsonPayload.TryGetProperty("url", out var url)) {
                            BashUtils.OpenUrl(url.ToString());
                        } else {
                            responsePayload = new { success = false, error = "Soft name missing, wtf?" };
                        }
                        break;
                    case "installer::elevate":
                        InstallationService.ElevateAndRestart();
                        responsePayload = new { success = true };
                        break;
                    case "installer::testInstallationPath":
                        if (jsonPayload.TryGetProperty("path", out var installPath)) {
                            bool canWrite = InstallationService.CanCreateInPath(installPath.ToString());
                            if (!canWrite) {
                                InstallationService.ElevateAndRestart();
                                responsePayload = new { success = canWrite };
                            } else {
                                responsePayload = new { success = canWrite };
                            }
                        } else {
                            responsePayload = new { success = false };
                        }
                        break;
                    case "dialog::selectDirectory":
                        string[] selectedFolders = window.ShowOpenFolder();
                        
                        if (selectedFolders != null && selectedFolders.Length > 0) {
                            responsePayload = new { success = true, path = selectedFolders[0] };
                        } else {
                            responsePayload = new { success = false };
                        }
                        break;
                    case "installer::defaultInstallPath":
                        if (jsonPayload.TryGetProperty("soft", out var productName)) {
                            string defaultInstallPath = InstallationService.GetDefaultInstallationPath(productName.ToString());
                            responsePayload = new { success = true, path = defaultInstallPath };
                        } else {
                            responsePayload = new { success = false };
                        }
                        break;
                    case "installer::install":
                        if (jsonPayload.TryGetProperty("soft", out var softName) && jsonPayload.TryGetProperty("path", out var softInstallPath)) {
                            try {
                                Constants.ProductMetadata.Name = softName.ToString();
                                Constants.ProductMetadata.InstallPath = softInstallPath.ToString();
                                await GenericFilesService.SyncFilesAsync(Constants.URLs.Endpoints.Download(softName.ToString(), Constants.Os, Constants.Arch), Constants.URLs.Endpoints.Manifest(softName.ToString(), Constants.Os, Constants.Arch), softInstallPath.ToString());
                                responsePayload = new { success = true };
                            } catch (Exception) {
                                responsePayload = new { success = false };
                            }
                        }
                        break;
                    case "installer::finalize":
                        if (jsonPayload.TryGetProperty("createShortcut", out var createShortcut) && jsonPayload.TryGetProperty("launchAfterExit", out var launchAfterExit)) {
                            try {
                                HttpMethod httpMethod = HttpMethod.Get;
                                var response = await HttpHelper.FetchAsync(Constants.URLs.Endpoints.Configuration(Constants.ProductMetadata.Name, Constants.Os, Constants.Arch), httpMethod);
                                var productConfig = await response.Content.ReadFromJsonAsync<ProductConfig>(Constants._jsonOptions);
                                InstallationFinish installationFinish = jsonPayload.Deserialize<InstallationFinish>(Constants._jsonOptions)!;
                                if (installationFinish.CreateShortcut) {
                                    ShortcutService.CreateShortcut(Constants.ProductMetadata.Name, Path.Combine(Constants.ProductMetadata.InstallPath, productConfig!.Executable!));
                                }
                                if (installationFinish.LaunchAfterExit) {
                                    InstallationService.LaunchApplication(Path.Combine(Constants.ProductMetadata.InstallPath, productConfig!.Executable!));
                                }
                                responsePayload = new { success = true };
                            } catch (Exception) {
                                responsePayload = new { success = false };
                            }
                        }
                        break;
                    case "installer::kill":
                        window.Close();
                        break;
                    case "brik::metadata":
                        try {
                            HttpMethod httpMethod = HttpMethod.Get;
                            var response = await HttpHelper.FetchAsync(Constants.URLs.Endpoints.Metadata(), httpMethod);
                            string content = await response.Content.ReadAsStringAsync();
                            responsePayload = new { success = true, content = content.ToString() };
                        } catch (Exception) {
                            responsePayload = new { success = false };
                        }
                        break;
                }

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

    public static void LoadPage(PhotinoWindow window, string url) {
        if (Constants.Env == "PROD") {
            window.Load($"http://internal/{url}");
        } else {
            window.Load($"src/resources/renderer/{url}");
        }
    }
}