using System.IO;
using System.Text;
using System.Text.Json;
using SharepointToolbox.Core.Models;

namespace SharepointToolbox.Infrastructure.Persistence;

public class ProfileRepository
{
    private readonly string _filePath;
    private readonly SemaphoreSlim _writeLock = new(1, 1);

    public ProfileRepository(string filePath)
    {
        _filePath = filePath;
    }

    public async Task<IReadOnlyList<TenantProfile>> LoadAsync()
    {
        if (!File.Exists(_filePath))
            return Array.Empty<TenantProfile>();

        string json;
        try
        {
            json = await File.ReadAllTextAsync(_filePath, Encoding.UTF8);
        }
        catch (IOException ex)
        {
            throw new InvalidDataException($"Failed to read profiles file: {_filePath}", ex);
        }

        ProfilesRoot? root;
        try
        {
            root = JsonSerializer.Deserialize<ProfilesRoot>(json,
                new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
        }
        catch (JsonException ex)
        {
            throw new InvalidDataException($"Profiles file contains invalid JSON: {_filePath}", ex);
        }

        if (root?.Profiles is null)
            return Array.Empty<TenantProfile>();
        return root.Profiles;
    }

    public async Task SaveAsync(IReadOnlyList<TenantProfile> profiles)
    {
        await _writeLock.WaitAsync();
        try
        {
            var root = new ProfilesRoot { Profiles = profiles.ToList() };
            var json = JsonSerializer.Serialize(root,
                new JsonSerializerOptions
                {
                    WriteIndented = true,
                    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
                });

            var tmpPath = _filePath + ".tmp";
            var dir = Path.GetDirectoryName(_filePath);
            if (!string.IsNullOrEmpty(dir))
                Directory.CreateDirectory(dir);

            await File.WriteAllTextAsync(tmpPath, json, Encoding.UTF8);

            // Validate round-trip before replacing
            JsonDocument.Parse(await File.ReadAllTextAsync(tmpPath, Encoding.UTF8)).Dispose();

            File.Move(tmpPath, _filePath, overwrite: true);
        }
        finally
        {
            _writeLock.Release();
        }
    }

    private sealed class ProfilesRoot
    {
        public List<TenantProfile> Profiles { get; set; } = new();
    }
}
