using System.IO;
using System.Text.Json;
using System.Windows;
using FluentAssertions;
using DragonISO.App.Services;

namespace DragonISO.App.Tests.Services;

// Round-trip tests for WindowStateStore.Save / TryApply. Constructing a
// real WPF Window inside an xUnit fact is awkward (no Application.Run,
// no dispatcher), so we exercise the JSON layer + the placement-validity
// rejection logic by writing snapshots directly to disk and reading
// them back. Save is exercised by serializing a Snapshot record
// inline and asserting JsonSerializer can round-trip it through the
// shape WindowStateStore writes.
//
// The full Window.Left/Width property writes inside TryApply aren't
// covered here â€” they require a WPF Window instance, which means an
// Application.Current + dispatcher. We instead cover the bail paths
// (file missing, too-small, off-screen) which is where regressions
// typically land.
public sealed class WindowStateStoreTests : IDisposable
{
    private readonly string _tempPath;
    private readonly string? _previousOverride;

    public WindowStateStoreTests()
    {
        _tempPath = Path.Combine(Path.GetTempPath(), $"Dragon-ISO-window-{Guid.NewGuid():N}.json");
        _previousOverride = WindowStateStore.PathOverride;
        WindowStateStore.PathOverride = _tempPath;
    }

    public void Dispose()
    {
        WindowStateStore.PathOverride = _previousOverride;
        try { if (File.Exists(_tempPath)) File.Delete(_tempPath); }
        catch { /* best-effort */ }
    }

    private static void WriteSnapshot(string path, WindowStateStore.Snapshot snap)
    {
        File.WriteAllText(path, JsonSerializer.Serialize(snap, new JsonSerializerOptions { WriteIndented = true }));
    }

    [Fact]
    public void Snapshot_JsonRoundTrips_CleanlyThroughTheSameSerializerShape()
    {
        // Write a Snapshot record through the same JsonSerializer.Serialize
        // call WindowStateStore.Save uses; read it back and verify all
        // five fields survive. Coverage gap (Save's own Window reads)
        // intentional â€” see file header.
        var snap = new WindowStateStore.Snapshot(
            Left: 120, Top: 80, Width: 1024, Height: 768, State: WindowState.Maximized);
        WriteSnapshot(_tempPath, snap);

        var roundTripped = JsonSerializer.Deserialize<WindowStateStore.Snapshot>(File.ReadAllText(_tempPath));

        roundTripped.Should().NotBeNull();
        roundTripped!.Left.Should().Be(120);
        roundTripped.Top.Should().Be(80);
        roundTripped.Width.Should().Be(1024);
        roundTripped.Height.Should().Be(768);
        roundTripped.State.Should().Be(WindowState.Maximized);
    }

    [Fact]
    public void TryApply_NoFile_ReturnsFalse()
    {
        File.Exists(_tempPath).Should().BeFalse();

        // We can't construct a Window without STA; we *can* exercise
        // the bail path that returns before any Window property is
        // touched by passing null and catching the NRE through the
        // store's own try/catch â€” which makes TryApply return false.
        var result = WindowStateStore.TryApply(null!);

        result.Should().BeFalse();
    }

    [Fact]
    public void TryApply_TooSmallSnapshot_RejectsBeforeTouchingWindow()
    {
        // 100Ã—100 is below the 320Ã—240 floor. TryApply should return
        // false without throwing on the null window.
        WriteSnapshot(_tempPath, new WindowStateStore.Snapshot(0, 0, 100, 100, WindowState.Normal));

        var result = WindowStateStore.TryApply(null!);

        result.Should().BeFalse();
    }

    [Fact]
    public void TryApply_AbsurdlyLargeSnapshot_RejectsBeforeTouchingWindow()
    {
        // 20000Ã—20000 is above the safety ceiling. Again no throw.
        WriteSnapshot(_tempPath, new WindowStateStore.Snapshot(0, 0, 20000, 20000, WindowState.Normal));

        var result = WindowStateStore.TryApply(null!);

        result.Should().BeFalse();
    }

    [Fact]
    public void TryApply_FullyOffScreenSnapshot_RejectsBeforeTouchingWindow()
    {
        // Way off the virtual screen â€” no corner falls inside any
        // monitor's working area.
        WriteSnapshot(_tempPath, new WindowStateStore.Snapshot(
            Left: -99999, Top: -99999, Width: 800, Height: 600, State: WindowState.Normal));

        var result = WindowStateStore.TryApply(null!);

        result.Should().BeFalse();
    }

    [Fact]
    public void TryApply_GarbageJson_ReturnsFalseRatherThanThrowing()
    {
        File.WriteAllText(_tempPath, "{ this is not valid json");

        var result = WindowStateStore.TryApply(null!);

        result.Should().BeFalse();
    }
}
