using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using SimpleCore;
using SimpleCore.PropType;
using SimpleLite.RCS;
using SimpleLite.RCS.CarTypes;
using StandardScene.Signal.Site;

namespace StandardScene.Signal.Plc
{
    /// <summary>
    /// 不引用 Magnetic.dll：按方法名反射 Start/Stop/牵引棒。
    /// MagCar / Mag2Car 有对应 MethodMember 时生效；没有则视为动作已完成。
    /// </summary>
    public static class SignalCarAdapter
    {
        public static IEnumerable<Car> All()
        {
            try
            {
                return SimpleLib.GetAllCars().OfType<Car>();
            }
            catch
            {
                return Array.Empty<Car>();
            }
        }

        public static Car Find(int id)
        {
            if (id <= 0) return null;
            return SimpleLib.GetCar(id) as Car;
        }

        public static int PositionId(Car car)
        {
            if (car == null) return 0;
            try
            {
                if (car.siteID > 0) return car.siteID;
                var last = car.GetLastSite();
                return last > 0 ? last : 0;
            }
            catch
            {
                return 0;
            }
        }

        public static string GetState(Car car)
        {
            if (car?.status?.enums != null &&
                car.status.enums.TryGetValue("State", out var state) &&
                !string.IsNullOrWhiteSpace(state))
                return state;
            return "";
        }

        public static bool IsMoving(Car car)
        {
            var state = GetState(car);
            return state == "运行中" ||
                   string.Equals(state, "Running", StringComparison.OrdinalIgnoreCase);
        }

        /// <summary>对齐反编译 RunStatus==停止：非运行中即视为停稳，含充电中。</summary>
        public static bool IsStopped(Car car)
        {
            if (car == null) return false;
            if (IsMoving(car)) return false;
            var state = GetState(car);
            if (state == "停止中" || state == "充电中" ||
                string.Equals(state, "Stopping", StringComparison.OrdinalIgnoreCase) ||
                string.Equals(state, "Charging", StringComparison.OrdinalIgnoreCase) ||
                string.Equals(state, "WaitPass", StringComparison.OrdinalIgnoreCase))
                return true;
            return !IsRunning(car);
        }

        public static bool IsCharging(Car car)
        {
            var state = GetState(car);
            return state == "充电中" ||
                   string.Equals(state, "Charging", StringComparison.OrdinalIgnoreCase);
        }

        public static bool IsRunning(Car car)
        {
            if (car == null) return false;
            if (IsMoving(car)) return true;
            if (car.status?.pendingLocks != null && car.status.pendingLocks.Length > 0)
                return true;
            if (car.tags != null && car.tags.Contains("occupied"))
                return true;
            return false;
        }

        public static double Soc(Car car)
        {
            if (car?.status?.enums != null &&
                car.status.enums.TryGetValue("Soc", out var soc) &&
                double.TryParse(soc, NumberStyles.Float, CultureInfo.InvariantCulture, out var value))
                return value;
            return 0;
        }

        public static string GroupId(Car car)
        {
            if (car?.fields == null) return "";
            if (car.fields.TryGetValue("group", out var g) && !string.IsNullOrWhiteSpace(g))
                return g;
            if (car.fields.TryGetValue("GroupID", out var g2) && !string.IsNullOrWhiteSpace(g2))
                return g2;
            return "";
        }

        public static IReadOnlyList<Car> CarsAt(IEnumerable<int> siteIds)
        {
            var set = new HashSet<int>(siteIds ?? Array.Empty<int>());
            if (set.Count == 0)
                return Array.Empty<Car>();
            return All().Where(c => set.Contains(PositionId(c))).ToList();
        }

        public static IReadOnlyList<Car> CarsAt(int siteId)
        {
            if (siteId <= 0) return Array.Empty<Car>();
            return CarsAt(new[] { siteId });
        }

        public static bool TryReadSiteField(int siteId, string key, out string value)
        {
            value = "";
            if (siteId <= 0 || string.IsNullOrWhiteSpace(key))
                return false;
            try
            {
                var site = SimpleLib.GetSite(siteId);
                if (site?.fields == null)
                    return false;
                if (!site.fields.TryGetValue(key, out var raw) || string.IsNullOrWhiteSpace(raw))
                    return false;
                value = raw;
                return true;
            }
            catch
            {
                return false;
            }
        }

        public static bool TryReadSignalStop(int siteId, out string signalType, out string signalParams)
        {
            signalType = "";
            signalParams = "";
            if (!TryReadSiteField(siteId, SignalSiteFields.SignalType, out signalType))
                return false;
            TryReadSiteField(siteId, SignalSiteFields.SignalParams, out signalParams);
            return true;
        }

        public static int ChargeSeconds(Car car, IDictionary<int, DateTime> startedAt)
        {
            if (car == null) return 0;
            var mi = car.GetType().GetMethod("GetChargeTime", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            if (mi != null)
            {
                try
                {
                    var r = mi.Invoke(car, null);
                    if (r is int i) return i;
                    if (r is double d) return (int)d;
                    if (r is TimeSpan ts) return (int)ts.TotalSeconds;
                }
                catch
                {
                }
            }

            if (IsCharging(car))
            {
                if (!startedAt.ContainsKey(car.id))
                    startedAt[car.id] = DateTime.Now;
                return (int)(DateTime.Now - startedAt[car.id]).TotalSeconds;
            }

            startedAt.Remove(car.id);
            return 0;
        }

        public static void Start(Car car)
        {
            if (car == null) return;
            if (TryInvoke(car, "StartMag2Car") || TryInvoke(car, "StartMagCar"))
                return;
            TryInvoke(car, "SendStart", new object[] { (ushort)0 });
        }

        public static void Stop(Car car)
        {
            if (car == null) return;
            if (TryInvoke(car, "StopMag2Car") || TryInvoke(car, "StopMagCar"))
                return;
            TryInvoke(car, "SendStop", new object[] { (ushort)0 });
        }

        public static void StopCharge(Car car)
        {
            if (car == null) return;
            TryInvoke(car, "StopCharge");
        }

        public static bool IsActionFinish(Car car, string action)
        {
            if (car == null) return false;
            if (string.IsNullOrWhiteSpace(action)) return true;
            var mi = car.GetType().GetMethod("IsActionFinish", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            if (mi == null)
                return true;
            try
            {
                var r = mi.Invoke(car, new object[] { action });
                return r is true;
            }
            catch
            {
                return true;
            }
        }

        public static void Control(Car car, string action)
        {
            if (car == null || string.IsNullOrWhiteSpace(action)) return;
            if (TryInvoke(car, action)) return;
            TryInvoke(car, "ImmediateCommand", new object[] { action });
        }

        private static bool TryInvoke(object target, string name, object[] args = null)
        {
            var type = target.GetType();
            var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
                .Where(m => string.Equals(m.Name, name, StringComparison.Ordinal));
            foreach (var mi in methods)
            {
                var ps = mi.GetParameters();
                if (args == null)
                {
                    if (ps.Length != 0) continue;
                    try { mi.Invoke(target, null); return true; }
                    catch { return false; }
                }

                if (ps.Length != args.Length) continue;
                try { mi.Invoke(target, args); return true; }
                catch { return false; }
            }

            return false;
        }
    }
}
