using System.Text;
using System.Text.Json;
using Metin2Launcher.Config;
using Metin2Launcher.Formats;
using Metin2Launcher.Manifest;
using Metin2Launcher.Orchestration;
using Xunit;
using ManifestDto = Metin2Launcher.Manifest.Manifest;

namespace Metin2Launcher.Tests;

/// <summary>
/// Exercises the <see cref="UpdateOrchestrator"/>'s format-dispatch wiring.
///
/// The orchestrator short-circuits to a signature exception when the fetched
/// manifest isn't signed by the hardcoded <see cref="LauncherConfig.PublicKeyHex"/>.
/// We can't realistically override that constant in a test without widening
/// the surface area, so these tests focus on:
///
///   1. The unit-level pieces the orchestrator relies on (factory, effective
///      format, file filtering, post-apply hook) — covered in sibling test
///      classes and asserted here for regression safety.
///   2. End-to-end "manifest fetched but signature mismatches" showing the
///      orchestrator really reaches the verify stage against a synthetic
///      HTTP endpoint served by <see cref="TestHttpListener"/>.
/// </summary>
public class UpdateOrchestratorFormatDispatchTests
{
    [Fact]
    public async Task Bad_signature_throws_before_format_dispatch()
    {
        using var listener = new TestHttpListener();
        var url = listener.Start();

        var manifest = new ManifestDto
        {
            Format = "m2pack",
            Version = "v1",
            CreatedAt = "2026-04-14T00:00:00Z",
            Launcher = new ManifestLauncherEntry { Path = "L.exe", Sha256 = "dead", Size = 1 },
        };
        var body = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(manifest));
        var badSig = new byte[64]; // deliberately wrong signature

        listener.RespondBody(req => req.RawUrl!.EndsWith(".sig") ? badSig : body);

        var clientRoot = Path.Combine(Path.GetTempPath(), "orch-" + Guid.NewGuid());
        Directory.CreateDirectory(clientRoot);
        try
        {
            using var http = new HttpClient();
            var settings = new LauncherSettings
            {
                DevMode = true,
                ManifestUrlOverride = url + "manifest.json",
            };
            var orchestrator = new UpdateOrchestrator(clientRoot, settings, http);

            await Assert.ThrowsAsync<ManifestSignatureException>(async () =>
                await orchestrator.RunAsync(new Progress<UpdateProgress>(_ => { }), CancellationToken.None));
        }
        finally { Directory.Delete(clientRoot, true); }
    }

    [Fact]
    public async Task Manifest_fetch_failure_returns_offline_fallback()
    {
        using var listener = new TestHttpListener();
        var url = listener.Start();
        listener.RespondWith(System.Net.HttpStatusCode.NotFound);

        var clientRoot = Path.Combine(Path.GetTempPath(), "orch-" + Guid.NewGuid());
        Directory.CreateDirectory(clientRoot);
        try
        {
            using var http = new HttpClient();
            var settings = new LauncherSettings
            {
                DevMode = true,
                ManifestUrlOverride = url + "manifest.json",
            };
            var orchestrator = new UpdateOrchestrator(clientRoot, settings, http);
            var result = await orchestrator.RunAsync(
                new Progress<UpdateProgress>(_ => { }),
                CancellationToken.None);
            Assert.False(result.Success);
            Assert.Equal(LauncherState.OfflineFallback, result.FinalState);
            Assert.Null(result.Format);
            Assert.Null(result.RuntimeKey);
        }
        finally { Directory.Delete(clientRoot, true); }
    }

    [Fact]
    public void Result_carries_format_and_key_slots()
    {
        // Contract test: the orchestrator Result exposes Format and RuntimeKey
        // so the launcher can thread the runtime key into GameProcess.Launch.
        var r = new UpdateOrchestrator.Result(true, LauncherState.UpToDate, null);
        Assert.Null(r.Format);
        Assert.Null(r.RuntimeKey);
    }

    [Fact]
    public void Factory_falls_back_through_effective_format_on_empty_string()
    {
        // A manifest with format="" should resolve via EffectiveFormat -> legacy.
        var m = new ManifestDto { Version = "v1", Format = "" };
        var f = ReleaseFormatFactory.Resolve(m.EffectiveFormat);
        Assert.IsType<LegacyJsonBlobFormat>(f);
    }
}
