using System;
using PolyTrader.Core.Security;
using Xunit;

namespace PolyTrader.Tests
{
    /// <summary>
    /// Sicherheitsnetz für die at-rest-Verschlüsselung sensibler Felder (F1): Round-Trip, Alt-Klartext-
    /// Passthrough, Manipulations-/Falscher-Key-Erkennung (AES-GCM), Verhalten ohne Master-Key.
    /// Geldkritisch – ein Fehler hier macht Wallet-Keys unlesbar.
    /// </summary>
    public class SecretProtectionTests : IDisposable
    {
        // 32-Byte-Testschlüssel als Base64.
        private const string KeyA = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8="; // 0..31
        private const string KeyB = "/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v4=";

        public SecretProtectionTests() => SecretProtection.Reset();
        public void Dispose() => SecretProtection.Reset();

        [Fact]
        public void Roundtrip_encrypts_and_decrypts()
        {
            SecretProtection.Configure(KeyA);
            const string secret = "0x1234567890abcdef_private_key";

            string enc = SecretProtection.Protect(secret);

            Assert.StartsWith(SecretProtection.Prefix, enc);
            Assert.DoesNotContain(secret, enc);              // Klartext nicht sichtbar
            Assert.Equal(secret, SecretProtection.Unprotect(enc));
        }

        [Fact]
        public void Protect_uses_fresh_nonce_each_time()
        {
            SecretProtection.Configure(KeyA);
            Assert.NotEqual(SecretProtection.Protect("same"), SecretProtection.Protect("same"));
        }

        [Fact]
        public void Empty_stays_empty()
        {
            SecretProtection.Configure(KeyA);
            Assert.Equal("", SecretProtection.Protect(""));
            Assert.Equal("", SecretProtection.Unprotect(""));
        }

        [Fact]
        public void Legacy_plaintext_passes_through_on_read()
        {
            SecretProtection.Configure(KeyA);
            Assert.Equal("legacy-plain", SecretProtection.Unprotect("legacy-plain")); // kein Präfix
        }

        [Fact]
        public void Double_protect_does_not_wrap_twice()
        {
            SecretProtection.Configure(KeyA);
            string once = SecretProtection.Protect("x");
            Assert.Equal(once, SecretProtection.Protect(once)); // schon verschlüsselt -> unverändert
        }

        [Fact]
        public void Without_master_key_plaintext_passthrough()
        {
            // nicht konfiguriert
            Assert.False(SecretProtection.IsConfigured);
            Assert.Equal("plain", SecretProtection.Protect("plain"));   // kein Zwang zu Klartextverlust, aber Passthrough
            Assert.Equal("plain", SecretProtection.Unprotect("plain"));
        }

        [Fact]
        public void Without_master_key_encrypted_value_throws()
        {
            SecretProtection.Configure(KeyA);
            string enc = SecretProtection.Protect("secret");
            SecretProtection.Reset(); // Key entfernt
            Assert.Throws<InvalidOperationException>(() => SecretProtection.Unprotect(enc));
        }

        [Fact]
        public void Wrong_key_throws()
        {
            SecretProtection.Configure(KeyA);
            string enc = SecretProtection.Protect("secret");
            SecretProtection.Configure(KeyB);
            Assert.Throws<InvalidOperationException>(() => SecretProtection.Unprotect(enc));
        }

        [Fact]
        public void Tampered_ciphertext_throws()
        {
            SecretProtection.Configure(KeyA);
            string enc = SecretProtection.Protect("secret");
            // letztes Base64-Zeichen kippen
            char last = enc[^1];
            string tampered = enc.Substring(0, enc.Length - 1) + (last == 'A' ? 'B' : 'A');
            Assert.Throws<InvalidOperationException>(() => SecretProtection.Unprotect(tampered));
        }

        [Theory]
        [InlineData("AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=")]  // Base64 32 Byte
        [InlineData("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")] // Hex 64
        [InlineData("eine-beliebige-passphrase")]                       // abgeleitet via SHA-256
        public void Various_key_formats_roundtrip(string key)
        {
            SecretProtection.Configure(key);
            string enc = SecretProtection.Protect("value");
            Assert.Equal("value", SecretProtection.Unprotect(enc));
        }

        [Fact]
        public void IsEncrypted_detects_prefix()
        {
            SecretProtection.Configure(KeyA);
            Assert.True(SecretProtection.IsEncrypted(SecretProtection.Protect("x")));
            Assert.False(SecretProtection.IsEncrypted("plain"));
            Assert.False(SecretProtection.IsEncrypted(""));
        }
    }
}
