using System.Collections.Generic;
using System.Linq;

namespace AvatarRefinery.Editor.Core.Rules
{
    /// <summary>
    /// SDKのパフォーマンス統計(ベイク後)から、ランクを下げているカテゴリを1プラットフォーム=1 Issue にまとめる。
    /// PC: Poor/VeryPoor → Performance、Medium → Info。 Android: VeryPoor → Info(PC専用アバターが多数派のため強く出さない)。
    /// </summary>
    public sealed class PerformanceRule : IRule
    {
        public string Id => "perf.pc";
        public bool CanRun(RuleContext ctx) => ctx.HasVrcSdk && ctx.Perf != null && ctx.Perf.Ok;

        private static readonly string[] RankOrder = { "Excellent", "Good", "Medium", "Poor", "VeryPoor" };
        public static int RankIndex(string r) { var i = System.Array.IndexOf(RankOrder, r); return i < 0 ? -1 : i; }
        public static string JpRank(string r)
        {
            switch (r) { case "Excellent": return "Excellent"; case "Good": return "Good"; case "Medium": return "Medium"; case "Poor": return "Poor"; case "VeryPoor": return "Very Poor"; default: return "—"; }
        }
        public static string JpCategory(string c)
        {
            if (L10n.IsEnglish)
            {
                switch (c)
                {
                    case "PolyCount": return "Triangles";
                    case "AABB": return "Avatar Bounds";
                    case "SkinnedMeshCount": return "Skinned Meshes";
                    case "MeshCount": return "Mesh Renderers";
                    case "MaterialCount": return "Material Slots";
                    case "AnimatorCount": return "Animators";
                    case "BoneCount": return "Bones";
                    case "LightCount": return "Lights";
                    case "ParticleSystemCount": return "Particle Systems";
                    case "ParticleTotalCount": return "Total Particles";
                    case "ParticleMaxMeshPolyCount": return "Particle Mesh Triangles";
                    case "ParticleTrailsEnabled": return "Particle Trails";
                    case "ParticleCollisionEnabled": return "Particle Collision";
                    case "TrailRendererCount": return "Trail Renderers";
                    case "LineRendererCount": return "Line Renderers";
                    case "PhysBoneComponentCount": return "PhysBone Components";
                    case "PhysBoneTransformCount": return "PhysBone Transforms";
                    case "PhysBoneColliderCount": return "PhysBone Colliders";
                    case "PhysBoneCollisionCheckCount": return "PhysBone Collision Checks";
                    case "ContactCount": return "Contacts";
                    case "ConstraintsCount": return "Constraints";
                    case "ConstraintDepth": return "Constraint Depth";
                    case "RaycastCount": return "Raycasts";
                    case "ClothCount": return "Cloth Components";
                    case "ClothMaxVertices": return "Cloth Vertices";
                    case "PhysicsColliderCount": return "Physics Colliders";
                    case "PhysicsRigidbodyCount": return "Rigidbodies";
                    case "AudioSourceCount": return "Audio Sources";
                    case "TextureMegabytes": return "Texture Memory (MB)";
                    case "DownloadSize": return "Download Size";
                    case "UncompressedSize": return "Uncompressed Size";
                    default: return c;
                }
            }
            switch (c)
            {
                case "PolyCount": return "ポリゴン数(三角形)";
                case "AABB": return "サイズ(Bounds)";
                case "SkinnedMeshCount": return "Skinned Mesh 数";
                case "MeshCount": return "Mesh Renderer 数";
                case "MaterialCount": return "マテリアルスロット数";
                case "AnimatorCount": return "Animator 数";
                case "BoneCount": return "ボーン数";
                case "LightCount": return "ライト数";
                case "ParticleSystemCount": return "パーティクルシステム数";
                case "ParticleTotalCount": return "パーティクル総数";
                case "ParticleMaxMeshPolyCount": return "パーティクルメッシュのポリゴン数";
                case "ParticleTrailsEnabled": return "パーティクルトレイル";
                case "ParticleCollisionEnabled": return "パーティクル衝突";
                case "TrailRendererCount": return "Trail Renderer 数";
                case "LineRendererCount": return "Line Renderer 数";
                case "PhysBoneComponentCount": return "PhysBone コンポーネント数";
                case "PhysBoneTransformCount": return "PhysBone 影響ボーン数";
                case "PhysBoneColliderCount": return "PhysBone コライダー数";
                case "PhysBoneCollisionCheckCount": return "PhysBone 衝突チェック数";
                case "ContactCount": return "Contact 数";
                case "ConstraintsCount": return "Constraint 数";
                case "ConstraintDepth": return "Constraint の深さ";
                case "RaycastCount": return "Raycast 数";
                case "ClothCount": return "Cloth 数";
                case "ClothMaxVertices": return "Cloth 頂点数";
                case "PhysicsColliderCount": return "物理コライダー数";
                case "PhysicsRigidbodyCount": return "Rigidbody 数";
                case "AudioSourceCount": return "Audio Source 数";
                case "TextureMegabytes": return "テクスチャメモリ(MB)";
                case "DownloadSize": return "ダウンロードサイズ";
                case "UncompressedSize": return "非圧縮サイズ";
                default: return c;
            }
        }

        public IEnumerable<Issue> Evaluate(RuleContext ctx)
        {
            var perf = ctx.Perf;

            // ---- PC
            var pcBad = perf.Categories.Where(c => RankIndex(c.PcRating) >= RankIndex("Medium")).OrderByDescending(c => RankIndex(c.PcRating)).ToList();
            if (pcBad.Count > 0)
            {
                int worst = pcBad.Max(c => RankIndex(c.PcRating));
                var sev = worst >= RankIndex("Poor") ? Severity.Performance : Severity.Info;
                var issue = new Issue("perf.pc", sev)
                {
                    TitleOverride = L10n.IsEnglish
                        ? $"PC performance rank is {JpRank(perf.PcOverall)} — {pcBad.Count} limiting categories"
                        : $"PC ランクは {JpRank(perf.PcOverall)} です — 下げている項目: {pcBad.Count} 個",
                    Detail = string.Join("\n", pcBad.Select(c => Line(c.Category, c.Value, c.PcRating, false))),
                }.WithMetric("overall", perf.PcOverall);
                foreach (var c in pcBad) issue.WithMetric(c.Category, $"{c.Value} ({c.PcRating})");
                yield return issue;
            }

            // ---- Android(Quest / モバイル)
            var andBad = perf.Categories.Where(c => RankIndex(c.AndroidRating) >= RankIndex("Poor")).OrderByDescending(c => RankIndex(c.AndroidRating)).ToList();
            if (andBad.Count > 0)
            {
                var issue = new Issue("perf.android", Severity.Info)
                {
                    TitleOverride = L10n.IsEnglish
                        ? $"Quest / mobile rank is {JpRank(perf.AndroidOverall)} — {andBad.Count} limiting categories (ignore for PC-only avatars)"
                        : $"Quest / スマホ版ランクは {JpRank(perf.AndroidOverall)} です — 下げている項目: {andBad.Count} 個(PC専用なら無視可)",
                    Detail = string.Join("\n", andBad.Take(12).Select(c => Line(c.Category, c.Value, c.AndroidRating, true))) + (andBad.Count > 12 ? "\n  - …" : ""),
                }.WithMetric("overall", perf.AndroidOverall);
                yield return issue;
            }
        }

        private static string Line(string category, string value, string rating, bool mobile)
        {
            // 「次のランクに上げるには上限いくつまで」を添える
            string next = "";
            int idx = RankIndex(rating);
            if (idx > 0)
            {
                var better = RankOrder[idx - 1];
                var limit = VrcPerformanceAdapter.GetLimit(category, better, mobile);
                if (limit.HasValue) next = L10n.IsEnglish
                    ? $" → {FormatLimit(category, limit.Value)} or less for {JpRank(better)}"
                    : $" → {JpRank(better)} にするには {FormatLimit(category, limit.Value)} 以下";
            }
            return $"  - {JpCategory(category)}: {value}  [{JpRank(rating)}]{next}";
        }

        private static string FormatLimit(string category, double v)
        {
            if (category == "TextureMegabytes") return $"{v:0} MB";
            return v % 1 == 0 ? ((long)v).ToString("N0") : v.ToString("0.##");
        }
    }
}
