using System.Text.Json;
using Microsoft.Extensions.Logging;
using PSLauncher.Core.Configuration;
using PSLauncher.Models;

namespace PSLauncher.Core.Downloads;

public interface IDownloadStateStore
{
    string GetDownloadsDirectory();
    string GetPartialPath(string version);
    string GetStatePath(string version);
    DownloadState? Load(string version);
    void Save(DownloadState state);
    void Discard(string version);
    IReadOnlyList<DownloadState> ScanResumable();
}

public sealed class DownloadStateStore : IDownloadStateStore
{
    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        WriteIndented = true,
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        PropertyNameCaseInsensitive = true,
    };

    private readonly IConfigStore _configStore;
    private readonly ILogger<DownloadStateStore> _logger;

    public DownloadStateStore(IConfigStore configStore, ILogger<DownloadStateStore> logger)
    {
        _configStore = configStore;
        _logger = logger;
    }

    public string GetDownloadsDirectory()
    {
        var dir = Path.Combine(_configStore.ConfigDirectory, "downloads");
        Directory.CreateDirectory(dir);
        return dir;
    }

    public string GetPartialPath(string version) =>
        Path.Combine(GetDownloadsDirectory(), $"proserve-{version}.zip.partial");

    public string GetStatePath(string version) =>
        Path.Combine(GetDownloadsDirectory(), $"proserve-{version}.state.json");

    public DownloadState? Load(string version)
    {
        var path = GetStatePath(version);
        if (!File.Exists(path)) return null;
        try
        {
            using var fs = File.OpenRead(path);
            return JsonSerializer.Deserialize<DownloadState>(fs, JsonOptions);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Failed to load state file {Path}", path);
            return null;
        }
    }

    public void Save(DownloadState state)
    {
        var path = GetStatePath(state.Version);
        var tmp = path + ".tmp";
        try
        {
            using (var fs = File.Create(tmp))
            {
                JsonSerializer.Serialize(fs, state, JsonOptions);
            }
            File.Move(tmp, path, overwrite: true);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Failed to persist state for {Version}", state.Version);
        }
    }

    public void Discard(string version)
    {
        TryDelete(GetStatePath(version));
        TryDelete(GetPartialPath(version));
    }

    public IReadOnlyList<DownloadState> ScanResumable()
    {
        var dir = GetDownloadsDirectory();
        var states = new List<DownloadState>();
        foreach (var f in Directory.EnumerateFiles(dir, "*.state.json"))
        {
            try
            {
                using var fs = File.OpenRead(f);
                var s = JsonSerializer.Deserialize<DownloadState>(fs, JsonOptions);
                if (s is not null && File.Exists(s.PartialPath)) states.Add(s);
            }
            catch (Exception ex) { _logger.LogDebug(ex, "Skip {File}", f); }
        }
        return states;
    }

    private static void TryDelete(string path)
    {
        try { if (File.Exists(path)) File.Delete(path); } catch { /* best effort */ }
    }
}
