using StandardScene.Magnetic.Protocol;
using System;
using System.Collections.Generic;
using System.Globalization;

namespace StandardScene.Magnetic.Tasking
{
    /// <summary>
    /// 管控停止（StartStop=12）按车放行：地图站点保持 12，仅改本车任务报文。
    /// </summary>
    public sealed class Fass2ControlAreaInfo
    {
        public int SiteId { get; set; }
        public string AreaId { get; set; } = string.Empty;
        public int[] AreaSiteIds { get; set; } = Array.Empty<int>();
        public int Capacity { get; set; } = 1;
        public byte ReleaseStartStop { get; set; } = Fass2TaskBuilder.StartStopControlStart;
    }

    public sealed class Fass2ControlOccupancy
    {
        public Fass2ControlOccupancy(int carId, IReadOnlyList<int> occupiedSiteIds)
        {
            CarId = carId;
            OccupiedSiteIds = occupiedSiteIds ?? Array.Empty<int>();
        }

        public int CarId { get; }
        public IReadOnlyList<int> OccupiedSiteIds { get; }
    }

    public sealed class Fass2ControlReleaseCheck
    {
        public bool CanRelease { get; set; }
        public string AreaId { get; set; } = string.Empty;
        public int Capacity { get; set; } = 1;
        public int OthersInArea { get; set; }
        public byte ReleaseStartStop { get; set; } = Fass2TaskBuilder.StartStopControlStart;
        public string Reason { get; set; } = string.Empty;
    }

    public static class Fass2ControlAreaGate
    {
        private static readonly char[] SiteIdSeparators = { ',', ';', '|', ' ', '\t', '[', ']', '(', ')', '{', '}' };

        public static bool IsControlStop(byte startStop)
        {
            return startStop == Fass2TaskBuilder.StartStopControlStop;
        }

        public static bool AllowsLeave(byte startStop)
        {
            return startStop == Fass2TaskBuilder.StartStopPass
                   || startStop == Fass2TaskBuilder.StartStopControlStart;
        }

        public static Fass2ControlAreaInfo Resolve(Fass2SiteActionData data, int siteId)
        {
            data ??= new Fass2SiteActionData();
            var areaSites = ParseSiteIds(data.ControlArea, siteId);
            var capacity = data.ControlCapacity.GetValueOrDefault(1);
            if (capacity < 1)
            {
                capacity = 1;
            }

            var release = data.ControlReleaseStartStop ?? Fass2TaskBuilder.StartStopControlStart;
            if (release != Fass2TaskBuilder.StartStopPass &&
                release != Fass2TaskBuilder.StartStopControlStart)
            {
                release = Fass2TaskBuilder.StartStopControlStart;
            }

            return new Fass2ControlAreaInfo
            {
                SiteId = siteId,
                AreaSiteIds = areaSites,
                AreaId = string.Join(",", areaSites),
                Capacity = capacity,
                ReleaseStartStop = release
            };
        }

        public static int[] ParseSiteIds(string text, int fallbackSiteId)
        {
            var ids = new List<int>();
            if (!string.IsNullOrWhiteSpace(text))
            {
                var parts = text.Split(SiteIdSeparators, StringSplitOptions.RemoveEmptyEntries);
                foreach (var part in parts)
                {
                    if (int.TryParse(part.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var id) &&
                        id > 0 && !ids.Contains(id))
                    {
                        ids.Add(id);
                    }
                }
            }

            if (ids.Count == 0 && fallbackSiteId > 0)
            {
                ids.Add(fallbackSiteId);
            }

            return ids.ToArray();
        }

        public static int LimitWindowCount(
            IReadOnlyList<Fass2NodeMessage> nodes,
            int startIndex,
            int count,
            int releasedIndex)
        {
            if (nodes == null || count <= 0 || startIndex < 0 || startIndex >= nodes.Count)
            {
                return 0;
            }

            var limited = Math.Min(count, nodes.Count - startIndex);
            for (var i = 0; i < limited; i++)
            {
                var node = nodes[startIndex + i];
                if (node != null && IsControlStop(node.StartStop) && releasedIndex != startIndex + i)
                {
                    return i + 1;
                }
            }

            return limited;
        }

        public static Fass2ControlReleaseCheck Evaluate(
            int selfCarId,
            Fass2ControlAreaInfo area,
            IReadOnlyList<Fass2ControlOccupancy> occupancies)
        {
            area ??= new Fass2ControlAreaInfo();
            if (area.AreaSiteIds == null || area.AreaSiteIds.Length == 0)
            {
                area.AreaSiteIds = area.SiteId > 0 ? new[] { area.SiteId } : Array.Empty<int>();
            }

            if (string.IsNullOrEmpty(area.AreaId))
            {
                area.AreaId = string.Join(",", area.AreaSiteIds);
            }

            if (area.Capacity < 1)
            {
                area.Capacity = 1;
            }

            var others = 0;
            if (occupancies != null)
            {
                foreach (var occupancy in occupancies)
                {
                    if (occupancy == null || occupancy.CarId == selfCarId)
                    {
                        continue;
                    }

                    if (OccupiesListedSites(occupancy.OccupiedSiteIds, area.AreaSiteIds))
                    {
                        others++;
                    }
                }
            }

            var canRelease = others < area.Capacity;
            return new Fass2ControlReleaseCheck
            {
                CanRelease = canRelease,
                AreaId = area.AreaId,
                Capacity = area.Capacity,
                OthersInArea = others,
                ReleaseStartStop = area.ReleaseStartStop == 0
                    ? Fass2TaskBuilder.StartStopControlStart
                    : area.ReleaseStartStop,
                Reason = canRelease
                    ? "area clear"
                    : $"area occupied cars={others}, capacity={area.Capacity}"
            };
        }

        public static bool OccupiesListedSites(IReadOnlyList<int> occupiedSiteIds, IReadOnlyList<int> areaSiteIds)
        {
            if (occupiedSiteIds == null || occupiedSiteIds.Count == 0 ||
                areaSiteIds == null || areaSiteIds.Count == 0)
            {
                return false;
            }

            for (var i = 0; i < occupiedSiteIds.Count; i++)
            {
                var occupied = occupiedSiteIds[i];
                if (occupied <= 0)
                {
                    continue;
                }

                for (var j = 0; j < areaSiteIds.Count; j++)
                {
                    if (areaSiteIds[j] == occupied)
                    {
                        return true;
                    }
                }
            }

            return false;
        }
    }
}
