using Moq;
using Xunit;
using System.Windows.Media.Imaging;
using XplorePlane.Services;
using XplorePlane.ViewModels;
using XplorePlane.Tests.Helpers;
using System.Threading.Tasks;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading;

namespace XplorePlane.Tests.Pipeline
{
    /// <summary>
    /// 测试 PipelineExecutionService（任务 6.6）
    /// </summary>
    public class PipelineExecutionServiceTests
    {
        private readonly Mock<IImageProcessingService> _mockImageSvc;
        private readonly PipelineExecutionService _svc;
        private readonly BitmapSource _testBitmap;

        public PipelineExecutionServiceTests()
        {
            _mockImageSvc = TestHelpers.CreateMockImageService();
            _svc = new PipelineExecutionService(_mockImageSvc.Object);
            _testBitmap = TestHelpers.CreateTestBitmap();
        }

        private static PipelineNodeViewModel MakeNode(string key, int order = 0, bool enabled = true)
        {
            var node = new PipelineNodeViewModel(key, key + "算子") { Order = order, IsEnabled = enabled };
            return node;
        }

        // ── 空流水线 ──────────────────────────────────────────────────

        [Fact]
        public async Task EmptyPipeline_ReturnsSourceImage()
        {
            var result = await _svc.ExecutePipelineAsync(
                Enumerable.Empty<PipelineNodeViewModel>(), _testBitmap);

            Assert.Same(_testBitmap, result);
        }

        [Fact]
        public async Task NullNodes_ReturnsSourceImage()
        {
            var result = await _svc.ExecutePipelineAsync(null!, _testBitmap);

            Assert.Same(_testBitmap, result);
        }

        [Fact]
        public async Task AllDisabledNodes_ReturnsSourceImage()
        {
            var nodes = new[] { MakeNode("Blur", 0, enabled: false) };
            var result = await _svc.ExecutePipelineAsync(nodes, _testBitmap);

            Assert.Same(_testBitmap, result);
        }

        [Fact]
        public async Task NullSource_ThrowsArgumentNullException()
        {
            await Assert.ThrowsAsync<ArgumentNullException>(
                () => _svc.ExecutePipelineAsync(Enumerable.Empty<PipelineNodeViewModel>(), null!));
        }

        // ── 取消令牌 ──────────────────────────────────────────────────

        [Fact]
        public async Task CancelledToken_ThrowsOperationCanceledException()
        {
            // 让 ProcessImageAsync 在执行时检查取消令牌
            _mockImageSvc.Setup(s => s.ProcessImageAsync(
                    It.IsAny<BitmapSource>(),
                    It.IsAny<string>(),
                    It.IsAny<IDictionary<string, object>>(),
                    It.IsAny<IProgress<double>>(),
                    It.IsAny<CancellationToken>()))
                .Returns<BitmapSource, string, IDictionary<string, object>, IProgress<double>, CancellationToken>(
                    (src, _, _, _, ct) =>
                    {
                        ct.ThrowIfCancellationRequested();
                        return Task.FromResult(src);
                    });

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

            var nodes = new[] { MakeNode("Blur", 0) };

            await Assert.ThrowsAsync<OperationCanceledException>(
                () => _svc.ExecutePipelineAsync(nodes, _testBitmap, cancellationToken: cts.Token));
        }

        [Fact]
        public async Task PreCancelledToken_ThrowsBeforeExecution()
        {
            using var cts = new CancellationTokenSource();
            cts.Cancel();

            var nodes = new[] { MakeNode("Blur", 0) };

            await Assert.ThrowsAsync<OperationCanceledException>(
                () => _svc.ExecutePipelineAsync(nodes, _testBitmap, cancellationToken: cts.Token));

            // ProcessImageAsync 不应被调用
            _mockImageSvc.Verify(s => s.ProcessImageAsync(
                It.IsAny<BitmapSource>(),
                It.IsAny<string>(),
                It.IsAny<IDictionary<string, object>>(),
                It.IsAny<IProgress<double>>(),
                It.IsAny<CancellationToken>()), Times.Never);
        }

        // ── 节点失败包装 ──────────────────────────────────────────────

        [Fact]
        public async Task NodeThrows_WrappedAsPipelineExecutionException()
        {
            _mockImageSvc.Setup(s => s.ProcessImageAsync(
                    It.IsAny<BitmapSource>(),
                    "Blur",
                    It.IsAny<IDictionary<string, object>>(),
                    It.IsAny<IProgress<double>>(),
                    It.IsAny<CancellationToken>()))
                .ThrowsAsync(new InvalidOperationException("处理失败"));

            var nodes = new[] { MakeNode("Blur", 0) };

            var ex = await Assert.ThrowsAsync<PipelineExecutionException>(
                () => _svc.ExecutePipelineAsync(nodes, _testBitmap));

            Assert.Equal("Blur", ex.FailedOperatorKey);
            Assert.Equal(0, ex.FailedNodeOrder);
        }

        [Fact]
        public async Task NodeReturnsNull_ThrowsPipelineExecutionException()
        {
            _mockImageSvc.Setup(s => s.ProcessImageAsync(
                    It.IsAny<BitmapSource>(),
                    It.IsAny<string>(),
                    It.IsAny<IDictionary<string, object>>(),
                    It.IsAny<IProgress<double>>(),
                    It.IsAny<CancellationToken>()))
                .ReturnsAsync((BitmapSource?)null);

            var nodes = new[] { MakeNode("Blur", 0) };

            await Assert.ThrowsAsync<PipelineExecutionException>(
                () => _svc.ExecutePipelineAsync(nodes, _testBitmap));
        }

        // ── 进度回调 ──────────────────────────────────────────────────

        [Fact]
        public async Task Progress_ReportedForEachNode()
        {
            var nodes = new[]
            {
                MakeNode("Blur", 0),
                MakeNode("Sharpen", 1)
            };

            var reports = new List<PipelineProgress>();
            var progress = new Progress<PipelineProgress>(p => reports.Add(p));

            await _svc.ExecutePipelineAsync(nodes, _testBitmap, progress);

            // 等待 Progress 回调（Progress<T> 是异步的）
            await Task.Delay(50);

            Assert.Equal(2, reports.Count);
            Assert.Equal(1, reports[0].CurrentStep);
            Assert.Equal(2, reports[1].CurrentStep);
        }

        // ── 节点按 Order 排序执行 ─────────────────────────────────────

        [Fact]
        public async Task Nodes_ExecutedInOrderAscending()
        {
            var executionOrder = new List<string>();
            _mockImageSvc.Setup(s => s.ProcessImageAsync(
                    It.IsAny<BitmapSource>(),
                    It.IsAny<string>(),
                    It.IsAny<IDictionary<string, object>>(),
                    It.IsAny<IProgress<double>>(),
                    It.IsAny<CancellationToken>()))
                .Returns<BitmapSource, string, IDictionary<string, object>, IProgress<double>, CancellationToken>(
                    (src, key, _, _, _) =>
                    {
                        executionOrder.Add(key);
                        return Task.FromResult(src);
                    });

            // 故意乱序传入
            var nodes = new[]
            {
                MakeNode("Threshold", 2),
                MakeNode("Blur", 0),
                MakeNode("Sharpen", 1)
            };

            await _svc.ExecutePipelineAsync(nodes, _testBitmap);

            Assert.Equal(new[] { "Blur", "Sharpen", "Threshold" }, executionOrder);
        }
    }
}
