// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using SkiaSharp;
using Xunit;
using Microsoft.Maui.Platform.Linux.Services;

namespace Microsoft.Maui.Platform.Tests;

public class HiDpiServiceTests
{
    [Fact]
    public void Constructor_InitializesWithDefaultValues()
    {
        var service = new HiDpiService();

        Assert.Equal(1.0f, service.ScaleFactor);
        Assert.Equal(96f, service.Dpi);
    }

    [Fact]
    public void Initialize_CanBeCalled()
    {
        var service = new HiDpiService();

        service.Initialize();

        // Should not throw
    }

    [Fact]
    public void Initialize_OnlyRunsOnce()
    {
        var service = new HiDpiService();

        service.Initialize();
        service.Initialize();

        // Second call should be no-op
    }

    [Fact]
    public void DetectScaleFactor_CanBeCalled()
    {
        var service = new HiDpiService();

        service.DetectScaleFactor();

        // Should not throw, may or may not find scale
    }

    [Fact]
    public void ToPhysicalPixels_WithDefaultScale_ReturnsInput()
    {
        var service = new HiDpiService();

        float result = service.ToPhysicalPixels(100f);

        Assert.Equal(100f, result);
    }

    [Fact]
    public void ToLogicalPixels_WithDefaultScale_ReturnsInput()
    {
        var service = new HiDpiService();

        float result = service.ToLogicalPixels(100f);

        Assert.Equal(100f, result);
    }

    [Fact]
    public void ScaleChangedEvent_CanBeSubscribed()
    {
        var service = new HiDpiService();
        bool eventRaised = false;

        service.ScaleChanged += (s, e) => eventRaised = true;

        Assert.False(eventRaised); // Not raised yet
    }

    [Fact]
    public void GetFontScaleFactor_ReturnsValidValue()
    {
        var service = new HiDpiService();

        float scale = service.GetFontScaleFactor();

        Assert.True(scale > 0);
    }
}

public class ScaleChangedEventArgsTests
{
    [Fact]
    public void Constructor_SetsProperties()
    {
        var args = new ScaleChangedEventArgs(1.0f, 2.0f, 192f);

        Assert.Equal(1.0f, args.OldScale);
        Assert.Equal(2.0f, args.NewScale);
        Assert.Equal(192f, args.NewDpi);
    }
}

public class HighContrastServiceTests
{
    [Fact]
    public void Constructor_InitializesWithDefaultValues()
    {
        var service = new HighContrastService();

        Assert.False(service.IsHighContrastEnabled);
        Assert.Equal(HighContrastTheme.None, service.CurrentTheme);
    }

    [Fact]
    public void Initialize_CanBeCalled()
    {
        var service = new HighContrastService();

        service.Initialize();

        // Should not throw
    }

    [Fact]
    public void DetectHighContrast_CanBeCalled()
    {
        var service = new HighContrastService();

        service.DetectHighContrast();

        // Should not throw
    }

    [Fact]
    public void ForceHighContrast_EnablesHighContrast()
    {
        var service = new HighContrastService();

        service.ForceHighContrast(true, HighContrastTheme.WhiteOnBlack);

        Assert.True(service.IsHighContrastEnabled);
        Assert.Equal(HighContrastTheme.WhiteOnBlack, service.CurrentTheme);
    }

    [Fact]
    public void ForceHighContrast_DisablesHighContrast()
    {
        var service = new HighContrastService();
        service.ForceHighContrast(true);

        service.ForceHighContrast(false);

        Assert.False(service.IsHighContrastEnabled);
    }

    [Fact]
    public void GetColors_ReturnsWhiteOnBlackColors()
    {
        var service = new HighContrastService();
        service.ForceHighContrast(true, HighContrastTheme.WhiteOnBlack);

        var colors = service.GetColors();

        Assert.Equal(SKColors.Black, colors.Background);
        Assert.Equal(SKColors.White, colors.Foreground);
    }

    [Fact]
    public void GetColors_ReturnsBlackOnWhiteColors()
    {
        var service = new HighContrastService();
        service.ForceHighContrast(true, HighContrastTheme.BlackOnWhite);

        var colors = service.GetColors();

        Assert.Equal(SKColors.White, colors.Background);
        Assert.Equal(SKColors.Black, colors.Foreground);
    }

    [Fact]
    public void GetColors_ReturnsDefaultColorsWhenDisabled()
    {
        var service = new HighContrastService();

        var colors = service.GetColors();

        Assert.Equal(SKColors.White, colors.Background);
        Assert.NotEqual(SKColors.Black, colors.Foreground); // Default is gray
    }

    [Fact]
    public void HighContrastChangedEvent_CanBeSubscribed()
    {
        var service = new HighContrastService();
        bool eventRaised = false;

        service.HighContrastChanged += (s, e) => eventRaised = true;
        service.ForceHighContrast(true);

        Assert.True(eventRaised);
    }
}

public class HighContrastColorsTests
{
    [Fact]
    public void AllPropertiesCanBeSet()
    {
        var colors = new HighContrastColors
        {
            Background = SKColors.Black,
            Foreground = SKColors.White,
            Accent = SKColors.Cyan,
            Border = SKColors.White,
            Error = SKColors.Red,
            Success = SKColors.Green,
            Warning = SKColors.Yellow,
            Link = SKColors.Blue,
            LinkVisited = SKColors.Purple,
            Selection = SKColors.Blue,
            SelectionText = SKColors.White,
            DisabledText = SKColors.Gray,
            DisabledBackground = SKColors.DarkGray
        };

        Assert.Equal(SKColors.Black, colors.Background);
        Assert.Equal(SKColors.White, colors.Foreground);
        Assert.Equal(SKColors.Cyan, colors.Accent);
    }
}

public class HighContrastChangedEventArgsTests
{
    [Fact]
    public void Constructor_SetsProperties()
    {
        var args = new HighContrastChangedEventArgs(true, HighContrastTheme.WhiteOnBlack);

        Assert.True(args.IsEnabled);
        Assert.Equal(HighContrastTheme.WhiteOnBlack, args.Theme);
    }
}

public class DragDropServiceTests
{
    [Fact]
    public void Constructor_InitializesWithDefaultValues()
    {
        var service = new DragDropService();

        Assert.False(service.IsDragging);
    }

    [Fact]
    public void CancelDrag_WhenNotDragging_DoesNotThrow()
    {
        var service = new DragDropService();

        service.CancelDrag();

        Assert.False(service.IsDragging);
    }

    [Fact]
    public void ProcessClientMessage_CanBeCalled()
    {
        var service = new DragDropService();

        // Simply verify the method can be called without exception
        service.ProcessClientMessage(0, new nint[5]);

        Assert.NotNull(service);
    }

    [Fact]
    public void DragEnterEvent_CanBeSubscribed()
    {
        var service = new DragDropService();
        bool eventRaised = false;

        service.DragEnter += (s, e) => eventRaised = true;

        Assert.False(eventRaised);
    }

    [Fact]
    public void DragOverEvent_CanBeSubscribed()
    {
        var service = new DragDropService();
        bool eventRaised = false;

        service.DragOver += (s, e) => eventRaised = true;

        Assert.False(eventRaised);
    }

    [Fact]
    public void DragLeaveEvent_CanBeSubscribed()
    {
        var service = new DragDropService();
        bool eventRaised = false;

        service.DragLeave += (s, e) => eventRaised = true;

        Assert.False(eventRaised);
    }

    [Fact]
    public void DropEvent_CanBeSubscribed()
    {
        var service = new DragDropService();
        bool eventRaised = false;

        service.Drop += (s, e) => eventRaised = true;

        Assert.False(eventRaised);
    }

    [Fact]
    public void Dispose_CanBeCalled()
    {
        var service = new DragDropService();

        service.Dispose();

        // Should not throw
    }

    [Fact]
    public void Dispose_CanBeCalledMultipleTimes()
    {
        var service = new DragDropService();

        service.Dispose();
        service.Dispose();

        // Should not throw
    }
}

public class DragDataTests
{
    [Fact]
    public void Constructor_InitializesWithDefaultValues()
    {
        var data = new DragData();

        Assert.Equal(IntPtr.Zero, data.SourceWindow);
        Assert.Empty(data.SupportedTypes);
        Assert.Null(data.Text);
        Assert.Null(data.FilePaths);
        Assert.Null(data.Data);
    }

    [Fact]
    public void Text_CanBeSet()
    {
        var data = new DragData { Text = "Hello World" };

        Assert.Equal("Hello World", data.Text);
    }

    [Fact]
    public void FilePaths_CanBeSet()
    {
        var data = new DragData
        {
            FilePaths = new[] { "/home/user/file1.txt", "/home/user/file2.txt" }
        };

        Assert.Equal(2, data.FilePaths.Length);
    }

    [Fact]
    public void Data_CanBeSet()
    {
        var customData = new { Id = 1, Name = "Test" };
        var data = new DragData { Data = customData };

        Assert.Equal(customData, data.Data);
    }
}

public class LinuxDragEventArgsTests
{
    [Fact]
    public void Constructor_SetsProperties()
    {
        var dragData = new DragData { Text = "Test" };
        var args = new Microsoft.Maui.Platform.Linux.Services.DragEventArgs(dragData, 100, 200);

        Assert.Equal(dragData, args.Data);
        Assert.Equal(100, args.X);
        Assert.Equal(200, args.Y);
        Assert.False(args.Accepted);
    }

    [Fact]
    public void Accepted_CanBeSet()
    {
        var args = new Microsoft.Maui.Platform.Linux.Services.DragEventArgs(new DragData(), 0, 0);

        args.Accepted = true;

        Assert.True(args.Accepted);
    }

    [Fact]
    public void AllowedAction_CanBeSet()
    {
        var args = new Microsoft.Maui.Platform.Linux.Services.DragEventArgs(new DragData(), 0, 0);

        args.AllowedAction = DragAction.Move;

        Assert.Equal(DragAction.Move, args.AllowedAction);
    }
}

public class LinuxDropEventArgsTests
{
    [Fact]
    public void Constructor_SetsProperties()
    {
        var dragData = new DragData();
        var args = new Microsoft.Maui.Platform.Linux.Services.DropEventArgs(dragData, "dropped content");

        Assert.Equal(dragData, args.Data);
        Assert.Equal("dropped content", args.DroppedData);
        Assert.False(args.Handled);
    }

    [Fact]
    public void Handled_CanBeSet()
    {
        var args = new Microsoft.Maui.Platform.Linux.Services.DropEventArgs(new DragData(), null);

        args.Handled = true;

        Assert.True(args.Handled);
    }
}

public class GlobalHotkeyServiceTests
{
    [Fact]
    public void Constructor_DoesNotThrow()
    {
        var service = new GlobalHotkeyService();

        Assert.NotNull(service);
    }

    [Fact]
    public void HotkeyPressedEvent_CanBeSubscribed()
    {
        var service = new GlobalHotkeyService();
        bool eventRaised = false;

        service.HotkeyPressed += (s, e) => eventRaised = true;

        Assert.False(eventRaised);
    }

    [Fact]
    public void UnregisterAll_WhenNoRegistrations_DoesNotThrow()
    {
        var service = new GlobalHotkeyService();

        service.UnregisterAll();

        // Should not throw
    }

    [Fact]
    public void Dispose_CanBeCalled()
    {
        var service = new GlobalHotkeyService();

        service.Dispose();

        // Should not throw
    }
}

public class HotkeyEventArgsTests
{
    [Fact]
    public void Constructor_SetsProperties()
    {
        var args = new HotkeyEventArgs(1, HotkeyKey.A, HotkeyModifiers.Control);

        Assert.Equal(1, args.Id);
        Assert.Equal(HotkeyKey.A, args.Key);
        Assert.Equal(HotkeyModifiers.Control, args.Modifiers);
    }

    [Fact]
    public void ModifiersCanBeCombined()
    {
        var modifiers = HotkeyModifiers.Control | HotkeyModifiers.Shift;
        var args = new HotkeyEventArgs(1, HotkeyKey.S, modifiers);

        Assert.True(args.Modifiers.HasFlag(HotkeyModifiers.Control));
        Assert.True(args.Modifiers.HasFlag(HotkeyModifiers.Shift));
    }
}

public class HotkeyModifiersTests
{
    [Fact]
    public void None_IsZero()
    {
        Assert.Equal(0, (int)HotkeyModifiers.None);
    }

    [Fact]
    public void AllModifiers_AreFlagBased()
    {
        var all = HotkeyModifiers.Shift | HotkeyModifiers.Control | HotkeyModifiers.Alt | HotkeyModifiers.Super;

        Assert.True(all.HasFlag(HotkeyModifiers.Shift));
        Assert.True(all.HasFlag(HotkeyModifiers.Control));
        Assert.True(all.HasFlag(HotkeyModifiers.Alt));
        Assert.True(all.HasFlag(HotkeyModifiers.Super));
    }
}

public class HotkeyKeyTests
{
    [Fact]
    public void Letters_HaveCorrectValues()
    {
        Assert.Equal((uint)0x61, (uint)HotkeyKey.A);
        Assert.Equal((uint)0x7A, (uint)HotkeyKey.Z);
    }

    [Fact]
    public void FunctionKeys_HaveCorrectValues()
    {
        Assert.Equal((uint)0xFFBE, (uint)HotkeyKey.F1);
        Assert.Equal((uint)0xFFC9, (uint)HotkeyKey.F12);
    }

    [Fact]
    public void SpecialKeys_HaveCorrectValues()
    {
        Assert.Equal(HotkeyKey.Escape, HotkeyKey.Escape);
        Assert.Equal(HotkeyKey.Space, HotkeyKey.Space);
        Assert.Equal(HotkeyKey.Enter, HotkeyKey.Enter);
    }

    [Fact]
    public void ArrowKeys_HaveCorrectValues()
    {
        Assert.Equal((uint)0xFF51, (uint)HotkeyKey.Left);
        Assert.Equal((uint)0xFF52, (uint)HotkeyKey.Up);
        Assert.Equal((uint)0xFF53, (uint)HotkeyKey.Right);
        Assert.Equal((uint)0xFF54, (uint)HotkeyKey.Down);
    }
}
