using System;
using System.Linq;
using UnityEngine;
using AvatarRefinery.Editor.Core.Rules;

namespace AvatarRefinery.Editor.Core
{
    /// <summary>
    /// 診断の統括。読み取り専用を保証する(シーン・アセットを変更しない。NDMFベイクはクローンに対してのみ行い、必ず破棄する)。
    /// 流れ: 環境検出 → シーン上の性能算定 → NDMFベイク → ベイク後の性能算定 → ルール実行 → レポート
    /// </summary>
    public static class DiagnosisRunner
    {
        /// <summary>package.json の version(取得できなければ "dev")</summary>
        public static string ToolVersion => _toolVersion ??= ReadToolVersion();
        private static string _toolVersion;
        private static string ReadToolVersion()
        {
            try
            {
                var info = UnityEditor.PackageManager.PackageInfo.FindForAssembly(typeof(DiagnosisRunner).Assembly);
                return string.IsNullOrEmpty(info?.version) ? "dev" : info.version;
            }
            catch { return "dev"; }
        }

        public static DiagnosisReport Run(GameObject avatarRoot)
        {
            if (avatarRoot == null) throw new ArgumentNullException(nameof(avatarRoot));

            var report = new DiagnosisReport
            {
                AvatarName = avatarRoot.name,
                Timestamp = DateTime.Now,
                ToolVersion = ToolVersion,
                UnityVersion = Application.unityVersion,
                HasVrcSdk = VrcPerformanceAdapter.Available,
                HasNdmf = BakedAvatar.Available,
                HasModularAvatar = AssemblyLoaded("nadena.dev.modular-avatar.core"),
                HasAao = AssemblyLoaded("com.anatawa12.avatar-optimizer.runtime"),
                SdkVersion = DetectSdkVersion(),
            };

            var ctx = new RuleContext
            {
                AvatarRoot = avatarRoot,
                HasVrcSdk = report.HasVrcSdk,
                HasNdmf = report.HasNdmf,
                HasModularAvatar = report.HasModularAvatar,
                HasAao = report.HasAao,
            };

            var swTotal = System.Diagnostics.Stopwatch.StartNew();

            // 1) シーン上の性能
            if (report.HasVrcSdk)
            {
                var sw = System.Diagnostics.Stopwatch.StartNew();
                ctx.PerfPreBake = VrcPerformanceAdapter.Calculate(avatarRoot);
                report.StatsMs += sw.ElapsedMilliseconds;
            }

            // 2) NDMF ベイク → ベイク後の性能(失敗したらシーン上の値を使う)
            BakedAvatar bake = null;
            try
            {
                if (report.HasNdmf && report.HasVrcSdk)
                {
                    bake = BakedAvatar.Create(avatarRoot);
                    report.BakeMs = bake.ElapsedMs;
                    ctx.NdmfMessages = bake.Messages;
                    report.NdmfMessages = bake.Messages;
                    ctx.BakeError = bake.Ok ? null : bake.Error;
                    if (bake.Ok)
                    {
                        var sw = System.Diagnostics.Stopwatch.StartNew();
                        ctx.Perf = VrcPerformanceAdapter.Calculate(bake.Root);
                        report.StatsMs += sw.ElapsedMilliseconds;
                        ctx.BakedRoot = bake.Root;
                        report.StatsFromBakedAvatar = ctx.Perf.Ok;
                    }
                    else report.BakeError = bake.Error;
                }
                if (ctx.Perf == null || !ctx.Perf.Ok) { ctx.Perf = ctx.PerfPreBake; report.StatsFromBakedAvatar = false; }

                // 3) ルール実行(1ルールの例外で全体を止めない)
                var swRules = System.Diagnostics.Stopwatch.StartNew();
                foreach (var rule in RuleRegistry.All)
                {
                    try
                    {
                        if (!rule.CanRun(ctx)) continue;
                        foreach (var issue in rule.Evaluate(ctx))
                        {
                            if (issue == null) continue;
                            // 知識データの既定値(AutoFixable)を反映
                            var k = KnowledgeBase.Get(issue.RuleId);
                            if (k.AutoFixable) issue.AutoFixable = true;
                            report.Issues.Add(issue);
                        }
                    }
                    catch (Exception e)
                    {
                        report.RuleErrors.Add($"{rule.Id}: {e.GetType().Name}: {e.Message}");
                        Debug.LogWarning($"[AvatarRefinery] Rule {rule.Id} threw an exception: {e}");
                    }
                }
                report.RulesMs = swRules.ElapsedMilliseconds;
            }
            finally
            {
                bake?.Dispose();
            }

            // 4) 性能サマリをレポートへ
            if (ctx.Perf != null && ctx.Perf.Ok)
            {
                report.PcOverall = ctx.Perf.PcOverall;
                report.AndroidOverall = ctx.Perf.AndroidOverall;
                report.PerfCategories = ctx.Perf.Categories.ToList();
            }
            if (ctx.PerfPreBake != null && ctx.PerfPreBake.Ok)
                report.PerfCategoriesPreBake = ctx.PerfPreBake.Categories.ToList();

            return report;
        }

        private static bool AssemblyLoaded(string name)
        {
            return AppDomain.CurrentDomain.GetAssemblies().Any(a => a.GetName().Name == name);
        }

        private static string DetectSdkVersion()
        {
#if AR_VRCSDK3_AVATARS
            return SdkVersionRule.DetectSdkVersion() ?? "unknown";
#else
            return "not installed";
#endif
        }
    }
}
