using MySqlConnector;
using OTSSignsOrchestrator.Core.Models.DTOs;

namespace OTSSignsOrchestrator.Core.Services;

// Re-export for convenience so consumers only need one using
using ServiceLogEntry = OTSSignsOrchestrator.Core.Models.DTOs.ServiceLogEntry;

/// <summary>
/// Abstraction for Docker CLI stack operations (deploy, remove, list, inspect).
/// Implementations may use local docker CLI or SSH-based remote execution.
/// </summary>
public interface IDockerCliService
{
    Task<DeploymentResultDto> DeployStackAsync(string stackName, string composeYaml, bool resolveImage = false);
    Task<DeploymentResultDto> RemoveStackAsync(string stackName);
    Task<List<StackInfo>> ListStacksAsync();
    Task<List<ServiceInfo>> InspectStackServicesAsync(string stackName);

    /// <summary>Ensures a directory exists on the target host (equivalent to mkdir -p).</summary>
    Task<bool> EnsureDirectoryAsync(string path);

    /// <summary>
    /// Ensures the required folders exist on an NFS export, creating any that are missing.
    /// If <paramref name="nfsExportFolder"/> is non-empty, creates it first as a subfolder of the export,
    /// then creates the volume folders inside it.
    /// Temporarily mounts the NFS export on the Docker host to create the directories.
    /// </summary>
    Task<bool> EnsureNfsFoldersAsync(
        string nfsServer,
        string nfsExport,
        IEnumerable<string> folderNames,
        string? nfsExportFolder = null);

    /// <summary>
    /// Same as <see cref="EnsureNfsFoldersAsync"/> but returns the error message on failure
    /// so callers can surface actionable diagnostics.
    /// </summary>
    Task<(bool Success, string? Error)> EnsureNfsFoldersWithErrorAsync(
        string nfsServer,
        string nfsExport,
        IEnumerable<string> folderNames,
        string? nfsExportFolder = null);

    /// <summary>
    /// Removes all Docker volumes whose names start with <paramref name="stackName"/>_.
    /// Volumes currently in use by running containers will be skipped.
    /// Safe for NFS volumes since data lives on the remote export, not in the local volume.
    /// </summary>
    Task<bool> RemoveStackVolumesAsync(string stackName);

    /// <summary>
    /// Lists all nodes in the Docker Swarm cluster.
    /// Must be executed against a Swarm manager node.
    /// </summary>
    Task<List<NodeInfo>> ListNodesAsync();

    /// <summary>
    /// Force-updates a service so all its tasks are restarted and pick up any changed
    /// secrets or config (equivalent to docker service update --force).
    /// </summary>
    Task<bool> ForceUpdateServiceAsync(string serviceName);

    /// <summary>
    /// Opens a <see cref="MySqlConnection"/> to a remote MySQL server through the
    /// implementation's transport (e.g. an SSH tunnel).  The caller must dispose
    /// both the connection <b>and</b> the returned <c>tunnel</c> handle when finished.
    /// </summary>
    /// <returns>
    /// A tuple of (connection, tunnel).  <c>tunnel</c> is <see cref="IDisposable"/>
    /// and MUST be disposed after the connection is closed.
    /// </returns>
    Task<(MySqlConnection Connection, IDisposable Tunnel)> OpenMySqlConnectionAsync(
        string mysqlHost, int port,
        string adminUser, string adminPassword);

    /// <summary>
    /// Executes <c>ALTER USER … IDENTIFIED BY …</c> on a remote MySQL server via
    /// <see cref="OpenMySqlConnectionAsync"/>.
    /// </summary>
    Task<(bool Success, string Error)> AlterMySqlUserPasswordAsync(
        string mysqlHost, int port,
        string adminUser, string adminPassword,
        string targetUser, string newPassword);

    /// <summary>
    /// Atomically swaps one secret reference on a running service:
    /// removes <paramref name="oldSecretName"/> and adds <paramref name="newSecretName"/>,
    /// preserving the in-container path as <paramref name="targetAlias"/> (defaults to
    /// <paramref name="oldSecretName"/> when null, keeping the same /run/secrets/ filename).
    /// </summary>
    Task<bool> ServiceSwapSecretAsync(string serviceName, string oldSecretName, string newSecretName, string? targetAlias = null);

    /// <summary>
    /// Fetches the last <paramref name="tailLines"/> log lines from a Docker Swarm service.
    /// If <paramref name="serviceName"/> is null, fetches logs from all services in the stack.
    /// Returns parsed log entries sorted by timestamp ascending.
    /// </summary>
    Task<List<ServiceLogEntry>> GetServiceLogsAsync(string stackName, string? serviceName = null, int tailLines = 200);

    /// <summary>
    /// Writes a file to an NFS volume by temporarily mounting the export on the Docker host.
    /// Used to deploy configuration files (e.g. settings-custom.php) into CMS containers.
    /// </summary>
    /// <param name="nfsServer">NFS server hostname or IP.</param>
    /// <param name="nfsExport">NFS export path (e.g. "/srv/nfs").</param>
    /// <param name="relativePath">Path relative to the export root (e.g. "subfolder/abbrev/cms-custom/settings-custom.php").</param>
    /// <param name="content">File content to write.</param>
    /// <param name="nfsExportFolder">Optional subfolder within the export.</param>
    Task<(bool Success, string? Error)> WriteFileToNfsAsync(
        string nfsServer,
        string nfsExport,
        string relativePath,
        string content,
        string? nfsExportFolder = null);
}

public class StackInfo
{
    public string Name { get; set; } = string.Empty;
    public int ServiceCount { get; set; }
}

public class ServiceInfo
{
    public string Name { get; set; } = string.Empty;
    public string Image { get; set; } = string.Empty;
    public string Replicas { get; set; } = string.Empty;
}
