using System.Collections.ObjectModel;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
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.Services;

namespace OTSSignsOrchestrator.Desktop.ViewModels;

/// <summary>
/// ViewModel for the Create Instance form.
/// Simplified: Customer Name, Abbreviation, SSH Host, and optional Newt credentials.
/// All other config comes from the Settings page.
/// </summary>
public partial class CreateInstanceViewModel : ObservableObject
{
    private readonly IServiceProvider _services;
    private readonly MainWindowViewModel _mainVm;

    [ObservableProperty] private string _statusMessage = string.Empty;
    [ObservableProperty] private bool _isBusy;
    [ObservableProperty] private string _deployOutput = string.Empty;
    [ObservableProperty] private double _progressPercent;
    [ObservableProperty] private string _progressStep = string.Empty;

    // Core form fields — only these two are required from the user
    [ObservableProperty] private string _customerName = string.Empty;
    [ObservableProperty] private string _customerAbbrev = string.Empty;

    // Optional Pangolin/Newt credentials (per-instance)
    [ObservableProperty] private string _newtId = string.Empty;
    [ObservableProperty] private string _newtSecret = string.Empty;

    // NFS volume settings (per-instance, defaults loaded from global settings)
    [ObservableProperty] private string _nfsServer = string.Empty;
    [ObservableProperty] private string _nfsExport = string.Empty;
    [ObservableProperty] private string _nfsExportFolder = string.Empty;
    [ObservableProperty] private string _nfsExtraOptions = string.Empty;

    /// <summary>When enabled, existing Docker volumes for the stack are removed before deploying.</summary>
    [ObservableProperty] private bool _purgeStaleVolumes = false;

    // SSH host selection
    [ObservableProperty] private ObservableCollection<SshHost> _availableHosts = new();
    [ObservableProperty] private SshHost? _selectedSshHost;

    // YML preview
    [ObservableProperty] private string _previewYml = string.Empty;
    [ObservableProperty] private bool _isLoadingYml;

    public bool HasPreviewYml => !string.IsNullOrEmpty(PreviewYml);
    partial void OnPreviewYmlChanged(string value) => OnPropertyChanged(nameof(HasPreviewYml));

    // ── Derived preview properties ───────────────────────────────────────────

    public string PreviewStackName      => Valid ? $"{Abbrev}-cms-stack"      : "—";
    public string PreviewServiceWeb     => Valid ? $"{Abbrev}-web"            : "—";
    public string PreviewServiceCache   => Valid ? $"{Abbrev}-memcached"      : "—";
    public string PreviewServiceChart   => Valid ? $"{Abbrev}-quickchart"     : "—";
    public string PreviewServiceNewt    => Valid ? $"{Abbrev}-newt"           : "—";
    public string PreviewNetwork        => Valid ? $"{Abbrev}-net"            : "—";
    public string PreviewVolCustom      => Valid ? $"{Abbrev}/cms-custom"     : "—";
    public string PreviewVolBackup      => Valid ? $"{Abbrev}/cms-backup"     : "—";
    public string PreviewVolLibrary     => Valid ? $"{Abbrev}/cms-library"    : "—";
    public string PreviewVolUserscripts => Valid ? $"{Abbrev}/cms-userscripts": "—";
    public string PreviewVolCaCerts     => Valid ? $"{Abbrev}/cms-ca-certs"   : "—";
    public string PreviewSecret         => Valid ? $"{Abbrev}-cms-db-password": "—";
    public string PreviewSecretUser     => Valid ? $"{Abbrev}-cms-db-user"    : "—";
    public string PreviewSecretHost     => "global_mysql_host";
    public string PreviewSecretPort     => "global_mysql_port";
    public string PreviewMySqlDb        => Valid ? $"{Abbrev}_cms_db"         : "—";
    public string PreviewMySqlUser      => Valid ? $"{Abbrev}_cms_user"       : "—";
    public string PreviewCmsUrl         => Valid ? $"https://{Abbrev}.ots-signs.com" : "—";

    private string Abbrev => CustomerAbbrev.Trim().ToLowerInvariant();
    private bool Valid => Abbrev.Length == 3 && System.Text.RegularExpressions.Regex.IsMatch(Abbrev, "^[a-z]{3}$");

    // ─────────────────────────────────────────────────────────────────────────

    public CreateInstanceViewModel(IServiceProvider services, MainWindowViewModel mainVm)
    {
        _services = services;
        _mainVm   = mainVm;
        _ = LoadHostsAsync();
        _ = LoadNfsDefaultsAsync();
    }

    partial void OnCustomerAbbrevChanged(string value) => RefreshPreview();

    private void RefreshPreview()
    {
        OnPropertyChanged(nameof(PreviewStackName));
        OnPropertyChanged(nameof(PreviewServiceWeb));
        OnPropertyChanged(nameof(PreviewServiceCache));
        OnPropertyChanged(nameof(PreviewServiceChart));
        OnPropertyChanged(nameof(PreviewServiceNewt));
        OnPropertyChanged(nameof(PreviewNetwork));
        OnPropertyChanged(nameof(PreviewVolCustom));
        OnPropertyChanged(nameof(PreviewVolBackup));
        OnPropertyChanged(nameof(PreviewVolLibrary));
        OnPropertyChanged(nameof(PreviewVolUserscripts));
        OnPropertyChanged(nameof(PreviewVolCaCerts));
        OnPropertyChanged(nameof(PreviewSecret));
        OnPropertyChanged(nameof(PreviewSecretUser));
        OnPropertyChanged(nameof(PreviewSecretHost));
        OnPropertyChanged(nameof(PreviewSecretPort));
        OnPropertyChanged(nameof(PreviewMySqlDb));
        OnPropertyChanged(nameof(PreviewMySqlUser));
        OnPropertyChanged(nameof(PreviewCmsUrl));
    }

    private async Task LoadHostsAsync()
    {
        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);
    }

    private async Task LoadNfsDefaultsAsync()
    {
        using var scope = _services.CreateScope();
        var settings = scope.ServiceProvider.GetRequiredService<SettingsService>();
        NfsServer = await settings.GetAsync(SettingsService.NfsServer) ?? string.Empty;
        NfsExport = await settings.GetAsync(SettingsService.NfsExport) ?? string.Empty;
        NfsExportFolder = await settings.GetAsync(SettingsService.NfsExportFolder) ?? string.Empty;
        NfsExtraOptions = await settings.GetAsync(SettingsService.NfsOptions) ?? string.Empty;
    }

    [RelayCommand]
    private async Task LoadYmlPreviewAsync()
    {
        if (!Valid)
        {
            PreviewYml = "# Abbreviation must be exactly 3 lowercase letters (a-z) before loading the YML preview.";
            return;
        }

        IsLoadingYml = true;
        try
        {
            using var scope = _services.CreateScope();
            var settings = scope.ServiceProvider.GetRequiredService<SettingsService>();
            var git      = scope.ServiceProvider.GetRequiredService<GitTemplateService>();
            var composer = scope.ServiceProvider.GetRequiredService<ComposeRenderService>();

            var repoUrl = await settings.GetAsync(SettingsService.GitRepoUrl);
            var repoPat = await settings.GetAsync(SettingsService.GitRepoPat);
            if (string.IsNullOrWhiteSpace(repoUrl))
            {
                PreviewYml = "# Git template repository URL is not configured. Set it in Settings → Git Repo URL.";
                return;
            }

            var templateConfig = await git.FetchAsync(repoUrl, repoPat);

            var abbrev    = Abbrev;
            var stackName = $"{abbrev}-cms-stack";

            var mySqlHost = await settings.GetAsync(SettingsService.MySqlHost, "localhost");
            var mySqlPort = await settings.GetAsync(SettingsService.MySqlPort, "3306");
            var mySqlDbName = (await settings.GetAsync(SettingsService.DefaultMySqlDbTemplate, "{abbrev}_cms_db")).Replace("{abbrev}", abbrev);
            var mySqlUser   = (await settings.GetAsync(SettingsService.DefaultMySqlUserTemplate, "{abbrev}_cms_user")).Replace("{abbrev}", abbrev);

            var cmsServerName = (await settings.GetAsync(SettingsService.DefaultCmsServerNameTemplate, "app.ots-signs.com")).Replace("{abbrev}", abbrev);
            var themePath     = (await settings.GetAsync(SettingsService.DefaultThemeHostPath, "/cms/ots-theme")).Replace("{abbrev}", abbrev);

            var smtpServer          = await settings.GetAsync(SettingsService.SmtpServer, string.Empty);
            var smtpUsername        = await settings.GetAsync(SettingsService.SmtpUsername, string.Empty);
            var smtpPassword        = await settings.GetAsync(SettingsService.SmtpPassword, string.Empty);
            var smtpUseTls          = await settings.GetAsync(SettingsService.SmtpUseTls, "YES");
            var smtpUseStartTls     = await settings.GetAsync(SettingsService.SmtpUseStartTls, "YES");
            var smtpRewriteDomain   = await settings.GetAsync(SettingsService.SmtpRewriteDomain, string.Empty);
            var smtpHostname        = await settings.GetAsync(SettingsService.SmtpHostname, string.Empty);
            var smtpFromLineOverride = await settings.GetAsync(SettingsService.SmtpFromLineOverride, "NO");

            var pangolinEndpoint = await settings.GetAsync(SettingsService.PangolinEndpoint, "https://app.pangolin.net");

            var cmsImage        = await settings.GetAsync(SettingsService.DefaultCmsImage, "ghcr.io/xibosignage/xibo-cms:release-4.2.3");
            var newtImage       = await settings.GetAsync(SettingsService.DefaultNewtImage, "fosrl/newt");
            var memcachedImage  = await settings.GetAsync(SettingsService.DefaultMemcachedImage, "memcached:alpine");
            var quickChartImage = await settings.GetAsync(SettingsService.DefaultQuickChartImage, "ianw/quickchart");

            var phpPostMaxSize       = await settings.GetAsync(SettingsService.DefaultPhpPostMaxSize, "10G");
            var phpUploadMaxFilesize = await settings.GetAsync(SettingsService.DefaultPhpUploadMaxFilesize, "10G");
            var phpMaxExecutionTime  = await settings.GetAsync(SettingsService.DefaultPhpMaxExecutionTime, "600");

            // Use form values; fall back to saved global settings
            var nfsServer       = string.IsNullOrWhiteSpace(NfsServer)       ? await settings.GetAsync(SettingsService.NfsServer)       : NfsServer;
            var nfsExport       = string.IsNullOrWhiteSpace(NfsExport)       ? await settings.GetAsync(SettingsService.NfsExport)       : NfsExport;
            var nfsExportFolder = string.IsNullOrWhiteSpace(NfsExportFolder) ? await settings.GetAsync(SettingsService.NfsExportFolder) : NfsExportFolder;
            var nfsOptions      = string.IsNullOrWhiteSpace(NfsExtraOptions) ? await settings.GetAsync(SettingsService.NfsOptions, string.Empty) : NfsExtraOptions;

            var ctx = new RenderContext
            {
                CustomerName         = CustomerName.Trim(),
                CustomerAbbrev       = abbrev,
                StackName            = stackName,
                CmsServerName        = cmsServerName,
                HostHttpPort         = 80,
                CmsImage             = cmsImage,
                MemcachedImage       = memcachedImage,
                QuickChartImage      = quickChartImage,
                NewtImage            = newtImage,
                ThemeHostPath        = themePath,
                MySqlHost            = mySqlHost,
                MySqlPort            = mySqlPort,
                MySqlDatabase        = mySqlDbName,
                MySqlUser            = mySqlUser,
                SmtpServer           = smtpServer,
                SmtpUsername         = smtpUsername,
                SmtpPassword         = smtpPassword,
                SmtpUseTls           = smtpUseTls,
                SmtpUseStartTls      = smtpUseStartTls,
                SmtpRewriteDomain    = smtpRewriteDomain,
                SmtpHostname         = smtpHostname,
                SmtpFromLineOverride = smtpFromLineOverride,
                PhpPostMaxSize       = phpPostMaxSize,
                PhpUploadMaxFilesize = phpUploadMaxFilesize,
                PhpMaxExecutionTime  = phpMaxExecutionTime,
                PangolinEndpoint     = pangolinEndpoint,
                NewtId               = string.IsNullOrWhiteSpace(NewtId)     ? null : NewtId.Trim(),
                NewtSecret           = string.IsNullOrWhiteSpace(NewtSecret) ? null : NewtSecret.Trim(),
                NfsServer           = nfsServer,
                NfsExport           = nfsExport,
                NfsExportFolder     = nfsExportFolder,
                NfsExtraOptions     = nfsOptions,
            };

            PreviewYml = composer.Render(templateConfig.Yaml, ctx);
        }
        catch (Exception ex)
        {
            PreviewYml = $"# Error rendering YML preview:\n# {ex.Message}";
        }
        finally
        {
            IsLoadingYml = false;
        }
    }

    [RelayCommand]
    private async Task CopyYmlAsync()
    {
        if (string.IsNullOrEmpty(PreviewYml)) return;
        var mainWindow = (Application.Current?.ApplicationLifetime
            as IClassicDesktopStyleApplicationLifetime)?.MainWindow;
        if (mainWindow is null) return;
        var clipboard = TopLevel.GetTopLevel(mainWindow)?.Clipboard;
        if (clipboard is not null)
            await clipboard.SetTextAsync(PreviewYml);
    }

    [RelayCommand]
    private async Task DeployAsync()
    {
        // ── Validation ───────────────────────────────────────────────────
        if (SelectedSshHost == null)
        {
            StatusMessage = "Select an SSH host first.";
            return;
        }
        if (string.IsNullOrWhiteSpace(CustomerName))
        {
            StatusMessage = "Customer Name is required.";
            return;
        }
        if (!Valid)
        {
            StatusMessage = "Abbreviation must be exactly 3 lowercase letters (a-z).";
            return;
        }

        IsBusy = true;
        StatusMessage = "Starting deployment...";
        DeployOutput = string.Empty;
        ProgressPercent = 0;

        try
        {
            // Wire SSH host into docker services (singletons must know the target host before
            // InstanceService uses them internally for secrets and CLI operations)
            var dockerCli = _services.GetRequiredService<SshDockerCliService>();
            dockerCli.SetHost(SelectedSshHost);
            var dockerSecrets = _services.GetRequiredService<SshDockerSecretsService>();
            dockerSecrets.SetHost(SelectedSshHost);

            using var scope = _services.CreateScope();
            var instanceSvc = scope.ServiceProvider.GetRequiredService<InstanceService>();

            // InstanceService.CreateInstanceAsync handles the full provisioning flow:
            //   1. Clone template repo
            //   2. Generate MySQL password → create Docker Swarm secret
            //   3. Create MySQL database + SQL user (same password as the secret)
            //   4. Render compose YAML → deploy stack
            SetProgress(30, "Provisioning instance (MySQL user, secrets, stack)...");

            var dto = new CreateInstanceDto
            {
                CustomerName   = CustomerName.Trim(),
                CustomerAbbrev = Abbrev,
                SshHostId      = SelectedSshHost.Id,
                NewtId         = string.IsNullOrWhiteSpace(NewtId) ? null : NewtId.Trim(),
                NewtSecret     = string.IsNullOrWhiteSpace(NewtSecret) ? null : NewtSecret.Trim(),
                NfsServer        = string.IsNullOrWhiteSpace(NfsServer) ? null : NfsServer.Trim(),
                NfsExport        = string.IsNullOrWhiteSpace(NfsExport) ? null : NfsExport.Trim(),
                NfsExportFolder  = string.IsNullOrWhiteSpace(NfsExportFolder) ? null : NfsExportFolder.Trim(),
                NfsExtraOptions  = string.IsNullOrWhiteSpace(NfsExtraOptions) ? null : NfsExtraOptions.Trim(),
                PurgeStaleVolumes = PurgeStaleVolumes,
            };

            var result = await instanceSvc.CreateInstanceAsync(dto);

            AppendOutput(result.Output ?? string.Empty);

            if (result.Success)
            {
                SetProgress(100, "Stack deployed successfully.");
                StatusMessage = $"Instance '{Abbrev}-cms-stack' deployed in {result.DurationMs}ms. " +
                                "Open the details pane on the Instances page to complete setup.";
                _mainVm.NavigateToInstancesWithSelection(Abbrev);
            }
            else
            {
                SetProgress(0, "Deployment failed.");
                StatusMessage = $"Deploy failed: {result.ErrorMessage}";
            }
        }
        catch (Exception ex)
        {
            StatusMessage = $"Error: {ex.Message}";
            AppendOutput(ex.ToString());
            SetProgress(0, "Failed.");
        }
        finally
        {
            IsBusy = false;
        }
    }

    private void SetProgress(double pct, string step)
    {
        ProgressPercent = pct;
        ProgressStep = step;
        AppendOutput($"[{pct:0}%] {step}");
    }

    private void AppendOutput(string text)
    {
        if (!string.IsNullOrWhiteSpace(text))
            DeployOutput += (DeployOutput.Length > 0 ? "\n" : "") + text;
    }

}

