using System.Text;
using Metin2Launcher.Runtime;
using Xunit;

namespace Metin2Launcher.Tests;

public class RuntimeKeyTests
{
    private const string ValidHex64 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";

    private static string SampleJson(
        string keyId = "2026.04.14-1",
        string master = ValidHex64,
        string pub = ValidHex64)
        => $$"""
{
  "key_id": "{{keyId}}",
  "master_key_hex": "{{master}}",
  "sign_pubkey_hex": "{{pub}}"
}
""";

    [Fact]
    public void Parse_happy_path()
    {
        var rk = RuntimeKey.Parse(Encoding.UTF8.GetBytes(SampleJson()));
        Assert.Equal("2026.04.14-1", rk.KeyId);
        Assert.Equal(ValidHex64, rk.MasterKeyHex);
        Assert.Equal(ValidHex64, rk.SignPubkeyHex);
    }

    [Fact]
    public void Parse_rejects_missing_key_id()
    {
        var json = SampleJson(keyId: "");
        Assert.Throws<InvalidDataException>(() => RuntimeKey.Parse(Encoding.UTF8.GetBytes(json)));
    }

    [Theory]
    [InlineData("")]
    [InlineData("tooshort")]
    [InlineData("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz")] // right len, wrong chars
    public void Parse_rejects_bad_master_key(string master)
    {
        var json = SampleJson(master: master);
        Assert.Throws<InvalidDataException>(() => RuntimeKey.Parse(Encoding.UTF8.GetBytes(json)));
    }

    [Fact]
    public void Parse_rejects_bad_pubkey()
    {
        var json = SampleJson(pub: new string('g', 64));
        Assert.Throws<InvalidDataException>(() => RuntimeKey.Parse(Encoding.UTF8.GetBytes(json)));
    }

    [Fact]
    public void Parse_accepts_uppercase_hex()
    {
        var upper = ValidHex64.ToUpperInvariant();
        var rk = RuntimeKey.Parse(Encoding.UTF8.GetBytes(SampleJson(master: upper, pub: upper)));
        Assert.Equal(upper, rk.MasterKeyHex);
    }

    [Fact]
    public void Load_reads_from_disk()
    {
        var tmp = Path.Combine(Path.GetTempPath(), "runtime-key-test-" + Guid.NewGuid() + ".json");
        File.WriteAllText(tmp, SampleJson());
        try
        {
            var rk = RuntimeKey.Load(tmp);
            Assert.Equal("2026.04.14-1", rk.KeyId);
        }
        finally { File.Delete(tmp); }
    }

    [Fact]
    public void Parse_rejects_malformed_json()
    {
        Assert.Throws<System.Text.Json.JsonException>(() =>
            RuntimeKey.Parse(Encoding.UTF8.GetBytes("not json")));
    }

    // m2pack export-runtime-key emits key_id as an integer and the public
    // key field as `sign_public_key_hex`, plus extra version/mapping_name
    // fields that are Windows-shared-memory bookkeeping. RuntimeKey must
    // tolerate that shape so release pipelines can pipe the CLI output
    // directly. See metin-server/m2pack-secure#3.
    [Fact]
    public void Parse_accepts_m2pack_cli_shape()
    {
        var json = $$"""
        {
          "version": 1,
          "mapping_name": "Local\\M2PackSharedKeys",
          "key_id": 1,
          "master_key_hex": "{{ValidHex64}}",
          "sign_public_key_hex": "{{ValidHex64}}"
        }
        """;
        var rk = RuntimeKey.Parse(Encoding.UTF8.GetBytes(json));
        Assert.Equal("1", rk.KeyId);
        Assert.Equal(ValidHex64, rk.MasterKeyHex);
        Assert.Equal(ValidHex64, rk.SignPubkeyHex);
    }
}
