using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
using PSLauncher.Models;

namespace PSLauncher.Core.Installations;

public sealed partial class InstallationRegistry : IInstallationRegistry
{
    private const string ExecutableName = "PROSERVE_UE_5_5.exe";

    // IgnoreCase pour matcher à la fois les anciens dossiers "Proserve vX.Y.Z" et les
    // nouveaux "PROSERVE vX.Y.Z" (changement de casse de la marque).
    [GeneratedRegex(@"^PROSERVE v(?<v>\d+\.\d+\.\d+)$", RegexOptions.IgnoreCase)]
    private static partial Regex VersionFolderRegex();

    private readonly ILogger<InstallationRegistry> _logger;
    private readonly Func<string> _installRootProvider;

    public InstallationRegistry(ILogger<InstallationRegistry> logger, Func<string> installRootProvider)
    {
        _logger = logger;
        _installRootProvider = installRootProvider;
    }

    public IReadOnlyList<InstalledVersion> Scan()
    {
        var root = _installRootProvider();
        if (string.IsNullOrWhiteSpace(root) || !Directory.Exists(root))
        {
            _logger.LogWarning("Install root does not exist: {Root}", root);
            return Array.Empty<InstalledVersion>();
        }

        var results = new List<InstalledVersion>();
        foreach (var dir in Directory.EnumerateDirectories(root))
        {
            var name = Path.GetFileName(dir);
            var match = VersionFolderRegex().Match(name);
            if (!match.Success) continue;

            var exe = Path.Combine(dir, ExecutableName);
            if (!File.Exists(exe))
            {
                _logger.LogDebug("Skipped {Dir}: missing {Exe}", dir, ExecutableName);
                continue;
            }

            var info = new DirectoryInfo(dir);
            results.Add(new InstalledVersion(
                Version: match.Groups["v"].Value,
                FolderPath: dir,
                ExecutablePath: exe,
                InstalledAt: info.CreationTimeUtc,
                SizeBytes: SafeDirectorySize(dir)));
        }

        return results
            .OrderByDescending(v => SemVer.Parse(v.Version))
            .ToList();
    }

    public InstalledVersion? Get(string version) =>
        Scan().FirstOrDefault(v => v.Version == version);

    public bool IsInstalled(string version) => Get(version) is not null;

    public Task DeleteAsync(string version, IProgress<double>? progress, CancellationToken ct)
    {
        var existing = Get(version) ?? throw new InvalidOperationException($"Version {version} not installed");
        return Task.Run(() =>
        {
            var trash = Path.Combine(_installRootProvider(), ".trash",
                $"{Path.GetFileName(existing.FolderPath)}-{DateTime.UtcNow:yyyyMMddHHmmss}");
            Directory.CreateDirectory(Path.GetDirectoryName(trash)!);
            Directory.Move(existing.FolderPath, trash);
            try
            {
                Directory.Delete(trash, recursive: true);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Failed to delete trash {Path}", trash);
            }
            progress?.Report(1.0);
        }, ct);
    }

    private long SafeDirectorySize(string dir)
    {
        try
        {
            return new DirectoryInfo(dir)
                .EnumerateFiles("*", SearchOption.AllDirectories)
                .Sum(f => f.Length);
        }
        catch
        {
            return 0;
        }
    }
}
