using System.Text.Json;
using System.Text.Json.Serialization;

namespace ZB.MOM.WW.Secrets.Cli.Interactive;

/// <summary>
/// Persists the operator's recently-used store targets (names + appsettings paths ONLY — never
/// key material or secret values) so the interactive console can offer quick re-selection.
/// </summary>
public sealed class RecentTargetsStore
{
    private const int MaxEntries = 10;

    private static readonly JsonSerializerOptions SerializerOptions = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        WriteIndented = true,
    };

    private readonly string _filePath;
    private readonly TimeProvider _timeProvider;

    /// <summary>Creates the store bound to a JSON file path, using an optional injected clock.</summary>
    /// <param name="filePath">The JSON file to load from and save to.</param>
    /// <param name="timeProvider">
    /// Clock used to stamp <see cref="RecentTarget.LastUsedUtc"/>. Defaults to
    /// <see cref="TimeProvider.System"/> when omitted.
    /// </param>
    public RecentTargetsStore(string filePath, TimeProvider? timeProvider = null)
    {
        _filePath = filePath;
        _timeProvider = timeProvider ?? TimeProvider.System;
    }

    /// <summary>One remembered target: a display name, its appsettings.json path, and when it was last used.</summary>
    /// <param name="Name">Display name for the target (e.g. the app name).</param>
    /// <param name="AppSettingsPath">Filesystem path to the target's appsettings.json.</param>
    /// <param name="LastUsedUtc">Timestamp of the most recent selection of this target.</param>
    public sealed record RecentTarget(string Name, string AppSettingsPath, DateTimeOffset LastUsedUtc);

    /// <summary>Loads the recents list, newest first. A missing or unreadable/corrupt file yields an empty list.</summary>
    public IReadOnlyList<RecentTarget> Load()
    {
        if (!File.Exists(_filePath))
        {
            return [];
        }

        try
        {
            var json = File.ReadAllText(_filePath);
            var entries = JsonSerializer.Deserialize<List<RecentTarget>>(json, SerializerOptions);
            return entries ?? [];
        }
        catch (JsonException)
        {
            return [];
        }
        catch (IOException)
        {
            return [];
        }
    }

    /// <summary>Upserts a target by path, moves it to the front, stamps now, caps the list at 10, and saves.</summary>
    /// <param name="name">Display name for the target.</param>
    /// <param name="appSettingsPath">Filesystem path to the target's appsettings.json — the dedup key.</param>
    public void Touch(string name, string appSettingsPath)
    {
        var existing = Load();
        var remaining = existing.Where(e => !string.Equals(e.AppSettingsPath, appSettingsPath, StringComparison.Ordinal));
        var touched = new RecentTarget(name, appSettingsPath, _timeProvider.GetUtcNow());
        var updated = new List<RecentTarget> { touched };
        updated.AddRange(remaining.Take(MaxEntries - 1));

        Save(updated);
    }

    private void Save(IReadOnlyList<RecentTarget> entries)
    {
        var directory = Path.GetDirectoryName(_filePath);
        if (!string.IsNullOrEmpty(directory))
        {
            Directory.CreateDirectory(directory);
        }

        var json = JsonSerializer.Serialize(entries, SerializerOptions);
        var tmpPath = _filePath + ".tmp";
        File.WriteAllText(tmpPath, json);
        File.Move(tmpPath, _filePath, overwrite: true);
    }
}
