using FsCheck;
using FsCheck.Fluent;
using FsCheck.Xunit;
using Moq;
using Xunit;
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using XplorePlane.Models;
using XplorePlane.Services;
using XplorePlane.ViewModels;
using XplorePlane.Tests.Helpers;

namespace XplorePlane.Tests.Pipeline
{
    /// <summary>
    /// 属性测试（Property-Based Tests）使用 FsCheck
    /// 覆盖任务 7.1 ~ 7.7
    /// </summary>
    public class PipelinePropertyTests
    {
        private static readonly string[] Keys = TestHelpers.DefaultKeys;

        private PipelineEditorViewModel CreateVm()
        {
            var mockImageSvc = TestHelpers.CreateMockImageService();
            var mockExecSvc = new Mock<IPipelineExecutionService>();
            var mockPersistSvc = new Mock<IPipelinePersistenceService>();
            return new PipelineEditorViewModel(mockImageSvc.Object, mockExecSvc.Object, mockPersistSvc.Object);
        }

        /// <summary>
        /// 向 VM 中添加 n 个节点（循环使用 Keys）
        /// </summary>
        private static void PopulateNodes(PipelineEditorViewModel vm, int count)
        {
            for (int i = 0; i < count; i++)
                vm.AddOperatorCommand.Execute(Keys[i % Keys.Length]);
        }

        // ── Property 6：ReorderNode 后 Order == 下标 ──────────────────

        /// <summary>
        /// 任务 7.1：对任意合法 oldIndex/newIndex，ReorderNode 后所有 Order == 下标
        /// </summary>
        [Property(MaxTest = 200)]
        public Property ReorderNode_OrderEqualsIndex()
        {
            return Prop.ForAll(
                Arb.From(Gen.Choose(2, 8)),          // nodeCount: 2..8
                Arb.From(Gen.Choose(0, 7)),           // oldIndex (clamped below)
                Arb.From(Gen.Choose(0, 7)),           // newIndex (clamped below)
                (nodeCount, rawOld, rawNew) =>
                {
                    var vm = CreateVm();
                    PopulateNodes(vm, nodeCount);

                    int oldIdx = rawOld % nodeCount;
                    int newIdx = rawNew % nodeCount;

                    vm.ReorderOperatorCommand.Execute(new PipelineReorderArgs
                    {
                        OldIndex = oldIdx,
                        NewIndex = newIdx
                    });

                    return vm.PipelineNodes
                        .Select((n, i) => n.Order == i)
                        .All(x => x);
                });
        }

        // ── Property 7：RemoveOperator 后节点不存在且 Order 连续 ───────

        /// <summary>
        /// 任务 7.2：对任意节点，RemoveOperator 后节点不存在且剩余 Order 连续
        /// </summary>
        [Property(MaxTest = 200)]
        public Property RemoveOperator_NodeAbsentAndOrderContinuous()
        {
            return Prop.ForAll(
                Arb.From(Gen.Choose(1, 8)),
                Arb.From(Gen.Choose(0, 7)),
                (nodeCount, rawIdx) =>
                {
                    var vm = CreateVm();
                    PopulateNodes(vm, nodeCount);

                    int idx = rawIdx % nodeCount;
                    var target = vm.PipelineNodes[idx];

                    vm.RemoveOperatorCommand.Execute(target);

                    bool notPresent = !vm.PipelineNodes.Contains(target);
                    bool orderContinuous = vm.PipelineNodes
                        .Select((n, i) => n.Order == i)
                        .All(x => x);

                    return notPresent && orderContinuous;
                });
        }

        // ── Property 3：AddOperator 后末尾节点正确 ────────────────────

        /// <summary>
        /// 任务 7.3：对任意已注册 Key，AddOperator 后末尾节点 OperatorKey 和 Order 正确
        /// </summary>
        [Property(MaxTest = 200)]
        public Property AddOperator_LastNodeCorrect()
        {
            return Prop.ForAll(
                Arb.From(Gen.Elements(Keys)),                       // 随机选一个合法 Key
                Arb.From(Gen.Choose(0, 5)),                         // 已有节点数
                (key, existingCount) =>
                {
                    var vm = CreateVm();
                    PopulateNodes(vm, existingCount);

                    int expectedOrder = vm.PipelineNodes.Count;
                    vm.AddOperatorCommand.Execute(key);

                    var last = vm.PipelineNodes.LastOrDefault();
                    return last != null
                        && last.OperatorKey == key
                        && last.Order == expectedOrder;
                });
        }

        // ── Property 5：未注册 Key 不改变节点数 ──────────────────────

        /// <summary>
        /// 任务 7.4：对任意未注册 Key，AddOperator 后 PipelineNodes 长度不变
        /// </summary>
        [Property(MaxTest = 200)]
        public Property AddOperator_UnregisteredKey_LengthUnchanged()
        {
            return Prop.ForAll(
                Arb.From(Gen.Choose(0, 5)),
                (existingCount) =>
                {
                    var vm = CreateVm();
                    PopulateNodes(vm, existingCount);

                    int before = vm.PipelineNodes.Count;
                    vm.AddOperatorCommand.Execute("UNREGISTERED_KEY_XYZ");

                    return vm.PipelineNodes.Count == before;
                });
        }

        // ── Property 14：Save/Load 往返一致性 ────────────────────────

        /// <summary>
        /// 任务 7.5：对任意合法 PipelineModel，Save 后 Load 得到字段一致的对象
        /// </summary>
        [Property(MaxTest = 100)]
        public Property SaveLoad_RoundTrip_FieldsConsistent()
        {
            return Prop.ForAll(
                Arb.From(Gen.Choose(0, 5)),
                (nodeCount) =>
                {
                    var tempDir = Path.Combine(Path.GetTempPath(), $"PBT_{Guid.NewGuid():N}");
                    Directory.CreateDirectory(tempDir);

                    try
                    {
                        var mockImageSvc = TestHelpers.CreateMockImageService(new[] { "Blur", "Sharpen" });
                        var svc = new PipelinePersistenceService(mockImageSvc.Object, tempDir);

                        var model = new PipelineModel { Name = $"Pipeline_{nodeCount}" };
                        for (int i = 0; i < nodeCount; i++)
                            model.Nodes.Add(new PipelineNodeModel
                            {
                                OperatorKey = i % 2 == 0 ? "Blur" : "Sharpen",
                                Order = i
                            });

                        var path = Path.Combine(tempDir, "test.pipeline.json");
                        svc.SaveAsync(model, path).GetAwaiter().GetResult();
                        var loaded = svc.LoadAsync(path).GetAwaiter().GetResult();

                        return loaded.Name == model.Name
                            && loaded.Nodes.Count == model.Nodes.Count
                            && loaded.Nodes.Select((n, i) => n.Order == i).All(x => x);
                    }
                    finally
                    {
                        if (Directory.Exists(tempDir))
                            Directory.Delete(tempDir, recursive: true);
                    }
                });
        }

        // ── Property 12：取消令牌触发 OperationCanceledException ──────

        /// <summary>
        /// 任务 7.6：对任意流水线，取消令牌触发后抛出 OperationCanceledException
        /// </summary>
        [Property(MaxTest = 100)]
        public Property CancelToken_AlwaysThrowsOperationCanceled()
        {
            return Prop.ForAll(
                Arb.From(Gen.Choose(1, 5)),
                (nodeCount) =>
                {
                    var mockImageSvc = TestHelpers.CreateMockImageService();
                    mockImageSvc.Setup(s => s.ProcessImageAsync(
                            It.IsAny<System.Windows.Media.Imaging.BitmapSource>(),
                            It.IsAny<string>(),
                            It.IsAny<IDictionary<string, object>>(),
                            It.IsAny<IProgress<double>>(),
                            It.IsAny<CancellationToken>()))
                        .Returns<System.Windows.Media.Imaging.BitmapSource, string,
                            IDictionary<string, object>, IProgress<double>, CancellationToken>(
                            (src, _, _, _, ct) =>
                            {
                                ct.ThrowIfCancellationRequested();
                                return Task.FromResult(src);
                            });

                    var svc = new PipelineExecutionService(mockImageSvc.Object);
                    var bitmap = TestHelpers.CreateTestBitmap();

                    using var cts = new CancellationTokenSource();
                    cts.Cancel();

                    var nodes = Enumerable.Range(0, nodeCount)
                        .Select(i => new PipelineNodeViewModel(Keys[i % Keys.Length], "N") { Order = i })
                        .ToList();

                    try
                    {
                        svc.ExecutePipelineAsync(nodes, bitmap, cancellationToken: cts.Token)
                           .GetAwaiter().GetResult();
                        return false; // 应该抛出异常
                    }
                    catch (OperationCanceledException)
                    {
                        return true;
                    }
                    catch
                    {
                        return false;
                    }
                });
        }

        // ── Property 2：FilteredOperators 每项均包含搜索文本 ─────────

        /// <summary>
        /// 任务 7.7：对任意搜索文本，FilteredOperators 中每个算子 DisplayName 均包含该文本
        /// </summary>
        [Property(MaxTest = 200)]
        public Property FilteredOperators_AllContainSearchText()
        {
            // 生成非空、非空白的搜索字符串（限制长度避免无意义测试）
            var searchGen = Gen.Elements("Blur", "Sharpen", "Threshold", "Denoise", "Contrast",
                "bl", "sh", "th", "de", "co", "a", "e", "r");

            return Prop.ForAll(
                Arb.From(searchGen),
                (searchText) =>
                {
                    var mockImageSvc = TestHelpers.CreateMockImageService(
                        new[] { "Blur", "Sharpen", "Threshold", "Denoise", "Contrast" });
                    var vm = new OperatorToolboxViewModel(mockImageSvc.Object);
                    vm.SearchText = searchText;

                    return vm.FilteredOperators.All(op =>
                        op.DisplayName.Contains(searchText, StringComparison.OrdinalIgnoreCase));
                });
        }
    }
}
