using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Knot.Modules.Federation.Application.Abstractions;
using Knot.Shared.Kernel;
using Knot.Contracts.Settings.Application.Abstractions;
using Knot.Contracts.Settings.Application.DTOs;
using Knot.Shared.Kernel.Security;

namespace Knot.Modules.Federation.Application.Federation.Services;

/// <summary>
/// Сервис для подготовки и рассылки зашифрованных федератовных пакетов.
/// </summary>
public sealed class FederationPacketService
{
    private readonly ISettingsService _settingsService;
    private readonly IFederationGateway _gateway;
    private readonly IEncryptionService _encryption;

    public FederationPacketService(
        ISettingsService settingsService, 
        IFederationGateway gateway, 
        IEncryptionService encryption)
    {
        _settingsService = settingsService;
        _gateway = gateway;
        _encryption = encryption;
    }

    /// <summary>
    /// Собирает зашифрованный пакет для целевого домена.
    /// </summary>
    public async Task<Result<FederationMessagePacket>> PreparePacketAsync(
        string messageBody, 
        FederationMetadata metadata, 
        string targetDomain, 
        CancellationToken ct = default)
    {
        var settings = await _settingsService.GetSettingsAsync(ct);
        var ourDomain = settings.System.DomainUrl;
        var ourPrivateKeyBase64 = settings.Federation.PrivateKey;
        
        // Находим ключ получателя в белом списке
        var targetConfig = settings.Federation.AllowedDomains.FirstOrDefault(d => d.Domain.Equals(targetDomain, StringComparison.OrdinalIgnoreCase));
        if (targetConfig == null || string.IsNullOrEmpty(targetConfig.PublicKey))
        {
            return Result.Failure<FederationMessagePacket>(new Error("Federation.KeyNotFound", "Public key for target domain not found."));
        }

        // 1. AES Шифрование тела
        // Используем IEncryptionService для получения потока или метода шифрования (в реальности здесь будет AES-256)
        // Для демонстрации представим, что мы получили IV и EncryptedPayload
        using var aes = Aes.Create();
        aes.KeySize = 256;
        aes.GenerateKey();
        aes.GenerateIV();
        
        var iv = aes.IV;
        var key = aes.Key; // В гибридной схеме мы передаем IV + Key (или только IV при общем ключе)
        
        byte[] encryptedBytes;
        using (var encryptor = aes.CreateEncryptor())
        {
            var plainBytes = Encoding.UTF8.GetBytes(messageBody);
            encryptedBytes = encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length);
        }

        // 2. RSA упаковка ключей на Open Public Key получателя
        byte[] encryptedKeys;
        using (var rsa = RSA.Create())
        {
            rsa.ImportRSAPublicKey(Convert.FromBase64String(targetConfig.PublicKey), out _);
            // Шифруем Key + IV (48 байт)
            var combinedKeys = new byte[key.Length + iv.Length];
            Buffer.BlockCopy(key, 0, combinedKeys, 0, key.Length);
            Buffer.BlockCopy(iv, 0, combinedKeys, key.Length, iv.Length);
            
            encryptedKeys = rsa.Encrypt(combinedKeys, RSAEncryptionPadding.Pkcs1);
        }

        // 3. Подпись всего пакета нашим Private Key
        string signature;
        using (var rsaSign = RSA.Create())
        {
            rsaSign.ImportPkcs8PrivateKey(Convert.FromBase64String(ourPrivateKeyBase64!), out _);
            var dataToSign = Encoding.UTF8.GetBytes(Convert.ToBase64String(encryptedBytes) + ourDomain + targetDomain);
            var sigBytes = rsaSign.SignData(dataToSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
            signature = Convert.ToBase64String(sigBytes);
        }

        var packet = new FederationMessagePacket(
            ourDomain,
            targetDomain,
            Convert.ToBase64String(encryptedBytes),
            Convert.ToBase64String(encryptedKeys),
            signature,
            metadata
        );

        return Result.Success(packet);
    }
}

