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

namespace OTSSignsOrchestrator.Desktop.ViewModels;

/// <summary>
/// ViewModel for managing SSH host connections.
/// Allows adding, editing, testing, and removing remote Docker Swarm hosts.
/// </summary>
public partial class HostsViewModel : ObservableObject
{
    private readonly IServiceProvider _services;

    [ObservableProperty] private ObservableCollection<SshHost> _hosts = new();
    [ObservableProperty] private SshHost? _selectedHost;
    [ObservableProperty] private bool _isEditing;
    [ObservableProperty] private string _statusMessage = string.Empty;
    [ObservableProperty] private bool _isBusy;
    [ObservableProperty] private ObservableCollection<NodeInfo> _remoteNodes = new();
    [ObservableProperty] private string _nodesStatusMessage = string.Empty;

    // Edit form fields
    [ObservableProperty] private string _editLabel = string.Empty;
    [ObservableProperty] private string _editHost = string.Empty;
    [ObservableProperty] private int _editPort = 22;
    [ObservableProperty] private string _editUsername = string.Empty;
    [ObservableProperty] private string _editPrivateKeyPath = string.Empty;
    [ObservableProperty] private string _editKeyPassphrase = string.Empty;
    [ObservableProperty] private string _editPassword = string.Empty;
    [ObservableProperty] private bool _editUseKeyAuth = true;
    private Guid? _editingHostId;

    public HostsViewModel(IServiceProvider services)
    {
        _services = services;
        _ = LoadHostsAsync();
    }

    [RelayCommand]
    private async Task LoadHostsAsync()
    {
        IsBusy = true;
        try
        {
            using var scope = _services.CreateScope();
            var db = scope.ServiceProvider.GetRequiredService<XiboContext>();
            var hosts = await db.SshHosts.OrderBy(h => h.Label).ToListAsync();

            Hosts = new ObservableCollection<SshHost>(hosts);
            StatusMessage = $"Loaded {hosts.Count} host(s).";
        }
        catch (Exception ex)
        {
            StatusMessage = $"Error loading hosts: {ex.Message}";
        }
        finally
        {
            IsBusy = false;
        }
    }

    [RelayCommand]
    private void NewHost()
    {
        _editingHostId = null;
        EditLabel = string.Empty;
        EditHost = string.Empty;
        EditPort = 22;
        EditUsername = string.Empty;
        EditPrivateKeyPath = string.Empty;
        EditKeyPassphrase = string.Empty;
        EditPassword = string.Empty;
        EditUseKeyAuth = true;
        IsEditing = true;
    }

    [RelayCommand]
    private void EditSelectedHost()
    {
        if (SelectedHost == null) return;

        _editingHostId = SelectedHost.Id;
        EditLabel = SelectedHost.Label;
        EditHost = SelectedHost.Host;
        EditPort = SelectedHost.Port;
        EditUsername = SelectedHost.Username;
        EditPrivateKeyPath = SelectedHost.PrivateKeyPath ?? string.Empty;
        EditKeyPassphrase = string.Empty; // Don't show existing passphrase
        EditPassword = string.Empty; // Don't show existing password
        EditUseKeyAuth = SelectedHost.UseKeyAuth;
        IsEditing = true;
    }

    [RelayCommand]
    private void CancelEdit()
    {
        IsEditing = false;
    }

    [RelayCommand]
    private async Task SaveHostAsync()
    {
        if (string.IsNullOrWhiteSpace(EditLabel) || string.IsNullOrWhiteSpace(EditHost) || string.IsNullOrWhiteSpace(EditUsername))
        {
            StatusMessage = "Label, Host, and Username are required.";
            return;
        }

        IsBusy = true;
        try
        {
            using var scope = _services.CreateScope();
            var db = scope.ServiceProvider.GetRequiredService<XiboContext>();

            SshHost host;
            if (_editingHostId.HasValue)
            {
                host = await db.SshHosts.FindAsync(_editingHostId.Value)
                    ?? throw new KeyNotFoundException("Host not found.");

                host.Label = EditLabel;
                host.Host = EditHost;
                host.Port = EditPort;
                host.Username = EditUsername;
                host.PrivateKeyPath = string.IsNullOrWhiteSpace(EditPrivateKeyPath) ? null : EditPrivateKeyPath;
                host.UseKeyAuth = EditUseKeyAuth;
                host.UpdatedAt = DateTime.UtcNow;

                if (!string.IsNullOrEmpty(EditKeyPassphrase))
                    host.KeyPassphrase = EditKeyPassphrase;
                if (!string.IsNullOrEmpty(EditPassword))
                    host.Password = EditPassword;
            }
            else
            {
                host = new SshHost
                {
                    Label = EditLabel,
                    Host = EditHost,
                    Port = EditPort,
                    Username = EditUsername,
                    PrivateKeyPath = string.IsNullOrWhiteSpace(EditPrivateKeyPath) ? null : EditPrivateKeyPath,
                    KeyPassphrase = string.IsNullOrEmpty(EditKeyPassphrase) ? null : EditKeyPassphrase,
                    Password = string.IsNullOrEmpty(EditPassword) ? null : EditPassword,
                    UseKeyAuth = EditUseKeyAuth
                };
                db.SshHosts.Add(host);
            }

            await db.SaveChangesAsync();

            IsEditing = false;
            StatusMessage = $"Host '{host.Label}' saved.";
            await LoadHostsAsync();
        }
        catch (Exception ex)
        {
            StatusMessage = $"Error saving host: {ex.Message}";
        }
        finally
        {
            IsBusy = false;
        }
    }

    [RelayCommand]
    private async Task DeleteHostAsync()
    {
        if (SelectedHost == null) return;

        IsBusy = true;
        try
        {
            using var scope = _services.CreateScope();
            var db = scope.ServiceProvider.GetRequiredService<XiboContext>();

            var host = await db.SshHosts.FindAsync(SelectedHost.Id);
            if (host != null)
            {
                db.SshHosts.Remove(host);
                await db.SaveChangesAsync();
            }

            // Disconnect if connected
            var ssh = _services.GetRequiredService<SshConnectionService>();
            ssh.Disconnect(SelectedHost.Id);

            StatusMessage = $"Host '{SelectedHost.Label}' deleted.";
            await LoadHostsAsync();
        }
        catch (Exception ex)
        {
            StatusMessage = $"Error deleting host: {ex.Message}";
        }
        finally
        {
            IsBusy = false;
        }
    }

    [RelayCommand]
    private async Task ListNodesAsync()
    {
        if (SelectedHost == null)
        {
            NodesStatusMessage = "Select a host first.";
            return;
        }

        IsBusy = true;
        NodesStatusMessage = $"Listing nodes on {SelectedHost.Label}...";
        try
        {
            var dockerCli = _services.GetRequiredService<SshDockerCliService>();
            dockerCli.SetHost(SelectedHost);
            var nodes = await dockerCli.ListNodesAsync();
            RemoteNodes = new ObservableCollection<NodeInfo>(nodes);
            NodesStatusMessage = $"Found {nodes.Count} node(s) on {SelectedHost.Label}.";
        }
        catch (Exception ex)
        {
            RemoteNodes.Clear();
            NodesStatusMessage = $"Error: {ex.Message}";
        }
        finally
        {
            IsBusy = false;
        }
    }

    [RelayCommand]
    private async Task TestConnectionAsync()
    {
        if (SelectedHost == null) return;

        IsBusy = true;
        StatusMessage = $"Testing connection to {SelectedHost.Label}...";
        try
        {
            var ssh = _services.GetRequiredService<SshConnectionService>();
            var (success, message) = await ssh.TestConnectionAsync(SelectedHost);

            // Update DB
            using var scope = _services.CreateScope();
            var db = scope.ServiceProvider.GetRequiredService<XiboContext>();
            var host = await db.SshHosts.FindAsync(SelectedHost.Id);
            if (host != null)
            {
                host.LastTestedAt = DateTime.UtcNow;
                host.LastTestSuccess = success;
                await db.SaveChangesAsync();
            }

            StatusMessage = success
                ? $"✓ {SelectedHost.Label}: {message}"
                : $"✗ {SelectedHost.Label}: {message}";

            await LoadHostsAsync();
        }
        catch (Exception ex)
        {
            StatusMessage = $"Connection test error: {ex.Message}";
        }
        finally
        {
            IsBusy = false;
        }
    }
}
