using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;

namespace StandardScene.Fass2Simulator
{
    public partial class MainWindow : Window
    {
        private const int MaxLogLines = 800;
        private readonly StringBuilder _logBuffer = new StringBuilder();
        private int _logLineCount;
        private Fass2SimConfig _config;
        private Fass2SimRuntime _runtime;
        private readonly DispatcherTimer _uiTimer;
        private readonly Queue<string> _pendingLogLines = new Queue<string>();
        private readonly object _logQueueLock = new object();
        private bool _logFlushScheduled;

        public MainWindow()
        {
            InitializeComponent();
            _config = Fass2SimBootstrap.LoadConfig();
            Fass2SimFileLogger.Configure(_config);
            LoadConfigToUi(_config);
            SetRunningUi(false);
            UpdateLogFilePathText();

            Fass2SimLog.MessageWritten += OnLogMessage;
            _uiTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(250) };
            _uiTimer.Tick += (_, __) => RefreshStatus();
            _uiTimer.Start();
            RefreshStatus();

            Closed += (_, __) =>
            {
                Fass2SimLog.MessageWritten -= OnLogMessage;
                _runtime?.Dispose();
                Fass2SimFileLogger.Shutdown();
            };

            AppendLog("就绪。配置参数后点击「启动模拟」开始联调。完整日志将写入 logs 目录。");
        }

        private void StartButton_Click(object sender, RoutedEventArgs e)
        {
            if (_runtime != null && _runtime.IsRunning)
            {
                return;
            }

            try
            {
                _config = ReadConfigFromUi();
                Fass2SimFileLogger.Configure(_config);
                Fass2SimFileLogger.BeginSession(_config);
                UpdateLogFilePathText();
                _runtime?.Dispose();
                _runtime = Fass2SimBootstrap.CreateRuntime(_config);
                _runtime.Start();
                SetRunningUi(true);
                RefreshStatus();
            }
            catch (Exception ex)
            {
                MessageBox.Show(this, ex.Message, "启动失败", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void StopButton_Click(object sender, RoutedEventArgs e)
        {
            if (_runtime == null)
            {
                return;
            }

            _runtime.Stop();
            SetRunningUi(false);
            RefreshStatus();
        }

        private void ClearLogButton_Click(object sender, RoutedEventArgs e)
        {
            _logBuffer.Clear();
            _logLineCount = 0;
            LogTextBox.Clear();
        }

        private void OpenLogFolderButton_Click(object sender, RoutedEventArgs e)
        {
            var directory = Fass2SimFileLogger.GetLogDirectory();
            Directory.CreateDirectory(directory);
            Process.Start(new ProcessStartInfo
            {
                FileName = directory,
                UseShellExecute = true
            });
        }

        private void UpdateLogFilePathText()
        {
            if (!_config.EnableFileLog)
            {
                LogFilePathText.Text = "文件日志已关闭（appsettings.json: EnableFileLog=false）";
                return;
            }

            var path = Fass2SimFileLogger.CurrentFilePath;
            if (string.IsNullOrEmpty(path))
            {
                LogFilePathText.Text = $"日志目录: {Fass2SimFileLogger.GetLogDirectory()}（启动模拟后生成会话日志）";
                return;
            }

            LogFilePathText.Text = $"日志文件: {path}";
        }

        private void OnLogMessage(string message)
        {
            lock (_logQueueLock)
            {
                _pendingLogLines.Enqueue(message);
            }

            if (_logFlushScheduled)
            {
                return;
            }

            _logFlushScheduled = true;
            Dispatcher.BeginInvoke(new Action(FlushPendingLogs), DispatcherPriority.Background);
        }

        private void FlushPendingLogs()
        {
            _logFlushScheduled = false;
            List<string> batch;
            lock (_logQueueLock)
            {
                if (_pendingLogLines.Count == 0)
                {
                    return;
                }

                batch = new List<string>(_pendingLogLines.Count);
                while (_pendingLogLines.Count > 0)
                {
                    batch.Add(_pendingLogLines.Dequeue());
                }
            }

            foreach (var line in batch)
            {
                AppendLog(line);
            }
        }

        private void AppendLog(string message)
        {
            if (string.IsNullOrEmpty(message))
            {
                return;
            }

            _logBuffer.AppendLine(message);
            _logLineCount++;
            while (_logLineCount > MaxLogLines)
            {
                var bufferText = _logBuffer.ToString();
                var firstBreak = bufferText.IndexOf('\n');
                if (firstBreak < 0)
                {
                    _logBuffer.Clear();
                    _logLineCount = 0;
                    break;
                }

                _logBuffer.Remove(0, firstBreak + 1);
                _logLineCount--;
            }

            LogTextBox.Text = _logBuffer.ToString();
            LogTextBox.CaretIndex = LogTextBox.Text.Length;
            LogTextBox.ScrollToEnd();
        }

        private void RefreshStatus()
        {
            if (_runtime == null || !_runtime.IsRunning)
            {
                if (_runtime == null)
                {
                    ApplyIdleStatus(
                        _config.InitialNode.ToString(),
                        "-",
                        "0",
                        Fass2SimProtocol.StateText(_config.InitialState),
                        "0",
                        "-",
                        "0",
                        $"{_config.BatteryCharge}%",
                        "-",
                        "0",
                        "0",
                        "0",
                        "无");
                }

                return;
            }

            var snap = _runtime.Vehicle.Snapshot();
            var expected = _runtime.Actions.ExpectedStation;
            var pending = expected == null
                ? "-"
                : Fass2SimActionResolver.DescribePending(expected, snap);
            if (string.IsNullOrEmpty(pending))
            {
                pending = "-";
            }

            var distanceText = snap.Distance.ToString();
            var targetNodeText = "-";
            if (_runtime.Motion.TryGetSegmentStatus(out var targetNode, out var segmentLengthMm, out var progressMm))
            {
                targetNodeText = targetNode.ToString();
                distanceText = $"{progressMm} / {segmentLengthMm}";
            }

            ApplyIdleStatus(
                snap.Node.ToString(),
                targetNodeText,
                distanceText,
                Fass2SimProtocol.StateText(snap.State),
                snap.Task.ToString(),
                snap.StartStop.ToString(),
                snap.Lift.ToString(),
                $"{snap.BatteryCharge}%",
                pending,
                snap.StateReportsSent.ToString(),
                snap.CommandsReceived.ToString(),
                snap.AckReceived.ToString(),
                snap.LastAckUtc == DateTime.MinValue
                    ? "无"
                    : $"{(DateTime.UtcNow - snap.LastAckUtc).TotalSeconds:F1}s 前");
        }

        private void ApplyIdleStatus(
            string node,
            string targetNode,
            string distance,
            string state,
            string task,
            string startStop,
            string lift,
            string battery,
            string pending,
            string reports,
            string commands,
            string ack,
            string lastAck)
        {
            NodeText.Text = node;
            TargetNodeText.Text = targetNode;
            DistanceText.Text = distance;
            StateText.Text = state;
            TaskText.Text = task;
            StartStopText.Text = startStop;
            LiftText.Text = lift;
            BatteryText.Text = battery;
            PendingText.Text = pending;
            ReportsText.Text = reports;
            CommandsText.Text = commands;
            AckText.Text = ack;
            LastAckText.Text = lastAck;
        }

        private void LoadConfigToUi(Fass2SimConfig config)
        {
            VehicleCodeBox.Text = config.VehicleCode.ToString();
            ListenAddressBox.Text = config.VehicleListenAddress;
            ListenPortBox.Text = config.VehicleListenPort.ToString();
            SchedulerHostBox.Text = config.SchedulerHost;
            SchedulerPortBox.Text = config.SchedulerListenPort.ToString();
            ReportIntervalBox.Text = config.ReportIntervalMs.ToString();
            InitialNodeBox.Text = config.InitialNode.ToString();
            AutoContinueBox.IsChecked = config.AutoContinueOnPass;
            AutoCompleteBox.IsChecked = config.AutoCompleteStationActions;
            LogRawFramesBox.IsChecked = config.LogRawFrames;
        }

        private Fass2SimConfig ReadConfigFromUi()
        {
            return new Fass2SimConfig
            {
                VehicleCode = ParseUShort(VehicleCodeBox.Text, "车号"),
                VehicleListenAddress = ListenAddressBox.Text?.Trim() ?? "0.0.0.0",
                VehicleListenPort = ParseInt(ListenPortBox.Text, "车体监听端口"),
                SchedulerHost = SchedulerHostBox.Text?.Trim() ?? "127.0.0.1",
                SchedulerListenPort = ParseInt(SchedulerPortBox.Text, "调度监听端口"),
                ReportIntervalMs = ParseInt(ReportIntervalBox.Text, "上报周期"),
                InitialNode = ParseUShort(InitialNodeBox.Text, "初始节点"),
                InitialState = _config?.InitialState ?? 0,
                BatteryCharge = _config?.BatteryCharge ?? 100,
                CarLength = _config?.CarLength ?? 1200,
                CarWidth = _config?.CarWidth ?? 800,
                DefaultSpeed = _config?.DefaultSpeed ?? 500,
                DefaultSegmentDistance = _config?.DefaultSegmentDistance ?? 1000,
                SecondsPerSegment = _config?.SecondsPerSegment ?? 2,
                ActionDelayMs = _config?.ActionDelayMs ?? 1000,
                NodeProfilesPath = _config?.NodeProfilesPath ?? "sim-nodes.json",
                AutoContinueOnPass = AutoContinueBox.IsChecked == true,
                AutoCompleteStationActions = AutoCompleteBox.IsChecked == true,
                LogRawFrames = LogRawFramesBox.IsChecked == true,
                EnableFileLog = _config?.EnableFileLog ?? true,
                LogDirectory = _config?.LogDirectory ?? "logs"
            };
        }

        private void SetRunningUi(bool running)
        {
            StartButton.IsEnabled = !running;
            StopButton.IsEnabled = running;
            VehicleCodeBox.IsEnabled = !running;
            ListenAddressBox.IsEnabled = !running;
            ListenPortBox.IsEnabled = !running;
            SchedulerHostBox.IsEnabled = !running;
            SchedulerPortBox.IsEnabled = !running;
            ReportIntervalBox.IsEnabled = !running;
            InitialNodeBox.IsEnabled = !running;
            AutoContinueBox.IsEnabled = !running;
            AutoCompleteBox.IsEnabled = !running;
            LogRawFramesBox.IsEnabled = !running;

            RunStateText.Text = running ? "运行中" : "已停止";
            RunStateText.Foreground = running
                ? new SolidColorBrush(Color.FromRgb(22, 163, 74))
                : new SolidColorBrush(Color.FromRgb(107, 114, 128));
        }

        private static ushort ParseUShort(string text, string fieldName)
        {
            if (!ushort.TryParse(text?.Trim(), out var value))
            {
                throw new InvalidOperationException($"{fieldName} 无效：{text}");
            }

            return value;
        }

        private static int ParseInt(string text, string fieldName)
        {
            if (!int.TryParse(text?.Trim(), out var value))
            {
                throw new InvalidOperationException($"{fieldName} 无效：{text}");
            }

            return value;
        }
    }
}
