using MainShell.Common;
using MainShell.Hardware;
using MainShell.Log;
using MainShell.Motion;
using MainShell.Vision;
using MwFramework.Device;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using System.Windows;
using CameraBufferReceivedEventArgs = MwFramework.Device.Model.CameraBufferReceivedEventArgs;

namespace MainShell.Process
{
    public class PlaceholderWaferCaptureExecutor : IWaferCaptureExecutor
    {
        private sealed class PendingCaptureRequest
        {
            public long SequenceId { get; set; }

            public Point TriggerPoint { get; set; }

            public DateTime TriggerTime { get; set; }
        }

        private sealed class EventDrivenCaptureSession
        {
            private readonly object _syncRoot = new object();
            private readonly Queue<PendingCaptureRequest> _pendingRequests = new Queue<PendingCaptureRequest>();
            private readonly List<WaferCaptureFrame> _frames = new List<WaferCaptureFrame>();
            private readonly TaskCompletionSource<bool> _completionSource = new TaskCompletionSource<bool>();
            private int _expectedFrameCount;
            private int _receivedFrameCount;

            public void Enqueue(PendingCaptureRequest pendingRequest)
            {
                lock (_syncRoot)
                {
                    _pendingRequests.Enqueue(pendingRequest);
                    _expectedFrameCount++;
                }
            }

            public void AddReceivedFrame(WaferCaptureFrame frame)
            {
                lock (_syncRoot)
                {
                    _frames.Add(frame);
                    _receivedFrameCount++;
                    if (_receivedFrameCount >= _expectedFrameCount && _pendingRequests.Count == 0)
                    {
                        _completionSource.TrySetResult(true);
                    }
                }
            }

            public PendingCaptureRequest Dequeue()
            {
                lock (_syncRoot)
                {
                    if (_pendingRequests.Count == 0)
                    {
                        return null;
                    }

                    return _pendingRequests.Dequeue();
                }
            }

            public Task WaitForCompletionAsync(int timeoutMilliseconds, CancellationToken cancellationToken)
            {
                return WaitForCompletionCoreAsync(timeoutMilliseconds, cancellationToken);
            }

            public List<WaferCaptureFrame> GetFramesOrdered()
            {
                lock (_syncRoot)
                {
                    List<WaferCaptureFrame> frames = new List<WaferCaptureFrame>(_frames);
                    frames.Sort((left, right) => left.SequenceId.CompareTo(right.SequenceId));
                    return frames;
                }
            }

            public int ExpectedFrameCount
            {
                get
                {
                    lock (_syncRoot)
                    {
                        return _expectedFrameCount;
                    }
                }
            }

            public int ReceivedFrameCount
            {
                get
                {
                    lock (_syncRoot)
                    {
                        return _receivedFrameCount;
                    }
                }
            }

            public List<WaferCaptureFrame> DrainPendingAsLost(string errorMessage)
            {
                lock (_syncRoot)
                {
                    List<WaferCaptureFrame> lostFrames = new List<WaferCaptureFrame>();
                    while (_pendingRequests.Count > 0)
                    {
                        PendingCaptureRequest pendingRequest = _pendingRequests.Dequeue();
                        WaferCaptureFrame lostFrame = new WaferCaptureFrame();
                        lostFrame.SequenceId = pendingRequest.SequenceId;
                        lostFrame.TriggerPoint = pendingRequest.TriggerPoint;
                        lostFrame.FrameId = Guid.NewGuid().ToString("N");
                        lostFrame.TriggerTime = pendingRequest.TriggerTime;
                        lostFrame.ReceiveTime = DateTime.Now;
                        lostFrame.IsLostFrame = true;
                        lostFrame.CaptureErrorMessage = errorMessage;
                        _frames.Add(lostFrame);
                        lostFrames.Add(lostFrame);
                    }

                    if (_receivedFrameCount >= _expectedFrameCount && _pendingRequests.Count == 0)
                    {
                        _completionSource.TrySetResult(true);
                    }

                    return lostFrames;
                }
            }

            private async Task WaitForCompletionCoreAsync(int timeoutMilliseconds, CancellationToken cancellationToken)
            {
                using (CancellationTokenRegistration registration = cancellationToken.Register(() => _completionSource.TrySetCanceled()))
                {
                    Task completedTask = await Task.WhenAny(_completionSource.Task, Task.Delay(timeoutMilliseconds, cancellationToken)).ConfigureAwait(false);
                    if (completedTask != _completionSource.Task)
                    {
                        throw new TimeoutException(string.Format(CultureInfo.InvariantCulture, "晶圆定位：等待相机图像超时，超时时间 {0} ms。", timeoutMilliseconds));
                    }

                    await _completionSource.Task.ConfigureAwait(false);
                }
            }
        }

        private const int DefaultMotionSettlingMilliseconds = 20;
        private readonly IWaferMachineAdapter _waferMachineAdapter;
        private readonly SafeAxisMotion _safeAxisMotion;
        private readonly HardwareManager _hardwareManager;

        public PlaceholderWaferCaptureExecutor(
            IWaferMachineAdapter waferMachineAdapter,
            SafeAxisMotion safeAxisMotion,
            HardwareManager hardwareManager)
        {
            _waferMachineAdapter = waferMachineAdapter ?? throw new ArgumentNullException(nameof(waferMachineAdapter));
            _safeAxisMotion = safeAxisMotion ?? throw new ArgumentNullException(nameof(safeAxisMotion));
            _hardwareManager = hardwareManager ?? throw new ArgumentNullException(nameof(hardwareManager));
        }

        public async Task<WaferScanExecutionSummary> ExecuteAsync(WaferDiePositionContext context, ChannelWriter<WaferCaptureFrame> frameWriter, CancellationToken cancellationToken)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (frameWriter == null)
            {
                throw new ArgumentNullException(nameof(frameWriter));
            }

            List<Point> pathPoints = ResolvePathPoints(context);
            WaferScanExecutionSummary summary = new WaferScanExecutionSummary();
            summary.PlannedPointCount = pathPoints.Count;
            summary.ProducedFrameCount = 0;
            summary.ConsumedFrameCount = 0;
            summary.RecognitionSuccessCount = 0;
            summary.RecognitionFailureCount = 0;
            summary.LastErrorMessage = null;

            if (pathPoints.Count == 0)
            {
                LogManager.LogProcessInfo("晶圆定位：未生成扫描点，跳过运动与采图。");
                return summary;
            }

            EnsureCaptureFrameList(context);

            CameraType cameraSource = _waferMachineAdapter.GetCameraSource(context);
            CameraCaptureOptions captureOptions = _waferMachineAdapter.CreateSoftTriggerCaptureOptions(context) ?? CameraCaptureOptions.CreateSoftTrigger(5000);
            MwCamera camera = _waferMachineAdapter.GetCamera(context);
            IAxis axisX = _hardwareManager.Axis_PHS_X1;
            IAxis axisY = _hardwareManager.Axis_Stage_Y3;
            EnsureAxes(axisX, axisY);
            EnsureCamera(camera, cameraSource);

            int motionSettlingMilliseconds = ResolveMotionSettlingMilliseconds(captureOptions);
            int exposureDelayMilliseconds = ResolveExposureDelayMilliseconds(_waferMachineAdapter.GetExposureTimeMilliseconds(context));
            int completionTimeoutMilliseconds = ResolveCompletionTimeoutMilliseconds(captureOptions, exposureDelayMilliseconds, pathPoints.Count);
            EventDrivenCaptureSession captureSession = new EventDrivenCaptureSession();

            EventHandler<CameraBufferReceivedEventArgs> cameraBufferHandler = (sender, eventArgs) =>
            {
                HandleCameraBufferReceived(captureSession, frameWriter, eventArgs, cancellationToken);
            };

            PrepareCameraForSoftTrigger(camera);
            camera.CameraBufferReceived += cameraBufferHandler;

            try
            {
                for (int pointIndex = 0; pointIndex < pathPoints.Count; pointIndex++)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    Point targetPoint = pathPoints[pointIndex];
                    LogManager.LogProcessInfo(
                        string.Format(
                            CultureInfo.InvariantCulture,
                            "晶圆定位：执行扫描点 {0}/{1}，X={2:F3}，Y={3:F3}。",
                            pointIndex + 1,
                            pathPoints.Count,
                            targetPoint.X,
                            targetPoint.Y));

                    try
                    {
                        await MoveToPointAsync(axisX, axisY, targetPoint, cancellationToken).ConfigureAwait(false);
                        await Task.Delay(motionSettlingMilliseconds, cancellationToken).ConfigureAwait(false);

                        PendingCaptureRequest pendingCaptureRequest = new PendingCaptureRequest();
                        pendingCaptureRequest.SequenceId = pointIndex + 1;
                        pendingCaptureRequest.TriggerPoint = targetPoint;
                        pendingCaptureRequest.TriggerTime = DateTime.Now;
                        captureSession.Enqueue(pendingCaptureRequest);

                        TriggerSoftCapture(camera, pendingCaptureRequest, exposureDelayMilliseconds);
                        await Task.Delay(exposureDelayMilliseconds, cancellationToken).ConfigureAwait(false);
                    }
                    catch (OperationCanceledException)
                    {
                        summary.LastErrorMessage = string.Format(
                            CultureInfo.InvariantCulture,
                            "晶圆定位：在扫描点 {0}/{1} 处取消流程。",
                            pointIndex + 1,
                            pathPoints.Count);
                        context.ErrorMessage = summary.LastErrorMessage;
                        throw;
                    }
                    catch (Exception ex)
                    {
                        summary.LastErrorMessage = string.Format(
                            CultureInfo.InvariantCulture,
                            "晶圆定位：扫描点 {0}/{1} 执行失败，X={2:F3}，Y={3:F3}。{4}",
                            pointIndex + 1,
                            pathPoints.Count,
                            targetPoint.X,
                            targetPoint.Y,
                            ex.Message);
                        context.ErrorMessage = summary.LastErrorMessage;
                        LogManager.LogProcessError(summary.LastErrorMessage);
                        throw new InvalidOperationException(summary.LastErrorMessage, ex);
                    }
                }

                await captureSession.WaitForCompletionAsync(completionTimeoutMilliseconds, cancellationToken).ConfigureAwait(false);
            }
            catch (TimeoutException ex)
            {
                List<WaferCaptureFrame> lostFrames = captureSession.DrainPendingAsLost(ex.Message);
                summary.LastErrorMessage = ex.Message;
                context.ErrorMessage = ex.Message;
                foreach (WaferCaptureFrame lostFrame in lostFrames)
                {
                    context.CaptureFrames.Add(lostFrame);
                    PublishCapturedFrame(lostFrame, frameWriter, cancellationToken);
                }

                throw new InvalidOperationException(ex.Message, ex);
            }
            finally
            {
                camera.CameraBufferReceived -= cameraBufferHandler;
            }

            List<WaferCaptureFrame> receivedFrames = captureSession.GetFramesOrdered();
            context.CaptureFrames.Clear();
            foreach (WaferCaptureFrame receivedFrame in receivedFrames)
            {
                context.CaptureFrames.Add(receivedFrame);
            }

            summary.ProducedFrameCount = context.CaptureFrames.Count;
            summary.ConsumedFrameCount = context.CaptureFrames.Count;
            if (summary.ProducedFrameCount != summary.PlannedPointCount)
            {
                summary.LastErrorMessage = string.Format(
                    CultureInfo.InvariantCulture,
                        "晶圆定位：采图帧数量不一致。计划={0}，实际接收={1}。",
                    summary.PlannedPointCount,
                    summary.ProducedFrameCount);
                context.ErrorMessage = summary.LastErrorMessage;
                throw new InvalidOperationException(summary.LastErrorMessage);
            }

            LogManager.LogProcessInfo(
                string.Format(
                    CultureInfo.InvariantCulture,
                    "晶圆定位：扫描执行完成。计划帧数={0}，采图帧数={1}。",
                    summary.PlannedPointCount,
                    summary.ProducedFrameCount));

            return summary;
        }

        private async Task MoveToPointAsync(IAxis axisX, IAxis axisY, Point targetPoint, CancellationToken cancellationToken)
        {
            MotionMoveRequest requestX = MotionMoveRequest.ForAxis(axisX, targetPoint.X, source: "DiePosition", tags: new string[] { "WaferScan", "TopCamera" });
            MotionMoveRequest requestY = MotionMoveRequest.ForAxis(axisY, targetPoint.Y, source: "DiePosition", tags: new string[] { "WaferScan", "TopCamera" });
            MotionBatchResult motionResult = await _safeAxisMotion.SafeMoveAsync(cancellationToken, requestX, requestY).ConfigureAwait(false);
            motionResult.EnsureSuccess();
        }

        private void HandleCameraBufferReceived(
            EventDrivenCaptureSession captureSession,
            ChannelWriter<WaferCaptureFrame> frameWriter,
            CameraBufferReceivedEventArgs eventArgs,
            CancellationToken cancellationToken)
        {
            PendingCaptureRequest pendingCaptureRequest = captureSession.Dequeue();
            if (pendingCaptureRequest == null)
            {
                LogManager.LogProcessInfo("晶圆定位：收到异常相机图像，因未找到待处理触发请求，已忽略该图像。");
                return;
            }

            WaferCaptureFrame frame = new WaferCaptureFrame();
            frame.SequenceId = pendingCaptureRequest.SequenceId;
            frame.TriggerPoint = pendingCaptureRequest.TriggerPoint;
            frame.FrameId = Guid.NewGuid().ToString("N");
            frame.TriggerTime = pendingCaptureRequest.TriggerTime;
            frame.ReceiveTime = DateTime.Now;

            if (eventArgs == null || !eventArgs.IsValid || eventArgs.CameraData == null)
            {
                frame.IsLostFrame = true;
                frame.CaptureErrorMessage = eventArgs == null
                    ? "晶圆定位：相机事件参数为空。"
                    : string.Format(
                        CultureInfo.InvariantCulture,
                        "晶圆定位：收到无效相机图像。错误码={0}，描述={1}。",
                        eventArgs.GrabErrorCode,
                        eventArgs.GrabErrorDescription ?? string.Empty);
                captureSession.AddReceivedFrame(frame);
                PublishCapturedFrame(frame, frameWriter, cancellationToken);
                return;
            }

            frame.IsLostFrame = false;
            frame.Image = new MainShell.Models.MxImage(eventArgs.CameraData);
            frame.CaptureErrorMessage = null;
            captureSession.AddReceivedFrame(frame);
            PublishCapturedFrame(frame, frameWriter, cancellationToken);
        }

        private static void PublishCapturedFrame(WaferCaptureFrame frame, ChannelWriter<WaferCaptureFrame> frameWriter, CancellationToken cancellationToken)
        {
            if (frame == null)
            {
                return;
            }

            if (frameWriter == null)
            {
                throw new ArgumentNullException(nameof(frameWriter));
            }

            frameWriter.WriteAsync(frame, cancellationToken).AsTask().GetAwaiter().GetResult();
        }

        private static List<Point> ResolvePathPoints(WaferDiePositionContext context)
        {
            if (context == null || context.ScanPlan == null)
            {
                return new List<Point>();
            }

            IReadOnlyList<Point> candidatePoints = context.ScanPlan.FinalPathPoints ?? context.ScanPlan.PathPoints;
            if (candidatePoints == null || candidatePoints.Count == 0)
            {
                return new List<Point>();
            }

            return new List<Point>(candidatePoints);
        }

        private static void EnsureCaptureFrameList(WaferDiePositionContext context)
        {
            if (context.CaptureFrames == null)
            {
                context.CaptureFrames = new List<WaferCaptureFrame>();
            }
            else
            {
                context.CaptureFrames.Clear();
            }
        }

        private static void EnsureCamera(MwCamera camera, CameraType cameraSource)
        {
            if (camera == null)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "晶圆定位：相机“{0}”不可用。", cameraSource));
            }

            if (!camera.IsOpen)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "晶圆定位：相机“{0}”未打开。", cameraSource));
            }
        }

        private static int ResolveMotionSettlingMilliseconds(CameraCaptureOptions captureOptions)
        {
            if (captureOptions != null && captureOptions.TriggerDelay.HasValue && captureOptions.TriggerDelay.Value > 0d)
            {
                return Math.Max(1, (int)Math.Ceiling(captureOptions.TriggerDelay.Value));
            }

            return DefaultMotionSettlingMilliseconds;
        }

        private static int ResolveExposureDelayMilliseconds(double exposureTimeMilliseconds)
        {
            if (double.IsNaN(exposureTimeMilliseconds) || double.IsInfinity(exposureTimeMilliseconds) || exposureTimeMilliseconds <= 0d)
            {
                return 1;
            }

            return Math.Max(1, (int)Math.Ceiling(exposureTimeMilliseconds));
        }

        private static int ResolveCompletionTimeoutMilliseconds(CameraCaptureOptions captureOptions, int exposureDelayMilliseconds, int pointCount)
        {
            int baseTimeout = captureOptions == null || captureOptions.TimeoutMilliseconds <= 0 ? 5000 : captureOptions.TimeoutMilliseconds;
            int pipelineTimeout = exposureDelayMilliseconds * Math.Max(1, pointCount) + 1000;
            return Math.Max(baseTimeout, pipelineTimeout);
        }

        private static void TriggerSoftCapture(MwCamera camera, PendingCaptureRequest pendingCaptureRequest, int exposureDelayMilliseconds)
        {
            DriverLibResult triggerResult = camera.ExcuteSoftTrigger();
            if (triggerResult != DriverLibResult.DriverLibNoError)
            {
                throw new InvalidOperationException(
                    string.Format(
                        CultureInfo.InvariantCulture,
                        "晶圆定位：软触发失败。序号={0}，X={1:F3}，Y={2:F3}，曝光等待={3} ms，结果={4}。",
                        pendingCaptureRequest.SequenceId,
                        pendingCaptureRequest.TriggerPoint.X,
                        pendingCaptureRequest.TriggerPoint.Y,
                        exposureDelayMilliseconds,
                        triggerResult));
            }
        }

        private static void PrepareCameraForSoftTrigger(MwCamera camera)
        {
            CameraTriggerMode triggerMode = (CameraTriggerMode)Enum.Parse(typeof(CameraTriggerMode), "On", true);
            CameraTriggerSource triggerSource = (CameraTriggerSource)Enum.Parse(typeof(CameraTriggerSource), "Software", true);

            DriverLibResult setModeResult = camera.SetTriggerMode(triggerMode);
            if (setModeResult != DriverLibResult.DriverLibNoError)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "晶圆定位：设置相机触发模式失败。结果={0}。", setModeResult));
            }

            DriverLibResult setSourceResult = camera.SetTriggerSource(triggerSource);
            if (setSourceResult != DriverLibResult.DriverLibNoError)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "晶圆定位：设置相机触发源失败。结果={0}。", setSourceResult));
            }

            if (!camera.IsGrabbing)
            {
                DriverLibResult startGrabbingResult = camera.StartGrabbing();
                if (startGrabbingResult != DriverLibResult.DriverLibNoError && startGrabbingResult != DriverLibResult.DriverLibDeviceAlreadyGrabbing)
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "晶圆定位：启动相机采集失败。结果={0}。", startGrabbingResult));
                }
            }
        }

        private static void EnsureAxes(IAxis axisX, IAxis axisY)
        {
            if (axisX == null || axisY == null)
            {
                throw new InvalidOperationException("晶圆定位：HardwareManager 中未找到顶相机对应的 X/Y 轴。");
            }
        }
    }
}