using Noctalk.Shared.Chat.Configuration;
using Noctalk.UI.Components.Button;
using Noctalk.UI.Components.Input;
using Noctalk.UI.Components.Label;
using Noctalk.UI.Components.Shell;
using Noctalk.UI.Windowing;
using Noctalk_server.Server.Conversations;
using Noctalk_server.Server.Networking;
using Noctalk_server.Server.Storage;
using Noctalk_server.Server.Tor;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Noctalk_server.Server.Admin;

public sealed class Server_status_window : Window
{
    private static readonly Brush Window_background = UI__brush(5, 9, 18);
    private static readonly Brush Accent_blue = UI__brush(73, 217, 255);
    private static readonly Brush Surface_body = UI__brush(5, 9, 18);
    private static readonly Brush Surface_panel = UI__brush(6, 11, 20);
    private readonly string executableDirectory;
    private readonly Noctalk_textbox serverNameTextBox = null!;
    private readonly Noctalk_label serverLocalRelayPortValueLabel = null!;
    private readonly Noctalk_textbox detailsTextBox = null!;
    private readonly CancellationTokenSource startupCancellationTokenSource = new();
    private readonly Noctalk_window_icon_handler windowIconHandler;
    private readonly Conversation_store conversationStore;
    private Server_config serverConfig = new();
    private Tor_service? torService;
    private Local_relay_listener? localRelayListener;

    public Server_status_window()
    {
        executableDirectory = AppContext.BaseDirectory;
        conversationStore = new Conversation_store(executableDirectory);
        windowIconHandler = new Noctalk_window_icon_handler(this, Noctalk_window_icon_app.Server);

        Title = "Noctalk server - Initializing";
        windowIconHandler.Window_icon__set(Noctalk_window_icon_state.Initializing);
        Width = 940;
        Height = 640;
        WindowStartupLocation = WindowStartupLocation.CenterScreen;
        Background = Window_background;
        Foreground = Accent_blue;

        Grid rootGrid = new();
        rootGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(64) });
        rootGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
        rootGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(64) });

        Grid serverNamePanel = new()
        {
            Margin = new Thickness(0)
        };
        serverNamePanel.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(142) });
        serverNamePanel.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
        serverNamePanel.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(12) });
        serverNamePanel.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(130) });
        serverNamePanel.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(140) });
        serverNamePanel.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(8) });
        serverNamePanel.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(46) });
        serverNamePanel.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(10) });

        Noctalk_label serverNameLabel = new()
        {
            Text = "Name of server",
            Size = Noctalk_label_size.medium,
            Margin = new Thickness(10, 0, 0, 0)
        };

        Noctalk_label serverLocalRelayPortLabel = new()
        {
            Text = "Local relay port",
            Size = Noctalk_label_size.medium,
            Margin = new Thickness(0, 0, 0, 0)
        };

        serverLocalRelayPortValueLabel = new Noctalk_label
        {
            Text = string.Empty,
            Size = Noctalk_label_size.medium,
            Margin = new Thickness(0),
            FontFamily = new FontFamily("Consolas")
        };

        serverNameTextBox = new Noctalk_textbox
        {
            Height = 34,
            Margin = new Thickness(8, 0, 0, 0),
            VerticalAlignment = VerticalAlignment.Center,
            VerticalContentAlignment = VerticalAlignment.Center
        };

        Noctalk_button saveServerNameButton = new()
        {
            Width = 46,
            Height = 34,
            Icon = Noctalk_button_icon.Save,
            Margin = new Thickness(0),
            VerticalAlignment = VerticalAlignment.Center,
            Color = Noctalk_button_color.Blue_O__Blue_O
        };
        saveServerNameButton.Click += (_, _) => Server_status__save_server_name(saveServerNameButton);

        Grid.SetColumn(serverNameLabel, 0);
        Grid.SetColumn(serverNameTextBox, 1);
        Grid.SetColumn(serverLocalRelayPortLabel, 3);
        Grid.SetColumn(serverLocalRelayPortValueLabel, 4);
        Grid.SetColumn(saveServerNameButton, 6);
        serverNamePanel.Children.Add(serverNameLabel);
        serverNamePanel.Children.Add(serverNameTextBox);
        serverNamePanel.Children.Add(serverLocalRelayPortLabel);
        serverNamePanel.Children.Add(serverLocalRelayPortValueLabel);
        serverNamePanel.Children.Add(saveServerNameButton);

        Grid logPanel = new()
        {
            Background = Surface_body,
            Margin = new Thickness(10, 4, 10, 14)
        };
        logPanel.RowDefinitions.Add(new RowDefinition { Height = new GridLength(42) });
        logPanel.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });

        Grid logHeaderPanel = new()
        {
            Background = Surface_panel,
            Margin = new Thickness(0),
            Height = 42
        };
        logHeaderPanel.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
        logHeaderPanel.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(46) });
        logHeaderPanel.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(8) });
        logHeaderPanel.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(46) });

        Noctalk_label logLabel = new()
        {
            Text = "Log",
            Size = Noctalk_label_size.large,
            Margin = new Thickness(0, 0, 0, 0)
        };

        Noctalk_button copyLogButton = new()
        {
            Width = 46,
            Icon = Noctalk_button_icon.Copy,
            Margin = new Thickness(0, 4, 0, 4),
            Color = Noctalk_button_color.Blue_O__Blue_O
        };
        copyLogButton.Click += (_, _) => Server_status__copy_text_to_clipboard(detailsTextBox.Text, copyLogButton, "log");

        Noctalk_button clearButton = new()
        {
            Width = 46,
            Icon = Noctalk_button_icon.Clear,
            Margin = new Thickness(0, 4, 0, 4),
            Color = Noctalk_button_color.Blue_O__Blue_O
        };
        clearButton.Click += (_, _) => detailsTextBox.Clear();

        Grid.SetColumn(logLabel, 0);
        Grid.SetColumn(copyLogButton, 1);
        Grid.SetColumn(clearButton, 3);
        logHeaderPanel.Children.Add(logLabel);
        logHeaderPanel.Children.Add(copyLogButton);
        logHeaderPanel.Children.Add(clearButton);

        detailsTextBox = new Noctalk_textbox
        {
            IsReadOnly = true,
            AcceptsReturn = true,
            TextWrapping = TextWrapping.NoWrap,
            VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
            HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
            VerticalContentAlignment = VerticalAlignment.Top,
            Margin = new Thickness(0),
            FontFamily = new FontFamily("Consolas"),
            FontSize = 13
        };

        Grid.SetRow(logHeaderPanel, 0);
        Grid.SetRow(detailsTextBox, 1);
        logPanel.Children.Add(logHeaderPanel);
        logPanel.Children.Add(detailsTextBox);

        Grid bottomPanel = new()
        {
            Margin = new Thickness(0),
            Height = 64
        };
        bottomPanel.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });

        Noctalk_shell_header headerBorder = new()
        {
            Child = serverNamePanel
        };

        Noctalk_shell_footer footerBorder = new()
        {
            Child = bottomPanel
        };

        Grid.SetRow(headerBorder, 0);
        Grid.SetRow(logPanel, 1);
        Grid.SetRow(footerBorder, 2);
        rootGrid.Children.Add(headerBorder);
        rootGrid.Children.Add(logPanel);
        rootGrid.Children.Add(footerBorder);
        Content = rootGrid;

        Loaded += Server_status_window__load;
        Closing += Server_status_window__closing;
    }

    private async void Server_status_window__load(object sender, RoutedEventArgs e)
    {
        try
        {
            Server_status__set_window_state(Noctalk_window_icon_state.Initializing);

            Server_startup.Noctalk_server__startup__ensure_runtime_folders(executableDirectory);
            serverConfig = Server_startup.Noctalk_server__startup__ensure_main_config(executableDirectory);
            serverNameTextBox.Text = serverConfig.Server__name;
            serverLocalRelayPortValueLabel.Text = serverConfig.Server__local_relay_port.ToString();
            Server_startup.Noctalk_server__startup__ensure_metadata_files(executableDirectory, serverConfig);

            Server_status__append_line("Noctalk_server is running. Close this window to stop it.");
            Server_status__append_line(string.Empty);
            Server_status__append_line("Public onion port: 65191");
            Server_status__append_line($"Local relay port: {serverConfig.Server__local_relay_port}");
            Server_status__append_line(string.Empty);

            torService = new Tor_service(executableDirectory, Server_status__append_line);
            Tor_file_layout torFileLayout = torService.Tor__file_layout;

            Server_status__log_missing_tor_file(torFileLayout.Tor__exe_path, "darknet\\tor\\tor.exe");
            Server_status__log_missing_tor_file(torFileLayout.Tor__geoip_path, "darknet\\tor\\geoip");
            Server_status__log_missing_tor_file(torFileLayout.Tor__geoip6_path, "darknet\\tor\\geoip6");
            Server_status__log_missing_tor_file(torFileLayout.Tor__torrc_path, "darknet\\tor\\torrc");

            localRelayListener = new Local_relay_listener(serverConfig.Admin__guid, () => serverConfig.Server__name, conversationStore, serverConfig.Server__local_relay_port, Server_status__append_line);
            localRelayListener.Networking__local_relay__start();

            await Server_status__start_tor_and_update_address();
        }
        catch (Exception exception)
        {
            Server_status__append_line($"Server startup failed: {exception.Message}");
            Server_status__append_line($"If local relay port {serverConfig.Server__local_relay_port} is already in use, close the other server or choose another port and restart this server.");
            Server_status__set_window_state(Noctalk_window_icon_state.Offline);
        }
    }

    private async Task Server_status__start_tor_and_update_address()
    {
        if (torService is null)
        {
            Server_status__set_window_state(Noctalk_window_icon_state.Offline);
            return;
        }

        Tor_startup_result torStartupResult = await torService.Tor__startup__start_async(serverConfig.Server__local_relay_port, startupCancellationTokenSource.Token);
        Server_status__append_line(torStartupResult.Message_to_client);

        if (torStartupResult.Flag__success)
        {
            serverConfig.Onion_address = torStartupResult.Onion_address;
            Server_startup.Noctalk_server__startup__write_main_config(executableDirectory, serverConfig);
            Server_status__set_window_state(Noctalk_window_icon_state.Online);
        }
        else
        {
            Server_status__set_window_state(Noctalk_window_icon_state.Offline);
        }
    }

    private void Server_status__save_server_name(Noctalk_button The_button)
    {
        string serverName = serverNameTextBox.Text.Trim();
        if (string.IsNullOrWhiteSpace(serverName))
        {
            serverName = "My private chat server";
            serverNameTextBox.Text = serverName;
        }

        serverConfig.Server__name = serverName;
        Server_startup.Noctalk_server__startup__write_main_config(executableDirectory, serverConfig);
        The_button.Make_button_blink_for_1_second();
        Server_status__append_line($"Server__name saved: {serverConfig.Server__name}");
    }

    private void Server_status__copy_text_to_clipboard(string The_text, Noctalk_button The_button, string The_label)
    {
        if (string.IsNullOrWhiteSpace(The_text))
        {
            return;
        }

        try
        {
            Clipboard.SetText(The_text);
            The_button.Make_button_blink_for_1_second();
        }
        catch (Exception exception)
        {
            Server_status__append_line($"Copy {The_label} failed: {exception.Message}");
        }
    }

    private void Server_status__log_missing_tor_file(string The_path, string The_relative_path)
    {
        if (!File.Exists(The_path))
        {
            Server_status__append_line($"Tor file missing: {The_relative_path}");
        }
    }

    private void Server_status_window__closing(object? sender, System.ComponentModel.CancelEventArgs e)
    {
        startupCancellationTokenSource.Cancel();
        localRelayListener?.Dispose();
        torService?.Dispose();
        startupCancellationTokenSource.Dispose();
    }

    private void Server_status__set_window_state(Noctalk_window_icon_state The_state)
    {
        Dispatcher.BeginInvoke(() =>
        {
            Title = $"Noctalk server - {Server_status__state_title__get(The_state)}";
            windowIconHandler.Window_icon__set(The_state);
        });
    }

    private static string Server_status__state_title__get(Noctalk_window_icon_state The_state)
    {
        return The_state switch
        {
            Noctalk_window_icon_state.Online => "Online",
            Noctalk_window_icon_state.Offline => "Offline",
            _ => "Initializing"
        };
    }

    private void Server_status__append_line(string The_line)
    {
        Dispatcher.BeginInvoke(() =>
        {
            detailsTextBox.AppendText(Noctalk.Shared.Logging.Noctalk_log.Log(The_line) + Environment.NewLine);
            detailsTextBox.ScrollToEnd();
        });
    }

    private static SolidColorBrush UI__brush(byte Red, byte Green, byte Blue)
    {
        SolidColorBrush brush = new(System.Windows.Media.Color.FromRgb(Red, Green, Blue));
        brush.Freeze();
        return brush;
    }

}
