using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.DependencyInjection;
using OTSSignsOrchestrator.Core.Models.Entities;
using OTSSignsOrchestrator.Core.Services;
using OTSSignsOrchestrator.Desktop.Models;
using OTSSignsOrchestrator.Desktop.Services;

namespace OTSSignsOrchestrator.Desktop.ViewModels;

/// <summary>
/// ViewModel for the instance details modal.
/// Shows admin credentials, DB credentials, and OAuth2 app details
/// with options to rotate passwords.
/// </summary>
public partial class InstanceDetailsViewModel : ObservableObject
{
    private readonly IServiceProvider _services;

    // ── Instance metadata ─────────────────────────────────────────────────────
    [ObservableProperty] private string _stackName    = string.Empty;
    [ObservableProperty] private string _customerAbbrev = string.Empty;
    [ObservableProperty] private string _hostLabel    = string.Empty;
    [ObservableProperty] private string _instanceUrl  = string.Empty;

    // ── OTS admin credentials ─────────────────────────────────────────────────
    [ObservableProperty] private string  _adminUsername        = string.Empty;
    [ObservableProperty] private string  _adminPassword        = string.Empty;
    [ObservableProperty] private bool    _adminPasswordVisible = false;
    [ObservableProperty] private string  _adminPasswordDisplay = "••••••••";

    // ── Database credentials ──────────────────────────────────────────────────
    [ObservableProperty] private string  _dbUsername           = string.Empty;
    [ObservableProperty] private string  _dbPassword           = string.Empty;
    [ObservableProperty] private bool    _dbPasswordVisible    = false;
    [ObservableProperty] private string  _dbPasswordDisplay    = "••••••••";

    // ── OAuth2 application ────────────────────────────────────────────────────
    [ObservableProperty] private string  _oAuthClientId        = string.Empty;
    [ObservableProperty] private string  _oAuthClientSecret    = string.Empty;
    [ObservableProperty] private bool    _oAuthSecretVisible   = false;
    [ObservableProperty] private string  _oAuthSecretDisplay   = "••••••••";

    // ── Status ────────────────────────────────────────────────────────────────
    [ObservableProperty] private string _statusMessage = string.Empty;
    [ObservableProperty] private bool   _isBusy;
    // ── Pending-setup inputs (shown when instance hasn't been initialised yet) ────────────
    [ObservableProperty] private bool   _isPendingSetup;
    [ObservableProperty] private string _initClientId     = string.Empty;
    [ObservableProperty] private string _initClientSecret = string.Empty;

    // Cached instance — needed by InitializeCommand to reload after setup
    private LiveStackItem? _currentInstance;
    public InstanceDetailsViewModel(IServiceProvider services)
    {
        _services = services;
    }

    // ─────────────────────────────────────────────────────────────────────────
    // Load
    // ─────────────────────────────────────────────────────────────────────────

    /// <summary>Populates the ViewModel from a live <see cref="LiveStackItem"/>.</summary>
    public async Task LoadAsync(LiveStackItem instance)
    {
        _currentInstance = instance;
        StackName      = instance.StackName;
        CustomerAbbrev = instance.CustomerAbbrev;
        HostLabel      = instance.HostLabel;

        IsBusy = true;
        StatusMessage = "Loading credentials...";

        try
        {
            using var scope   = _services.CreateScope();
            var settings      = scope.ServiceProvider.GetRequiredService<SettingsService>();
            var postInit      = scope.ServiceProvider.GetRequiredService<PostInstanceInitService>();

            // Derive the instance URL from the CMS server name template
            var serverTemplate = await settings.GetAsync(
                SettingsService.DefaultCmsServerNameTemplate, "app.ots-signs.com");
            var serverName = serverTemplate.Replace("{abbrev}", instance.CustomerAbbrev);
            InstanceUrl = $"https://{serverName}/{instance.CustomerAbbrev.Trim().ToLowerInvariant()}";

            // ── Admin credentials ─────────────────────────────────────────
            var creds = await postInit.GetCredentialsAsync(instance.CustomerAbbrev);
            AdminUsername = creds.AdminUsername;
            SetAdminPassword(creds.AdminPassword ?? string.Empty);

            OAuthClientId = creds.OAuthClientId ?? string.Empty;
            SetOAuthSecret(creds.OAuthClientSecret ?? string.Empty);

            // ── DB credentials ────────────────────────────────────────────
            var mySqlUserTemplate = await settings.GetAsync(
                SettingsService.DefaultMySqlUserTemplate, "{abbrev}_cms_user");
            DbUsername = mySqlUserTemplate.Replace("{abbrev}", instance.CustomerAbbrev);

            var dbPw = await settings.GetAsync(SettingsService.InstanceMySqlPassword(instance.CustomerAbbrev));
            SetDbPassword(dbPw ?? string.Empty);

            StatusMessage = creds.HasAdminPassword
                ? "Credentials loaded."
                : "Pending setup — enter your Xibo OAuth credentials below to initialise this instance.";

            IsPendingSetup    = !creds.HasAdminPassword;
            // Clear any previous init inputs when re-loading
            if (IsPendingSetup)
            {
                InitClientId     = string.Empty;
                InitClientSecret = string.Empty;
            }
        }
        catch (Exception ex)
        {
            StatusMessage = $"Error loading credentials: {ex.Message}";
        }
        finally
        {
            IsBusy = false;
        }
    }

    // ─────────────────────────────────────────────────────────────────────────
    // Initialise (pending setup)
    // ─────────────────────────────────────────────────────────────────────────

    [RelayCommand]
    private async Task InitializeAsync()
    {
        if (string.IsNullOrWhiteSpace(InitClientId) || string.IsNullOrWhiteSpace(InitClientSecret))
        {
            StatusMessage = "Both Client ID and Client Secret are required.";
            return;
        }
        if (_currentInstance is null) return;

        IsBusy = true;
        StatusMessage = "Waiting for Xibo and running initialisation (this may take several minutes)...";
        try
        {
            var postInit = _services.GetRequiredService<PostInstanceInitService>();
            await postInit.InitializeWithOAuthAsync(
                CustomerAbbrev,
                InstanceUrl,
                InitClientId.Trim(),
                InitClientSecret.Trim());

            // Reload credentials — IsPendingSetup will flip to false
            IsBusy = false;
            await LoadAsync(_currentInstance);
        }
        catch (Exception ex)
        {
            StatusMessage = $"Initialisation failed: {ex.Message}";
            IsBusy = false;
        }
    }

    // ─────────────────────────────────────────────────────────────────────────
    // Visibility toggles
    // ─────────────────────────────────────────────────────────────────────────

    [RelayCommand]
    private void ToggleAdminPasswordVisibility()
    {
        AdminPasswordVisible = !AdminPasswordVisible;
        AdminPasswordDisplay = AdminPasswordVisible
            ? AdminPassword
            : (AdminPassword.Length > 0 ? "••••••••" : "(not set)");
    }

    [RelayCommand]
    private void ToggleDbPasswordVisibility()
    {
        DbPasswordVisible = !DbPasswordVisible;
        DbPasswordDisplay = DbPasswordVisible
            ? DbPassword
            : (DbPassword.Length > 0 ? "••••••••" : "(not set)");
    }

    [RelayCommand]
    private void ToggleOAuthSecretVisibility()
    {
        OAuthSecretVisible = !OAuthSecretVisible;
        OAuthSecretDisplay = OAuthSecretVisible
            ? OAuthClientSecret
            : (OAuthClientSecret.Length > 0 ? "••••••••" : "(not set)");
    }

    // ─────────────────────────────────────────────────────────────────────────
    // Clipboard
    // ─────────────────────────────────────────────────────────────────────────

    [RelayCommand]
    private async Task CopyAdminPasswordAsync()
        => await CopyToClipboardAsync(AdminPassword, "Admin password");

    [RelayCommand]
    private async Task CopyDbPasswordAsync()
        => await CopyToClipboardAsync(DbPassword, "DB password");

    [RelayCommand]
    private async Task CopyOAuthClientIdAsync()
        => await CopyToClipboardAsync(OAuthClientId, "OAuth client ID");

    [RelayCommand]
    private async Task CopyOAuthSecretAsync()
        => await CopyToClipboardAsync(OAuthClientSecret, "OAuth client secret");

    // ─────────────────────────────────────────────────────────────────────────
    // Rotation
    // ─────────────────────────────────────────────────────────────────────────

    [RelayCommand]
    private async Task RotateAdminPasswordAsync()
    {
        if (string.IsNullOrWhiteSpace(CustomerAbbrev)) return;

        IsBusy = true;
        StatusMessage = "Rotating OTS admin password...";
        try
        {
            var postInit   = _services.GetRequiredService<PostInstanceInitService>();
            var newPassword = await postInit.RotateAdminPasswordAsync(CustomerAbbrev, InstanceUrl);
            SetAdminPassword(newPassword);
            StatusMessage = "Admin password rotated successfully.";
        }
        catch (Exception ex)
        {
            StatusMessage = $"Error rotating admin password: {ex.Message}";
        }
        finally
        {
            IsBusy = false;
        }
    }

    [RelayCommand]
    private async Task RotateDbPasswordAsync()
    {
        if (string.IsNullOrWhiteSpace(CustomerAbbrev)) return;

        IsBusy = true;
        StatusMessage = $"Rotating MySQL password for {StackName}...";
        try
        {
            var dockerCli = _services.GetRequiredService<SshDockerCliService>();
            var dockerSecrets = _services.GetRequiredService<SshDockerSecretsService>();

            // We need the Host — retrieve from the HostLabel lookup
            using var scope = _services.CreateScope();
            var instanceSvc = scope.ServiceProvider.GetRequiredService<InstanceService>();

            // Get the host from the loaded stack — caller must have set the SSH host before
            var (ok, msg) = await instanceSvc.RotateMySqlPasswordAsync(StackName);
            if (ok)
            {
                // Reload DB password
                var settings = scope.ServiceProvider.GetRequiredService<SettingsService>();
                var dbPw = await settings.GetAsync(SettingsService.InstanceMySqlPassword(CustomerAbbrev));
                SetDbPassword(dbPw ?? string.Empty);
                StatusMessage = $"DB password rotated: {msg}";
            }
            else
            {
                StatusMessage = $"DB rotation failed: {msg}";
            }
        }
        catch (Exception ex)
        {
            StatusMessage = $"Error rotating DB password: {ex.Message}";
        }
        finally
        {
            IsBusy = false;
        }
    }

    // ─────────────────────────────────────────────────────────────────────────
    // Helpers
    // ─────────────────────────────────────────────────────────────────────────

    private void SetAdminPassword(string value)
    {
        AdminPassword        = value;
        AdminPasswordVisible = false;
        AdminPasswordDisplay = value.Length > 0 ? "••••••••" : "(not set)";
    }

    private void SetDbPassword(string value)
    {
        DbPassword        = value;
        DbPasswordVisible = false;
        DbPasswordDisplay = value.Length > 0 ? "••••••••" : "(not set)";
    }

    private void SetOAuthSecret(string value)
    {
        OAuthClientSecret  = value;
        OAuthSecretVisible = false;
        OAuthSecretDisplay = value.Length > 0 ? "••••••••" : "(not set)";
    }

    private static async Task CopyToClipboardAsync(string text, string label)
    {
        if (string.IsNullOrEmpty(text)) return;
        var topLevel = Avalonia.Application.Current?.ApplicationLifetime is
            Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime dt
            ? dt.MainWindow
            : null;
        var clipboard = topLevel is not null ? Avalonia.Controls.TopLevel.GetTopLevel(topLevel)?.Clipboard : null;
        if (clipboard is not null)
            await clipboard.SetTextAsync(text);
    }
}
