using System.Security.Cryptography;
using System.Text.Json;
using NATS.Server.Configuration;
using NATS.Server.TestUtilities;
using NATS.Server.Tls;

namespace NATS.Server.Transport.Tests;

public class TlsOcspParityBatch1Tests
{
    [Fact]
    public void OCSPPeerConfig_defaults_match_go_reference()
    {
        var cfg = OCSPPeerConfig.NewOCSPPeerConfig();

        cfg.Verify.ShouldBeFalse();
        cfg.Timeout.ShouldBe(2d);
        cfg.ClockSkew.ShouldBe(30d);
        cfg.WarnOnly.ShouldBeFalse();
        cfg.UnknownIsGood.ShouldBeFalse();
        cfg.AllowWhenCAUnreachable.ShouldBeFalse();
        cfg.TTLUnsetNextUpdate.ShouldBe(3600d);
    }

    [Fact]
    public void OCSPPeerConfig_parse_map_parses_supported_fields()
    {
        var cfg = OCSPPeerConfig.Parse(new Dictionary<string, object?>
        {
            ["verify"] = true,
            ["allowed_clockskew"] = "45s",
            ["ca_timeout"] = 1.5d,
            ["cache_ttl_when_next_update_unset"] = 120L,
            ["warn_only"] = true,
            ["unknown_is_good"] = true,
            ["allow_when_ca_unreachable"] = true,
        });

        cfg.Verify.ShouldBeTrue();
        cfg.ClockSkew.ShouldBe(45d);
        cfg.Timeout.ShouldBe(1.5d);
        cfg.TTLUnsetNextUpdate.ShouldBe(120d);
        cfg.WarnOnly.ShouldBeTrue();
        cfg.UnknownIsGood.ShouldBeTrue();
        cfg.AllowWhenCAUnreachable.ShouldBeTrue();
    }

    [Fact]
    public void OCSPPeerConfig_parse_unknown_field_throws()
    {
        var ex = Should.Throw<FormatException>(() =>
            OCSPPeerConfig.Parse(new Dictionary<string, object?> { ["bogus"] = true }));

        ex.Message.ShouldContain("unknown field [bogus]");
    }

    [Fact]
    public void ConfigProcessor_parses_ocsp_peer_short_form()
    {
        var opts = ConfigProcessor.ProcessConfig("""
            tls {
              ocsp_peer: true
            }
            """);

        opts.OcspPeerVerify.ShouldBeTrue();
    }

    [Fact]
    public void ConfigProcessor_parses_ocsp_peer_long_form_verify()
    {
        var opts = ConfigProcessor.ProcessConfig("""
            tls {
              ocsp_peer {
                verify: true
                ca_timeout: 2s
                allowed_clockskew: 30s
              }
            }
            """);

        opts.OcspPeerVerify.ShouldBeTrue();
    }

    [Fact]
    public void GenerateFingerprint_uses_raw_certificate_sha256()
    {
        var (cert, _) = TestCertHelper.GenerateTestCert();

        var expected = Convert.ToBase64String(SHA256.HashData(cert.RawData));
        TlsHelper.GenerateFingerprint(cert).ShouldBe(expected);
    }

    [Fact]
    public void GetWebEndpoints_filters_non_web_uris()
    {
        var urls = TlsHelper.GetWebEndpoints(
            ["http://a.example", "https://b.example", "ftp://bad.example", "not a uri"]);

        urls.Count.ShouldBe(2);
        urls[0].Scheme.ShouldBe(Uri.UriSchemeHttp);
        urls[1].Scheme.ShouldBe(Uri.UriSchemeHttps);
    }

    [Fact]
    public void Subject_and_issuer_dn_helpers_return_values_and_empty_for_null()
    {
        var (cert, _) = TestCertHelper.GenerateTestCert();

        TlsHelper.GetSubjectDNForm(cert).ShouldNotBeNullOrWhiteSpace();
        TlsHelper.GetIssuerDNForm(cert).ShouldNotBeNullOrWhiteSpace();
        TlsHelper.GetSubjectDNForm(null).ShouldBe(string.Empty);
        TlsHelper.GetIssuerDNForm(null).ShouldBe(string.Empty);
    }

    [Fact]
    public void StatusAssertion_json_converter_uses_string_values_and_unknown_fallback()
    {
        var revokedJson = JsonSerializer.Serialize(StatusAssertion.Revoked);
        revokedJson.ShouldBe("\"revoked\"");

        var unknown = JsonSerializer.Deserialize<StatusAssertion>("\"nonsense\"");
        unknown.ShouldBe(StatusAssertion.Unknown);
    }

    [Fact]
    public void OcspPeer_messages_match_go_literals()
    {
        OcspPeerMessages.MsgTLSClientRejectConnection.ShouldBe("client not OCSP valid");
        OcspPeerMessages.MsgTLSServerRejectConnection.ShouldBe("server not OCSP valid");
        OcspPeerMessages.MsgCacheOnline.ShouldBe("OCSP peer cache online, type [%s]");
        OcspPeerMessages.MsgCacheOffline.ShouldBe("OCSP peer cache offline, type [%s]");
    }
}
