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

namespace OpenNest.Data;

public class LocalJsonProvider : IDataProvider
{
    private readonly string _directory;

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

    public LocalJsonProvider(string directory)
    {
        _directory = directory;
        Directory.CreateDirectory(_directory);
    }

    public IReadOnlyList<MachineSummary> GetMachines()
    {
        var summaries = new List<MachineSummary>();
        foreach (var file in Directory.GetFiles(_directory, "*.json"))
        {
            var machine = ReadFile(file);
            if (machine is not null)
                summaries.Add(new MachineSummary(machine.Id, machine.Name));
        }
        return summaries;
    }

    public MachineConfig? GetMachine(Guid id)
    {
        var path = GetPath(id);
        return File.Exists(path) ? ReadFile(path) : null;
    }

    public void SaveMachine(MachineConfig machine)
    {
        var json = JsonSerializer.Serialize(machine, JsonOptions);
        var path = GetPath(machine.Id);
        WriteWithRetry(path, json);
    }

    public void DeleteMachine(Guid id)
    {
        var path = GetPath(id);
        if (File.Exists(path))
            File.Delete(path);
    }

    private string GetPath(Guid id) => Path.Combine(_directory, $"{id}.json");

    private static MachineConfig? ReadFile(string path)
    {
        try
        {
            var json = File.ReadAllText(path);
            return JsonSerializer.Deserialize<MachineConfig>(json, JsonOptions);
        }
        catch (JsonException)
        {
            return null;
        }
        catch (IOException)
        {
            return null;
        }
    }

    public void EnsureDefaults()
    {
        if (Directory.GetFiles(_directory, "*.json").Length > 0)
            return;

        var assembly = typeof(LocalJsonProvider).Assembly;
        var resourceName = assembly.GetManifestResourceNames()
            .FirstOrDefault(n => n.EndsWith("CL-980.json"));

        if (resourceName is null) return;

        using var stream = assembly.GetManifestResourceStream(resourceName);
        if (stream is null) return;

        using var reader = new StreamReader(stream);
        var json = reader.ReadToEnd();

        var config = JsonSerializer.Deserialize<MachineConfig>(json, JsonOptions);
        if (config is null) return;

        SaveMachine(config);
    }

    private static void WriteWithRetry(string path, string json, int maxRetries = 3)
    {
        for (var attempt = 0; attempt < maxRetries; attempt++)
        {
            try
            {
                File.WriteAllText(path, json);
                return;
            }
            catch (IOException) when (attempt < maxRetries - 1)
            {
                Thread.Sleep(100);
            }
        }
    }
}
