using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Godot;
using HttpClient = System.Net.Http.HttpClient;

namespace SessionZeroClient.API;

public class ApiHandler : IApiHandler
{
    private readonly HttpClient _httpClient;
    
    public ApiHandler()
    {
        var handler = new HttpClientHandler
        {
            UseDefaultCredentials = true,
        };

        _httpClient = new HttpClient(handler)
        {
            Timeout = TimeSpan.FromSeconds(5),
        };
    }

    /// <summary>
    /// Returns true if the server URL is reachable valid, false otherwise.
    /// </summary>
    /// <param name="url"></param>
    /// <returns></returns>
    public async Task<bool> ValidateServerUrl(string url)
    {
        try
        {
            var apiString = $"{url}/api/Database/validate-server";
        
            using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
            var response = await  _httpClient.GetAsync(apiString, cts.Token);

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

            var responseString = await response.Content.ReadAsStringAsync(cts.Token);

            var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
            var validationResponse = JsonSerializer.Deserialize<ServerUrlValidationResponse>(responseString, options);
        
            return validationResponse?.IsValid ?? false;
        }
        catch
        {
            throw new HttpRequestException($"Server {url} is invalid or not reachable.");
            return false;
        }
    }
    
    /// <summary>
    /// Register a new user with the server.
    /// </summary>
    /// <param name="url"></param>
    /// <param name="username"></param>
    /// <param name="email"></param>
    /// <param name="password"></param>
    /// <returns></returns>
    /// <exception cref="HttpRequestException"></exception>
    public async Task<string> Register(string url, string username, string email, string password)
    {
        bool isValidServer = await ValidateServerUrl(url);
        if (!isValidServer)
        {
            throw new HttpRequestException($"Could not Register User, Server {url} is invalid or not reachable.");
        }
        
        var apiString = $"{url}/api/Auth/register";
        var requestBody = new Dictionary<string, string>
        {
            {"username", username},
            {"email", email},
            {"password", password}
        };
        
        var jsonBody = JsonSerializer.Serialize(requestBody);
        var content = new StringContent(jsonBody, System.Text.Encoding.UTF8, "application/json");
        var response = await _httpClient.PostAsync(apiString, content);
        
        if (!response.IsSuccessStatusCode)
        {
            // throw new HttpRequestException($"Register failed: {response.StatusCode}");
            
        }
        
        var responseString = await response.Content.ReadAsStringAsync();
        return responseString;
    }

    /// <summary>
    /// Login to the server with the given username and password. Returns the JWT token if successful.
    /// </summary>
    /// <param name="url"></param>
    /// <param name="username"></param>
    /// <param name="password"></param>
    /// <returns></returns>
    /// <exception cref="HttpRequestException"></exception>
    public async Task<string> Login(string url, string username, string password)
    {
        
        bool isValidServer = await ValidateServerUrl(url);
        if (!isValidServer)
        {
            throw new HttpRequestException($"Could not Login, Server {url} is invalid or not reachable.");
        }
        
        var apiString = $"{url}/api/Auth/login";
        var requestBody = new Dictionary<string, string>
        {
            {"username", username},
            {"password", password}
        };
        
        var jsonBody = JsonSerializer.Serialize(requestBody);
        var content = new StringContent(jsonBody, System.Text.Encoding.UTF8, "application/json");
        var response = await _httpClient.PostAsync(apiString, content);
        
        if (!response.IsSuccessStatusCode)
        {
            // throw new HttpRequestException($"Login failed: {response.StatusCode}");
        }
        
        var responseString = await response.Content.ReadAsStringAsync();
        return responseString;
    }
    
    public class ServerUrlValidationResponse
    {
        public bool IsValid { get; set; }
        public string Message { get; set; }
    }
}

-----------------------------------------------------

using System.Threading.Tasks;

namespace SessionZeroClient.API;

public interface IApiHandler
{
    Task<bool> ValidateServerUrl(string url);
    Task<string> Register(string url, string username, string email, string password);
    Task<string> Login(string url, string username, string password);
}

------------------------------------------------------

using Godot;
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;
using SessionZeroClient.API;

namespace SessionZeroClient;

public class AccountManager(IApiHandler apiHandler)
{
    public string Username { get; private set; } = "";
    public string Email { get; private set; } = "";
    public string Jwt { get; private set; } = "";
    
    private IApiHandler _apiHandler = apiHandler;
    
    public async Task<bool> Register(string url, string username, string email, string password)
    {
        var response = await _apiHandler.Register(url, username, email, password);
    
        if (!string.IsNullOrEmpty(response))
        {
            var responseData = JsonSerializer.Deserialize<Dictionary<string, string>>(response);
            if (responseData != null && responseData.ContainsKey("message") && responseData["message"] == "User registered successfully")
            {
                return true;
            }
        }
    
        return false;
    }
    
    public async Task<bool> Login(string url, string username, string password)
    {
        var response = await _apiHandler.Login(url, username, password);
    
        if (!string.IsNullOrEmpty(response))
        {
            var responseData = JsonSerializer.Deserialize<Dictionary<string, string>>(response);
            if (responseData != null && responseData.ContainsKey("token"))
            {
                Jwt = responseData["token"];
                Username = username;
                return true;
            }
        }
    
        return false;
    }
    
    public void Logout()
    {
        Jwt = "";
        Username = "";
        Email = "";
    }
}

-----------------------------------------------------------

using Godot;
using System;
using System.Threading.Tasks;
using SessionZeroClient;
using SessionZeroClient.API;

public partial class AccountScreenLogic : Node
{
    [ExportGroup("UI Elements")]
    // Selection Screen
    [ExportSubgroup("Selection Screen")]
    [Export] private VBoxContainer SelectionScreenVboxContainer { get; set; }
    [Export] private Button RegisterSelectionButton { get; set; }
    [Export] private Button LoginSelectionButton { get; set; }
    [Export] private LineEdit UrlLineEdit { get; set; }
    
    // Register Screen
    [ExportSubgroup("Register Screen")]
    [Export] private VBoxContainer RegisterScreenVboxContainer { get; set; }
    [Export] private LineEdit RegisterUsernameLineEdit { get; set; }
    [Export] private LineEdit RegisterEmailLineEdit { get; set; }
    [Export] private LineEdit RegisterPasswordLineEdit { get; set; }
    [Export] private Button RegisterButton { get; set; }
    
    // Login Screen
    [ExportSubgroup("Login Screen")]
    [Export] private VBoxContainer LoginScreenVboxContainer { get; set; }
    [Export] private LineEdit LoginUsernameLineEdit { get; set; }
    [Export] private LineEdit LoginPasswordLineEdit { get; set; }
    [Export] private Button LoginButton { get; set; }
    
    // Account Screen
    [ExportSubgroup("Account Screen")]
    [Export] private VBoxContainer AccountScreenVboxContainer { get; set; }
    [Export] private Label AccountScreenGreetingLabel { get; set; }
    [Export] private Button LogoutButton { get; set; }
    [Export] private Button OfflineModeButton { get; set; }
    
    // Other
    [ExportSubgroup("Other")]
    [Export] Button BackButton { get; set; }
    [Export] RichTextLabel StatusLabel { get; set; }
    
    
    private IApiHandler _apiHandler;
    private AccountManager _accountManager;
    private string _url = "";
    
    public void Init(IApiHandler apiHandler, AccountManager accountManager)
    {
        _apiHandler = apiHandler;
        _accountManager = accountManager;
        SetupSignals();
        InitialState();
    }
    
    private void InitialState()
    {
        SelectionScreenVboxContainer.Visible = true;
        RegisterScreenVboxContainer.Visible = false;
        LoginScreenVboxContainer.Visible = false;
        AccountScreenVboxContainer.Visible = false;
        BackButton.Visible = false;
    }
    
    private void RegisterState()
    {
        SelectionScreenVboxContainer.Visible = false;
        RegisterScreenVboxContainer.Visible = true;
        LoginScreenVboxContainer.Visible = false;
        AccountScreenVboxContainer.Visible = false;
        BackButton.Visible = true;
    }
    
    private void LoginState()
    {
        SelectionScreenVboxContainer.Visible = false;
        RegisterScreenVboxContainer.Visible = false;
        LoginScreenVboxContainer.Visible = true;
        AccountScreenVboxContainer.Visible = false;
        BackButton.Visible = true;
    }
    
    private void AccountSuccessState()
    {
        SelectionScreenVboxContainer.Visible = false;
        RegisterScreenVboxContainer.Visible = false;
        LoginScreenVboxContainer.Visible = false;
        AccountScreenVboxContainer.Visible = true;
        BackButton.Visible = false;
        
        AccountScreenGreetingLabel.Text = $"Welcome, {_accountManager.Username}!";
    }

    private void SetupSignals()
    {
        // Selection Screen
        RegisterSelectionButton.Pressed += RegisterSelectionButtonPressed;
        LoginSelectionButton.Pressed += LoginSelectionButtonPressed;
        OfflineModeButton.Pressed += () => GD.Print("Offline mode not implemented yet.");
        
        // Register Screen
        RegisterButton.Pressed += RegisterButtonPressed;
        
        // Login Screen
        LoginButton.Pressed += LoginButtonPressed;
        
        // Account Screen
        LogoutButton.Pressed += Logout;
        
        // Other
        BackButton.Pressed += BackButtonPressed;
    }
    
    private async void RegisterSelectionButtonPressed()
    {
        bool isValidServer = await ValidateServerUrl(UrlLineEdit.Text);
        if (isValidServer)
        {
            _url = UrlLineEdit.Text;
            RegisterState();
        }
    }
    
    private async void LoginSelectionButtonPressed()
    {
        bool isValidServer = await ValidateServerUrl(UrlLineEdit.Text);
        if (isValidServer)
        {
            _url = UrlLineEdit.Text;
            LoginState();
        }
    }
    
    private async void RegisterButtonPressed()
    {
        var username = RegisterUsernameLineEdit.Text;
        var email = RegisterEmailLineEdit.Text;
        var password = RegisterPasswordLineEdit.Text;
        
        var success = await _accountManager.Register(_url, username, email, password);
        
        GD.Print($"Register success: {success}");
        SetStatusLabel($"Register success: {success}", success ? Colors.Green : Colors.Red);
        
        if (success)
        {
            var loginSuccess = await _accountManager.Login(_url, username, password);
            
            GD.Print($"Login success: {loginSuccess}");
            SetStatusLabel($"Login success: {loginSuccess}", loginSuccess ? Colors.Green : Colors.Red);

            if (loginSuccess)
            {
                AccountSuccessState();    
            }
            else
            {
                SetStatusLabel($"Login failed: {loginSuccess}", Colors.Red);
            }
        }
        else
        {
            SetStatusLabel("Registration failed", Colors.Red);
        }
    }
    
    private async void LoginButtonPressed()
    {
        var username = LoginUsernameLineEdit.Text;
        var password = LoginPasswordLineEdit.Text;
        var success = await _accountManager.Login(_url, username, password);
        
        GD.Print($"Login success: {success}");
        SetStatusLabel($"Login success: {success}", success ? Colors.Green : Colors.Red);
        
        if (success)
        {
            AccountSuccessState();    
        }
        else
        {
            SetStatusLabel("Login failed. Check Username and Password.", Colors.Red);
        }
    }

    private async Task<bool> ValidateServerUrl(string url)
    {
        if (string.IsNullOrEmpty(url))
        {
            return false;
        }

        try 
        {
            var isValid = await _apiHandler.ValidateServerUrl(url);
            if (isValid)
            {
                GD.Print($"Server {url} is valid.");
                SetStatusLabel($"Server {url} is valid.", Colors.Green);
            }

            return isValid;
        }
        catch (Exception ex)
        {
            GD.Print($"Server validation error: {ex.Message}");
            SetStatusLabel($"Server validation error: {ex.Message}", Colors.Red);
            return false;
        }
    }
    
    private void Logout()
    {
        _accountManager.Logout();
        GD.Print("Logged out");
        SetStatusLabel("Logged out", Colors.Cyan);
        InitialState();
    }
    
    private void BackButtonPressed()
    {
        InitialState();
    }
    
    private void SetStatusLabel(string message, Color color)
    {
        StatusLabel.Text = $"[color={color.ToHtml()}] {message} [/color]";
    }
}
