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

using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using Moq;

namespace System.Windows.Forms.Design.Tests;

public class FlowLayoutPanelDesignerTests : IDisposable
{
    private readonly Mock<IServiceProvider> _serviceProviderMock;
    private readonly Mock<IDesignerHost> _designerHostMock;
    private readonly Mock<ISelectionService> _selectionServiceMock;
    private readonly FlowLayoutPanelDesigner _designer;
    private readonly FlowLayoutPanel _flowLayoutPanel;

    public FlowLayoutPanelDesignerTests()
    {
        _serviceProviderMock = new();
        _designerHostMock = new();
        _selectionServiceMock = new();

        _serviceProviderMock.Setup(s => s.GetService(typeof(IDesignerHost))).Returns(_designerHostMock.Object);
        _serviceProviderMock.Setup(s => s.GetService(typeof(ISelectionService))).Returns(_selectionServiceMock.Object);

        _flowLayoutPanel = new() { Site = new Mock<ISite>().Object };
        _designer = new();
        _designer.Initialize(_flowLayoutPanel);
    }

    public void Dispose()
    {
        _designer.Dispose();
        _flowLayoutPanel.Dispose();
    }

    [Fact]
    public void Initialize_ShouldSetInheritedReadOnlyAttribute_WhenFlowLayoutPanelIsInheritedReadOnly()
    {
        using Control childControl = new() { Site = new Mock<ISite>().Object };
        _flowLayoutPanel.Controls.Add(childControl);

        TypeDescriptor.AddAttributes(childControl, InheritanceAttribute.InheritedReadOnly);

        TypeDescriptor.GetAttributes(childControl)[typeof(InheritanceAttribute)]
            .Should().BeEquivalentTo(InheritanceAttribute.InheritedReadOnly);
    }

    [Fact]
    public void Initialize_ShouldBeNotInheritedAttribute_WhenFlowLayoutPanelIsNotInheritedReadOnly()
    {
        using Control childControl = new() { Site = new Mock<ISite>().Object };
        _flowLayoutPanel.Controls.Add(childControl);

        TypeDescriptor.GetAttributes(childControl)[typeof(InheritanceAttribute)]
            .Should().NotBeEquivalentTo(InheritanceAttribute.InheritedReadOnly);
    }

    [Fact]
    public void PreFilterProperties_ShouldModifyFlowDirectionProperty()
    {
        Hashtable properties = new()
        {
            { nameof(FlowLayoutPanel.FlowDirection), TypeDescriptor.CreateProperty(typeof(FlowLayoutPanel), nameof(FlowLayoutPanel.FlowDirection), typeof(FlowDirection)) }
        };

        _designer.TestAccessor.Dynamic.PreFilterProperties(properties);

        properties[nameof(FlowLayoutPanel.FlowDirection)].Should().NotBeNull();
        properties[nameof(FlowLayoutPanel.FlowDirection)].Should().BeAssignableTo<PropertyDescriptor>();
    }

    [Fact]
    public void AllowSetChildIndexOnDrop_ShouldReturnFalse() =>
        _designer.AllowSetChildIndexOnDrop.Should().BeFalse();

    [Fact]
    public void AllowGenericDragBox_ShouldReturnFalse()
    {
        bool result = _designer.TestAccessor.Dynamic.AllowGenericDragBox;

        result.Should().BeFalse();
    }

    [Theory]
    [InlineData(FlowDirection.LeftToRight, true)]
    [InlineData(FlowDirection.RightToLeft, true)]
    [InlineData(FlowDirection.TopDown, false)]
    [InlineData(FlowDirection.BottomUp, false)]
    public void HorizontalFlow_ShouldReturnCorrectValueBasedOnFlowDirection(FlowDirection flowDirection, bool expected)
    {
        _flowLayoutPanel.FlowDirection = flowDirection;

        bool result = _designer.TestAccessor.Dynamic.HorizontalFlow;

        result.Should().Be(expected);
    }

    [Theory]
    [InlineData(FlowDirection.LeftToRight, FlowDirection.RightToLeft)]
    [InlineData(FlowDirection.RightToLeft, FlowDirection.LeftToRight)]
    [InlineData(FlowDirection.TopDown, FlowDirection.TopDown)]
    [InlineData(FlowDirection.BottomUp, FlowDirection.BottomUp)]
    public void RTLTranslateFlowDirection_ShouldTranslateCorrectly(FlowDirection input, FlowDirection expected)
    {
        _flowLayoutPanel.RightToLeft = RightToLeft.Yes;

        FlowDirection result = _designer.TestAccessor.Dynamic.RTLTranslateFlowDirection(input);

        result.Should().Be(expected);
    }

    [Theory]
    [InlineData(RightToLeft.Yes, true)]
    [InlineData(RightToLeft.No, false)]
    public void IsRtl_ShouldReturnCorrectValueBasedOnRightToLeft(RightToLeft rightToLeft, bool expected)
    {
        _flowLayoutPanel.RightToLeft = rightToLeft;

        bool result = _designer.TestAccessor.Dynamic.IsRtl;

        result.Should().Be(expected);
    }

    [Fact]
    public void GetMarginBounds_ShouldReturnCorrectRectangle()
    {
        using Control control = new()
        {
            Bounds = new Rectangle(10, 20, 30, 40),
            Margin = new Padding(5, 6, 7, 8)
        };

        Rectangle result = _designer.TestAccessor.Dynamic.GetMarginBounds(control);

        result.Should().Be(new Rectangle(5, 14, 42, 54));
    }

    [Fact]
    public void OnDragEnter_ShouldInitializeDragState()
    {
        DragEventArgs dragEventArgs = new(
            new DataObject(),
            0,
            0,
            0,
            DragDropEffects.Copy,
            DragDropEffects.Copy);

        _designer.TestAccessor.Dynamic.OnDragEnter(dragEventArgs);

        int insertionIndex = _designer.TestAccessor.Dynamic._insertionIndex;
        insertionIndex.Should().Be(-1);

        Point lastMouseLocation = _designer.TestAccessor.Dynamic._lastMouseLocation;
        lastMouseLocation.Should().Be(Point.Empty);
    }

    [Fact]
    public void OnDragLeave_ShouldClearDragState()
    {
        _designer.TestAccessor.Dynamic.OnDragLeave(EventArgs.Empty);

        int insertionIndex = _designer.TestAccessor.Dynamic._insertionIndex;
        insertionIndex.Should().Be(-1);
    }

    [Fact]
    public void OnDragOver_ShouldUpdateInsertionIndex()
    {
        DragEventArgs dragEventArgs = new(
            new DataObject(),
            0,
            10,
            10,
            DragDropEffects.Copy,
            DragDropEffects.Copy);

        _designer.TestAccessor.Dynamic.OnDragOver(dragEventArgs);

        int insertionIndex = _designer.TestAccessor.Dynamic._insertionIndex;
        insertionIndex.Should().Be(-1);
    }

    [Fact]
    public void OnDragDrop_ShouldReorderControls()
    {
        using Control control1 = new();
        using Control control2 = new();
        _flowLayoutPanel.Controls.Add(control1);
        _flowLayoutPanel.Controls.Add(control2);
        DragEventArgs dragEventArgs = new(
            new DataObject(),
            0,
            0,
            0,
            DragDropEffects.Move,
            DragDropEffects.Move);

        _designer.TestAccessor.Dynamic.OnDragDrop(dragEventArgs);

        _flowLayoutPanel.Controls[0].Should().Be(control1);
        _flowLayoutPanel.Controls[1].Should().Be(control2);
    }
}
