using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using L10n = AvatarRefinery.Editor.Core.L10n;

namespace AvatarRefinery.Editor.Core.Rules
{
    /// <summary>Unityバージョンが VRChat 対応版(2022.3.22f1)か</summary>
    public sealed class UnityVersionRule : IRule
    {
        public const string RequiredUnity = "2022.3.22f1";
        public string Id => "env.unity_version";
        public bool CanRun(RuleContext ctx) => true;
        public IEnumerable<Issue> Evaluate(RuleContext ctx)
        {
            var v = Application.unityVersion;
            if (v == RequiredUnity) yield break;
            // VRChat SDK が無いプロジェクト(汎用モード)では情報レベルに落とす
            var sev = ctx.HasVrcSdk ? Severity.Blocker : Severity.Info;
            yield return new Issue(Id, sev)
            {
                Detail = L10n.IsEnglish ? $"Current: Unity {v} / VRChat-supported: {RequiredUnity}" : $"現在: Unity {v} / VRChat対応: {RequiredUnity}",
            }.WithMetric("current", v).WithMetric("required", RequiredUnity);
        }
    }

    /// <summary>VRChat SDK 不在の告知(汎用診断モード)</summary>
    public sealed class SdkPresenceRule : IRule
    {
        public string Id => "env.sdk_missing";
        public bool CanRun(RuleContext ctx) => !ctx.HasVrcSdk;
        public IEnumerable<Issue> Evaluate(RuleContext ctx)
        {
            yield return new Issue(Id, Severity.Info) { Detail = L10n.T("VRChat固有の診断項目とパフォーマンスランクはスキップされました。", "VRChat-specific checks and performance-rank calculation were skipped.") };
        }
    }

    /// <summary>旧 Dynamic Bone の残存</summary>
    public sealed class LegacyDynamicBoneRule : IRule
    {
        public string Id => "env.legacy_dynamic_bone";
        public bool CanRun(RuleContext ctx) => true;
        public IEnumerable<Issue> Evaluate(RuleContext ctx)
        {
            var hits = new List<Component>();
            foreach (var c in ctx.GetAll<Component>())
            {
                if (c == null) continue;
                var n = c.GetType().Name;
                if (n == "DynamicBone" || n == "DynamicBoneCollider" || n == "DynamicBonePlaneCollider") hits.Add(c);
            }
            if (hits.Count == 0) yield break;
            var issue = new Issue(Id, Severity.Broken)
            {
                Detail = (L10n.IsEnglish ? $"Legacy Dynamic Bone components: {hits.Count}: " : $"Dynamic Bone 系コンポーネント {hits.Count} 個: ") + string.Join(", ", hits.Take(5).Select(h => h.gameObject.name)) + (hits.Count > 5 ? " …" : ""),
            }.WithMetric("count", hits.Count);
            foreach (var h in hits) issue.WithTarget(h, ctx.Root);
            yield return issue;
        }
    }

    /// <summary>Missing Script(壊れたコンポーネント参照)</summary>
    public sealed class MissingScriptRule : IRule
    {
        public string Id => "vrc.missing_script";
        public bool CanRun(RuleContext ctx) => true;
        public IEnumerable<Issue> Evaluate(RuleContext ctx)
        {
            var hits = new List<GameObject>();
            int total = 0;
            foreach (var t in ctx.GetAll<Transform>())
            {
                int n = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(t.gameObject);
                if (n > 0) { hits.Add(t.gameObject); total += n; }
            }
            if (hits.Count == 0) yield break;
            // VRChat SDK 環境ではアップロードを止める可能性が高いので Blocker、汎用モードでは Broken
            var issue = new Issue(Id, ctx.HasVrcSdk ? Severity.Blocker : Severity.Broken)
            {
                Detail = (L10n.IsEnglish ? $"Missing Scripts: {total} across {hits.Count} objects: " : $"Missing Script {total} 個(オブジェクト {hits.Count} 個): ") + string.Join(", ", hits.Take(5).Select(h => h.name)) + (hits.Count > 5 ? " …" : ""),
                AutoFixable = true,
            }.WithMetric("count", total).WithMetric("objects", hits.Count);
            foreach (var h in hits) issue.WithTarget(h, ctx.Root);
            yield return issue;
        }
    }

    /// <summary>VRM(UniVRM)読み込み直後の状態(VRChat向け変換前)</summary>
    public sealed class VrmImportedStateRule : IRule
    {
        public string Id => "vrm.imported_state";
        public bool CanRun(RuleContext ctx) => true;
        public IEnumerable<Issue> Evaluate(RuleContext ctx)
        {
            var found = new List<string>();
            Component anchor = null;
            foreach (var c in ctx.GetAll<Component>())
            {
                if (c == null) continue;
                var fn = c.GetType().FullName ?? "";
                if (fn == "UniVRM10.Vrm10Instance" || fn == "VRM.VRMMeta" || fn == "VRM.VRMSpringBone" || fn.StartsWith("UniVRM10.VRM10SpringBone"))
                {
                    if (!found.Contains(c.GetType().Name)) found.Add(c.GetType().Name);
                    if (anchor == null && (fn == "UniVRM10.Vrm10Instance" || fn == "VRM.VRMMeta")) anchor = c;
                }
            }
            if (found.Count == 0) yield break;

            // 変換の進み具合を添える
            bool hasPhysBone = ctx.GetAll<Component>().Any(c => c != null && c.GetType().Name == "VRCPhysBone");
            bool hasSpring = found.Any(n => n.Contains("Spring"));
            var notes = new List<string>();
            if (hasSpring && !hasPhysBone) notes.Add(L10n.T("揺れもの: VRM SpringBone のまま(PhysBone 未変換)", "Secondary motion: VRM SpringBone remains (not converted to PhysBone)"));
            var mtoon = ctx.GetAll<Renderer>().SelectMany(r => r.sharedMaterials).Where(m => m != null && m.shader != null && m.shader.name.Contains("MToon")).Count();
            if (mtoon > 0) notes.Add(L10n.IsEnglish ? $"Shaders: {mtoon} MToon materials (not Quest-compatible)" : $"シェーダー: MToon のマテリアル {mtoon} 個(Quest 非対応)");

            var issue = new Issue(Id, Severity.Info)
            {
                Detail = L10n.T("VRM コンポーネント: ", "VRM components: ") + string.Join(", ", found) + (notes.Count > 0 ? "\n" + string.Join("\n", notes) : ""),
            }.WithMetric("components", string.Join("|", found));
            if (anchor != null) issue.WithTarget(anchor.gameObject, ctx.Root);
            yield return issue;
        }
    }
}
