using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Knot.Shared.Infrastructure.Persistence;
using Knot.Shared.Infrastructure.Persistence.Entities;
using Knot.Shared.Kernel.Security;
using Knot.Contracts.Settings.Application.Abstractions;
using Knot.Contracts.Settings.Application.DTOs;

namespace Knot.Modules.Settings.Infrastructure.Configuration;

public class SettingsService : ISettingsService, 
    ISystemSettings, 
    IStoriesSettings, 
    IChatsSettings, 
    IMessagesSettings, 
    IWebRtcSettings, 
    IKlipySettings, 
    IImportSettings, 
    IFederationSettings
{
    private readonly IServiceProvider _serviceProvider;
    private readonly IEncryptionService _encryptionService;
    private SystemSettingsDto _current;

    public SettingsService(IServiceProvider serviceProvider, IEncryptionService encryptionService)
    {
        _serviceProvider = serviceProvider;
        _encryptionService = encryptionService;
        _current = new SystemSettingsDto();
    }

    public SystemSettingsDto Current => _current;

    SystemConfig ISystemSettings.Current => _current.System;
    StoriesConfig IStoriesSettings.Current => _current.Stories;
    ChatsConfig IChatsSettings.Current => _current.Chats;
    MessagesConfig IMessagesSettings.Current => _current.Messages;
    WebRtcConfig IWebRtcSettings.Current => _current.WebRtc;
    KlipyConfig IKlipySettings.Current => _current.Klipy;
    ImportConfig IImportSettings.Current => _current.Import;
    FederationConfig IFederationSettings.Current => _current.Federation;

    public async Task<SystemSettingsDto> GetSettingsAsync(CancellationToken cancellationToken = default)
    {
        using var scope = _serviceProvider.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<SystemDbContext>();

        var setting = await db.Settings.FirstOrDefaultAsync(s => s.Key == "Global", cancellationToken);
        if (setting == null)
        {
            return new SystemSettingsDto();
        }

        try
        {
            var json = _encryptionService.DecryptMessage(setting.EncryptedValue);
            var dto = JsonSerializer.Deserialize<SystemSettingsDto>(json);
            if (dto != null)
            {
                _current = dto;
                return dto;
            }
        }
        catch
        {
            // Fail safe
        }

        return new SystemSettingsDto();
    }

    public async Task UpdateSettingsAsync(SystemSettingsDto settings, CancellationToken cancellationToken = default)
    {
        using var scope = _serviceProvider.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<SystemDbContext>();

        var json = JsonSerializer.Serialize(settings);
        var encrypted = _encryptionService.EncryptMessage(json);

        var setting = await db.Settings.FirstOrDefaultAsync(s => s.Key == "Global", cancellationToken);
        if (setting == null)
        {
            db.Settings.Add(new SystemSetting { Key = "Global", EncryptedValue = encrypted });
        }
        else
        {
            setting.EncryptedValue = encrypted;
        }

        await db.SaveChangesAsync(cancellationToken);
        _current = settings;
    }

    public void Initialize()
    {
        _current = GetSettingsAsync().GetAwaiter().GetResult();
    }
}
