using System;
using System.Collections.Generic;
using System.Net;

namespace StandardScene.MagCarSimulator
{
    public sealed class MagCarSimVehicleSnapshot
    {
        public string Name { get; set; }
        public ushort VehicleCode { get; set; }
        public int ListenPort { get; set; }
        public int StartSiteId { get; set; }
        public int EndSiteId { get; set; }
        public int IntervalMs { get; set; }
        public int CurrentSiteId { get; set; }
        public ushort Node { get; set; }
        public byte State { get; set; }
        public string StateText { get; set; }
        public bool WaitingRelease { get; set; }
        public bool HoldingForStop { get; set; }
        public bool Looping { get; set; }
        public bool Listening { get; set; }
        public int ClientCount { get; set; }
        public string PathText { get; set; }
    }

    /// <summary>
    /// 单车循环：普通站可叠车；停止点与交管点同时只允许一辆，后车停在上一站并保持运行中；仅 Mag_NeedStop 停车等 0x01。
    /// </summary>
    public sealed class MagCarSimVehicle : IDisposable
    {
        private readonly object _syncRoot = new object();
        private readonly MagCarSimConfig _hostConfig;
        private MagCarSimTcpServer _server;
        private SimpleLiteMap _map;
        private List<int> _loopSiteIds = new List<int>();
        private int _index;
        private DateTime _nextHopAt = DateTime.MaxValue;
        private DateTime _dispatchAt = DateTime.MinValue;
        private DateTime _lastHoldLogAt = DateTime.MinValue;
        private bool _looping;
        private bool _dispatched;
        private bool _waitingRelease;
        private bool _pausedByStopCommand;
        private bool _holdingForStop;

        public MagCarSimVehicle(MagCarSimVehicleConfig config, MagCarSimConfig hostConfig)
        {
            Config = config ?? throw new ArgumentNullException(nameof(config));
            _hostConfig = hostConfig ?? throw new ArgumentNullException(nameof(hostConfig));
            Name = string.IsNullOrWhiteSpace(config.Name) ? $"AGV-{config.VehicleCode}" : config.Name;
            Charge = 100;
            Voltage = 24f;
            Speed = 50;
        }

        public MagCarSimVehicleConfig Config { get; }
        public string Name { get; }
        public ushort VehicleCode => Config.VehicleCode;
        public int ListenPort => Config.ListenPort;
        public int CurrentSiteId { get; private set; }
        public ushort Node { get; private set; }
        public byte State { get; private set; }
        public byte Charge { get; private set; }
        public float Current { get; private set; }
        public float Voltage { get; private set; }
        public byte Speed { get; private set; }
        public ushort Angle { get; private set; }
        public byte Task { get; private set; }
        public byte Lift { get; private set; }
        public byte Roll { get; private set; }
        public bool WaitingRelease => _waitingRelease;
        public bool HoldingForStop => _holdingForStop;
        public bool Looping => _looping;

        public bool Listening => _server != null && _server.IsRunning;

        public void AttachMap(SimpleLiteMap map)
        {
            lock (_syncRoot)
            {
                _map = map;
                if (!_looping)
                {
                    PlaceAtStartUnlocked();
                }
            }
        }

        public void StartListen()
        {
            MagCarSimTcpServer server;
            lock (_syncRoot)
            {
                if (_server != null && _server.IsRunning)
                {
                    return;
                }

                _server?.Dispose();
                var address = string.IsNullOrWhiteSpace(_hostConfig.ListenAddress)
                    ? "0.0.0.0"
                    : _hostConfig.ListenAddress;
                server = new MagCarSimTcpServer(IPAddress.Parse(address), Config.ListenPort, HandleFrame);
                _server = server;
                PlaceAtStartUnlocked();
                Log($"监听 {address}:{Config.ListenPort}，当前站={CurrentSiteId} node={Node} {MagCarSimProtocol.StateText(State)}");
            }

            server.Start();
        }

        public void StopListen()
        {
            MagCarSimTcpServer server;
            lock (_syncRoot)
            {
                server = _server;
                _server = null;
            }

            server?.Dispose();
        }

        public void StartLoop(DateTime dispatchAt)
        {
            lock (_syncRoot)
            {
                if (_map == null)
                {
                    throw new InvalidOperationException($"{Name} 尚未加载地图");
                }

                _loopSiteIds = MapPathFinder.ResolveLoop(_map, Config);
                _index = 0;
                if (Config.StartSiteId > 0)
                {
                    var found = _loopSiteIds.IndexOf(Config.StartSiteId);
                    if (found >= 0)
                    {
                        _index = found;
                    }
                }

                _looping = true;
                _dispatched = false;
                _holdingForStop = false;
                _pausedByStopCommand = false;
                _waitingRelease = false;
                _dispatchAt = dispatchAt;
                PlaceAtStartUnlocked();
                // 待命阶段若起点是停止点，先报运行中，避免交管误放行并占住 pending，导致真发车后长时间不放。
                if (_map != null && _map.NeedStop(CurrentSiteId))
                {
                    State = MagCarSimProtocol.StateRunning;
                    _waitingRelease = false;
                }

                var delayMs = Math.Max(0, (int)(dispatchAt - DateTime.Now).TotalMilliseconds);
                Log($"待命发车，环线 {MapPathFinder.FormatPath(_loopSiteIds, 16)}，间隔 {IntervalOrDefault()}ms" +
                    (delayMs > 50 ? $"，{delayMs}ms 后启动" : "，立即启动"));
            }
        }

        public void StopLoop()
        {
            lock (_syncRoot)
            {
                _looping = false;
                _dispatched = false;
                _waitingRelease = false;
                _holdingForStop = false;
                _pausedByStopCommand = false;
                _nextHopAt = DateTime.MaxValue;
                State = MagCarSimProtocol.StateStopping;
                Log("已停止循环");
            }
        }

        public void Release()
        {
            lock (_syncRoot)
            {
                ApplyStartUnlocked("界面放行");
            }
        }

        public void Tick(DateTime now, Dictionary<int, ushort> occupancy)
        {
            lock (_syncRoot)
            {
                if (!_looping || _pausedByStopCommand)
                {
                    return;
                }

                if (!_dispatched)
                {
                    TryDispatchUnlocked(now, occupancy);
                    return;
                }

                if (_waitingRelease)
                {
                    return;
                }

                if (State != MagCarSimProtocol.StateRunning)
                {
                    return;
                }

                if (now < _nextHopAt)
                {
                    return;
                }

                AdvanceUnlocked(now, occupancy);
            }
        }

        public MagCarSimVehicleSnapshot Snapshot()
        {
            lock (_syncRoot)
            {
                return new MagCarSimVehicleSnapshot
                {
                    Name = Name,
                    VehicleCode = VehicleCode,
                    ListenPort = ListenPort,
                    StartSiteId = Config.StartSiteId,
                    EndSiteId = Config.EndSiteId,
                    IntervalMs = Config.IntervalMs,
                    CurrentSiteId = CurrentSiteId,
                    Node = Node,
                    State = State,
                    StateText = MagCarSimProtocol.StateText(State),
                    WaitingRelease = _waitingRelease,
                    HoldingForStop = _holdingForStop,
                    Looping = _looping,
                    Listening = Listening,
                    ClientCount = _server?.ClientCount ?? 0,
                    PathText = MapPathFinder.FormatPath(_loopSiteIds, 12)
                };
            }
        }

        public void Dispose()
        {
            lock (_syncRoot)
            {
                _looping = false;
            }

            StopListen();
        }

        private byte[] HandleFrame(byte[] request)
        {
            lock (_syncRoot)
            {
                if (!MagCarSimProtocol.TryParse(request, out var parsed))
                {
                    Log("收到非法帧");
                    return BuildStatusUnlocked(MagCarSimProtocol.CmdQueryState);
                }

                if (parsed.VehicleCode != 0 && parsed.VehicleCode != VehicleCode)
                {
                    Log($"帧车号 {parsed.VehicleCode} 与本车 {VehicleCode} 不一致，仍按本车应答");
                }

                if (_hostConfig.LogRawFrames)
                {
                    Log($"RX {MagCarSimProtocol.CommandName(parsed.Command)} {MagCarSimProtocol.ToHex(request)}");
                }
                else if (parsed.Command != MagCarSimProtocol.CmdQueryState || _hostConfig.LogQueries)
                {
                    Log($"RX {MagCarSimProtocol.CommandName(parsed.Command)}");
                }

                switch (parsed.Command)
                {
                    case MagCarSimProtocol.CmdStart:
                        ApplyStartUnlocked("0x01");
                        break;
                    case MagCarSimProtocol.CmdStop:
                        _pausedByStopCommand = true;
                        State = MagCarSimProtocol.StateStopping;
                        Log("收到停止，暂停循环");
                        break;
                    case MagCarSimProtocol.CmdSpeed:
                        Speed = (byte)Math.Clamp((int)parsed.Data0 >> 8, 0, 100);
                        break;
                    case MagCarSimProtocol.CmdAngle:
                        Angle = parsed.Data0;
                        break;
                    case MagCarSimProtocol.CmdLegacyNode:
                    case MagCarSimProtocol.CmdTask:
                        Log($"忽略路径命令 {MagCarSimProtocol.CommandName(parsed.Command)}（本模拟器按 WinForm 循环跑）");
                        break;
                }

                return BuildStatusUnlocked(parsed.Command);
            }
        }

        private void ApplyStartUnlocked(string source)
        {
            if (!_dispatched)
            {
                Log($"待命中忽略放行（{source}）");
                return;
            }

            if (_waitingRelease)
            {
                _waitingRelease = false;
                _pausedByStopCommand = false;
                State = MagCarSimProtocol.StateRunning;
                _nextHopAt = DateTime.Now.AddMilliseconds(IntervalOrDefault());
                Log($"放行成功（{source}），站点 {CurrentSiteId} 改为运行中，{IntervalOrDefault()}ms 后前往下一站");
                return;
            }

            if (_pausedByStopCommand && _looping)
            {
                _pausedByStopCommand = false;
                State = MagCarSimProtocol.StateRunning;
                _nextHopAt = DateTime.Now.AddMilliseconds(IntervalOrDefault());
                Log($"从暂停恢复（{source}）");
                return;
            }

            if (_looping && State != MagCarSimProtocol.StateRunning)
            {
                State = MagCarSimProtocol.StateRunning;
                _nextHopAt = DateTime.Now.AddMilliseconds(IntervalOrDefault());
                Log($"启动运行（{source}）");
            }
        }

        private void PlaceAtStartUnlocked()
        {
            CurrentSiteId = Config.StartSiteId;
            Node = _map == null ? (ushort)Math.Clamp(Config.StartSiteId, 0, ushort.MaxValue) : _map.ResolveNode(Config.StartSiteId);
            State = MagCarSimProtocol.StateStopping;
            _waitingRelease = false;
            _pausedByStopCommand = false;
        }

        private void ArriveUnlocked(int siteId, bool isStart)
        {
            CurrentSiteId = siteId;
            Node = _map.ResolveNode(siteId);
            var needStop = _map.NeedStop(siteId);
            if (needStop)
            {
                State = MagCarSimProtocol.StateStopping;
                _waitingRelease = true;
                _nextHopAt = DateTime.MaxValue;
                Log($"{(isStart ? "起点" : "到站")} {siteId} node={Node} Mag_NeedStop=true → 停止中，等待放行");
                return;
            }

            State = MagCarSimProtocol.StateRunning;
            _waitingRelease = false;
            _nextHopAt = DateTime.Now.AddMilliseconds(IntervalOrDefault());
            if (isStart)
            {
                Log($"起点 {siteId} node={Node} 过站，{IntervalOrDefault()}ms 后下一地标");
            }
        }

        private void TryDispatchUnlocked(DateTime now, Dictionary<int, ushort> occupancy)
        {
            if (now < _dispatchAt)
            {
                return;
            }

            if (_loopSiteIds.Count == 0)
            {
                return;
            }

            var startSite = _loopSiteIds[_index];
            if (IsOccupiedUnlocked(startSite, occupancy))
            {
                HoldForOccupiedUnlocked(now, startSite, "发车");
                return;
            }

            OccupyUnlocked(occupancy, startSite);
            _dispatched = true;
            _holdingForStop = false;
            ArriveUnlocked(startSite, isStart: true);
        }

        private void AdvanceUnlocked(DateTime now, Dictionary<int, ushort> occupancy)
        {
            if (_loopSiteIds.Count < 2)
            {
                return;
            }

            var nextIndex = (_index + 1) % _loopSiteIds.Count;
            var nextSite = _loopSiteIds[nextIndex];
            if (IsOccupiedUnlocked(nextSite, occupancy))
            {
                HoldForOccupiedUnlocked(now, nextSite, "进站");
                return;
            }

            MoveOccupancyUnlocked(occupancy, CurrentSiteId, nextSite);
            _holdingForStop = false;
            _index = nextIndex;
            ArriveUnlocked(nextSite, isStart: false);
        }

        private bool IsOccupiedUnlocked(int siteId, Dictionary<int, ushort> occupancy)
        {
            if (_map == null || occupancy == null || !_map.SingleOccupancy(siteId))
            {
                return false;
            }

            return occupancy.TryGetValue(siteId, out var occupier) && occupier != VehicleCode;
        }

        private void OccupyUnlocked(Dictionary<int, ushort> occupancy, int siteId)
        {
            if (occupancy == null || siteId <= 0 || _map == null || !_map.SingleOccupancy(siteId))
            {
                return;
            }

            occupancy[siteId] = VehicleCode;
        }

        private void MoveOccupancyUnlocked(Dictionary<int, ushort> occupancy, int fromSiteId, int toSiteId)
        {
            if (occupancy == null)
            {
                return;
            }

            if (fromSiteId > 0 &&
                occupancy.TryGetValue(fromSiteId, out var occupier) &&
                occupier == VehicleCode)
            {
                occupancy.Remove(fromSiteId);
            }

            OccupyUnlocked(occupancy, toSiteId);
        }

        private void HoldForOccupiedUnlocked(DateTime now, int occupiedSiteId, string action)
        {
            _holdingForStop = true;
            State = MagCarSimProtocol.StateRunning;
            _waitingRelease = false;
            _nextHopAt = now.AddMilliseconds(200);
            if ((now - _lastHoldLogAt).TotalMilliseconds >= 5000)
            {
                _lastHoldLogAt = now;
                Log($"停止/交管点 {occupiedSiteId} 已有车，{action}受阻，保持站点 {CurrentSiteId} 运行中");
            }
        }

        private int IntervalOrDefault()
        {
            var value = Config.IntervalMs > 0 ? Config.IntervalMs : _hostConfig.DefaultIntervalMs;
            return Math.Max(200, value);
        }

        private byte[] BuildStatusUnlocked(byte command)
        {
            return MagCarSimProtocol.BuildStatus(
                command,
                VehicleCode,
                Node,
                State,
                Charge,
                Current,
                Voltage,
                Speed,
                Angle,
                Task,
                Lift,
                Roll);
        }

        private void Log(string message)
        {
            MagCarSimLog.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] [{Name}/{VehicleCode}] {message}");
        }
    }
}
