using System.Collections.ObjectModel;
using Avalonia.Threading;
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.Core.Services;
using OTSSignsOrchestrator.Desktop.Models;
using OTSSignsOrchestrator.Desktop.Services;

namespace OTSSignsOrchestrator.Desktop.ViewModels;

/// <summary>
/// ViewModel for listing, viewing, and managing CMS instances.
/// All data is fetched live from Docker Swarm hosts — nothing stored locally.
/// </summary>
public partial class InstancesViewModel : ObservableObject
{
    private readonly IServiceProvider _services;

    [ObservableProperty] private ObservableCollection<LiveStackItem> _instances = new();
    [ObservableProperty] private LiveStackItem? _selectedInstance;
    [ObservableProperty] private string _statusMessage = string.Empty;
    [ObservableProperty] private bool _isBusy;
    [ObservableProperty] private string _filterText = string.Empty;
    [ObservableProperty] private ObservableCollection<ServiceInfo> _selectedServices = new();

    // Available SSH hosts — loaded for display and used to scope operations
    [ObservableProperty] private ObservableCollection<SshHost> _availableHosts = new();
    [ObservableProperty] private SshHost? _selectedSshHost;

    // ── Container Logs ──────────────────────────────────────────────────────
    [ObservableProperty] private ObservableCollection<ServiceLogEntry> _logEntries = new();
    [ObservableProperty] private ObservableCollection<string> _logServiceFilter = new();
    [ObservableProperty] private string _selectedLogService = "All Services";
    [ObservableProperty] private bool _isLogsPanelVisible;
    [ObservableProperty] private bool _isLogsAutoRefresh = true;
    [ObservableProperty] private bool _isLoadingLogs;
    [ObservableProperty] private string _logsStatusMessage = string.Empty;
    [ObservableProperty] private int _logTailLines = 200;

    private DispatcherTimer? _logRefreshTimer;
    private bool _isLogRefreshRunning;

    /// <summary>Raised when the instance details modal should be opened for the given ViewModel.</summary>
    public event Action<InstanceDetailsViewModel>? OpenDetailsRequested;

    private string? _pendingSelectAbbrev;

    public InstancesViewModel(IServiceProvider services)
    {
        _services = services;
        _ = RefreshAllAsync();
    }

    /// <summary>
    /// Queues an abbreviation to be auto-selected once the next live refresh completes.
    /// Call immediately after construction (before <see cref="RefreshAllAsync"/> finishes).
    /// </summary>
    public void SetPendingSelection(string abbrev)
        => _pendingSelectAbbrev = abbrev;

    /// <summary>
    /// Enumerates all SSH hosts, then calls docker stack ls on each to build the
    /// live instance list. Only stacks matching *-cms-stack are shown.
    /// </summary>
    [RelayCommand]
    private async Task LoadInstancesAsync() => await RefreshAllAsync();

    private async Task RefreshAllAsync()
    {
        IsBusy = true;
        StatusMessage = "Loading live instances from all hosts...";
        SelectedServices = new ObservableCollection<ServiceInfo>();
        try
        {
            using var scope = _services.CreateScope();
            var db = scope.ServiceProvider.GetRequiredService<XiboContext>();
            var hosts = await db.SshHosts.OrderBy(h => h.Label).ToListAsync();
            AvailableHosts = new ObservableCollection<SshHost>(hosts);

            var dockerCli = _services.GetRequiredService<SshDockerCliService>();
            var all = new List<LiveStackItem>();
            var errors = new List<string>();

            foreach (var host in hosts)
            {
                try
                {
                    dockerCli.SetHost(host);
                    var stacks = await dockerCli.ListStacksAsync();
                    foreach (var stack in stacks.Where(s => s.Name.EndsWith("-cms-stack")))
                    {
                        all.Add(new LiveStackItem
                        {
                            StackName      = stack.Name,
                            CustomerAbbrev = stack.Name[..^10],
                            ServiceCount   = stack.ServiceCount,
                            Host           = host,
                        });
                    }
                }
                catch (Exception ex) { errors.Add($"{host.Label}: {ex.Message}"); }
            }

            if (!string.IsNullOrWhiteSpace(FilterText))
                all = all.Where(i =>
                    i.StackName.Contains(FilterText, StringComparison.OrdinalIgnoreCase) ||
                    i.CustomerAbbrev.Contains(FilterText, StringComparison.OrdinalIgnoreCase) ||
                    i.HostLabel.Contains(FilterText, StringComparison.OrdinalIgnoreCase)).ToList();

            Instances = new ObservableCollection<LiveStackItem>(all);

            // Auto-select a pending instance (e.g. just deployed from Create Instance page)
            if (_pendingSelectAbbrev is not null)
            {
                SelectedInstance = all.FirstOrDefault(i =>
                    i.CustomerAbbrev.Equals(_pendingSelectAbbrev, StringComparison.OrdinalIgnoreCase));
                _pendingSelectAbbrev = null;
            }

            var msg = $"Found {all.Count} instance(s) across {hosts.Count} host(s).";
            if (errors.Count > 0) msg += $" | Errors: {string.Join(" | ", errors)}";
            StatusMessage = msg;
        }
        catch (Exception ex) { StatusMessage = $"Error: {ex.Message}"; }
        finally { IsBusy = false; }
    }

    [RelayCommand]
    private async Task InspectInstanceAsync()
    {
        if (SelectedInstance == null) return;
        IsBusy = true;
        StatusMessage = $"Inspecting '{SelectedInstance.StackName}'...";
        try
        {
            var dockerCli = _services.GetRequiredService<SshDockerCliService>();
            dockerCli.SetHost(SelectedInstance.Host);
            var services = await dockerCli.InspectStackServicesAsync(SelectedInstance.StackName);
            SelectedServices = new ObservableCollection<ServiceInfo>(services);
            StatusMessage = $"Found {services.Count} service(s) in stack '{SelectedInstance.StackName}'.";

            // Populate service filter dropdown and show logs panel
            var filterItems = new List<string> { "All Services" };
            filterItems.AddRange(services.Select(s => s.Name));
            LogServiceFilter = new ObservableCollection<string>(filterItems);
            SelectedLogService = "All Services";
            IsLogsPanelVisible = true;

            // Fetch initial logs and start auto-refresh
            await FetchLogsInternalAsync();
            StartLogAutoRefresh();
        }
        catch (Exception ex) { StatusMessage = $"Error inspecting: {ex.Message}"; }
        finally { IsBusy = false; }
    }

    // ── Container Log Commands ──────────────────────────────────────────────

    [RelayCommand]
    private async Task RefreshLogsAsync()
    {
        await FetchLogsInternalAsync();
    }

    [RelayCommand]
    private void ToggleLogsAutoRefresh()
    {
        IsLogsAutoRefresh = !IsLogsAutoRefresh;
        if (IsLogsAutoRefresh)
            StartLogAutoRefresh();
        else
            StopLogAutoRefresh();
    }

    [RelayCommand]
    private void CloseLogsPanel()
    {
        StopLogAutoRefresh();
        IsLogsPanelVisible = false;
        LogEntries = new ObservableCollection<ServiceLogEntry>();
        LogsStatusMessage = string.Empty;
    }

    partial void OnSelectedLogServiceChanged(string value)
    {
        // When user changes the service filter, refresh logs immediately
        if (IsLogsPanelVisible)
            _ = FetchLogsInternalAsync();
    }

    private async Task FetchLogsInternalAsync()
    {
        if (SelectedInstance == null || _isLogRefreshRunning) return;

        _isLogRefreshRunning = true;
        IsLoadingLogs = true;
        try
        {
            var dockerCli = _services.GetRequiredService<SshDockerCliService>();
            dockerCli.SetHost(SelectedInstance.Host);

            string? serviceFilter = SelectedLogService == "All Services" ? null : SelectedLogService;
            var entries = await dockerCli.GetServiceLogsAsync(
                SelectedInstance.StackName, serviceFilter, LogTailLines);

            LogEntries = new ObservableCollection<ServiceLogEntry>(entries);
            LogsStatusMessage = $"{entries.Count} log line(s) · last fetched {DateTime.Now:HH:mm:ss}";
        }
        catch (Exception ex)
        {
            LogsStatusMessage = $"Error fetching logs: {ex.Message}";
        }
        finally
        {
            IsLoadingLogs = false;
            _isLogRefreshRunning = false;
        }
    }

    private void StartLogAutoRefresh()
    {
        StopLogAutoRefresh();
        if (!IsLogsAutoRefresh) return;

        _logRefreshTimer = new DispatcherTimer
        {
            Interval = TimeSpan.FromSeconds(5)
        };
        _logRefreshTimer.Tick += async (_, _) =>
        {
            if (IsLogsPanelVisible && IsLogsAutoRefresh && !_isLogRefreshRunning)
                await FetchLogsInternalAsync();
        };
        _logRefreshTimer.Start();
    }

    private void StopLogAutoRefresh()
    {
        _logRefreshTimer?.Stop();
        _logRefreshTimer = null;
    }

    [RelayCommand]
    private async Task DeleteInstanceAsync()
    {
        if (SelectedInstance == null) return;
        IsBusy = true;
        StatusMessage = $"Deleting {SelectedInstance.StackName}...";
        try
        {
            var dockerCli = _services.GetRequiredService<SshDockerCliService>();
            dockerCli.SetHost(SelectedInstance.Host);
            var dockerSecrets = _services.GetRequiredService<SshDockerSecretsService>();
            dockerSecrets.SetHost(SelectedInstance.Host);
            using var scope = _services.CreateScope();
            var instanceSvc = scope.ServiceProvider.GetRequiredService<InstanceService>();
            var result = await instanceSvc.DeleteInstanceAsync(
                SelectedInstance.StackName, SelectedInstance.CustomerAbbrev);
            StatusMessage = result.Success
                ? $"Instance '{SelectedInstance.StackName}' deleted."
                : $"Delete failed: {result.ErrorMessage}";
            await RefreshAllAsync();
        }
        catch (Exception ex) { StatusMessage = $"Error deleting: {ex.Message}"; }
        finally { IsBusy = false; }
    }

    [RelayCommand]
    private async Task RotateMySqlPasswordAsync()
    {
        if (SelectedInstance == null) return;
        IsBusy = true;
        StatusMessage = $"Rotating MySQL password for {SelectedInstance.StackName}...";
        try
        {
            var dockerCli = _services.GetRequiredService<SshDockerCliService>();
            dockerCli.SetHost(SelectedInstance.Host);
            var dockerSecrets = _services.GetRequiredService<SshDockerSecretsService>();
            dockerSecrets.SetHost(SelectedInstance.Host);
            using var scope = _services.CreateScope();
            var instanceSvc = scope.ServiceProvider.GetRequiredService<InstanceService>();
            var (ok, msg) = await instanceSvc.RotateMySqlPasswordAsync(SelectedInstance.StackName);
            StatusMessage = ok ? $"Done: {msg}" : $"Failed: {msg}";
            await RefreshAllAsync();
        }
        catch (Exception ex) { StatusMessage = $"Error rotating password: {ex.Message}"; }
        finally { IsBusy = false; }
    }

    [RelayCommand]
    private async Task OpenDetailsAsync()
    {
        if (SelectedInstance == null) return;

        IsBusy = true;
        StatusMessage = $"Loading details for '{SelectedInstance.StackName}'...";
        try
        {
            // Set the SSH host on singleton Docker services so modal operations target the right host
            var dockerCli = _services.GetRequiredService<SshDockerCliService>();
            dockerCli.SetHost(SelectedInstance.Host);
            var dockerSecrets = _services.GetRequiredService<SshDockerSecretsService>();
            dockerSecrets.SetHost(SelectedInstance.Host);

            var detailsVm = _services.GetRequiredService<InstanceDetailsViewModel>();
            await detailsVm.LoadAsync(SelectedInstance);

            OpenDetailsRequested?.Invoke(detailsVm);
            StatusMessage = string.Empty;
        }
        catch (Exception ex) { StatusMessage = $"Error opening details: {ex.Message}"; }
        finally { IsBusy = false; }
    }
}
