using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;

namespace CatsUdonTools.Editor
{
    /// <summary>
    /// Unity 単体では読み込めない形式のモデルファイル（.blend など。読み込みに Blender などのアプリが要る）にあるメッシュとアバター定義を、
    /// 入稿フォルダへ Unity のアセット（.asset）として書き出し、入稿用の一時階層の参照を書き出した側へ差し替える。
    /// Why: 変換サーバー（Worker）には Blender などが無く、これらのファイルを読み込めない。そのまま入稿するとメッシュが空になり、
    /// その部分が表示されない（2026-10-12、dom.blend の空の球）。FBX・OBJ など Unity が自前で読める形式は、そのまま入稿する。
    /// エディタ時のみ動作し、プレイヤービルドには含まれない。入稿者の元ファイルは変更しない。
    /// 既知の制限: アニメーションクリップは書き出さない（該当形式の中のクリップを使うアニメーションは、変換サーバーで再生できない）
    /// </summary>
    internal static class SubmissionMeshDuplicator
    {
        private const string AssetExtension = ".asset";

        // Unity が読み込みに外部のアプリを呼ぶ形式（Unity マニュアル「サポートしているモデルファイル形式」）
        private static readonly HashSet<string> ExternalToolModelExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
        {
            ".blend",
            ".max",
            ".ma",
            ".mb",
            ".c4d",
            ".lxo",
            ".jas",
        };

        /// <summary>
        /// exporterRoot 配下の MeshFilter・SkinnedMeshRenderer・MeshCollider のメッシュと Animator のアバター定義のうち、
        /// 該当形式のものを meshesFolder へ書き出して差し替える。書き出したアセットの数を返す
        /// </summary>
        public static int ReplaceWithCopies(GameObject exporterRoot, string meshesFolder)
        {
            // 同じ元メッシュを複数のコンポーネントが共有していても、書き出しは 1 回だけにするため
            var copiedByOriginal = new Dictionary<Object, Object>();

            foreach (var meshFilter in exporterRoot.GetComponentsInChildren<MeshFilter>(true))
            {
                meshFilter.sharedMesh = GetOrCreateCopy(meshFilter.sharedMesh, meshesFolder, copiedByOriginal);
            }

            foreach (var skinnedMeshRenderer in exporterRoot.GetComponentsInChildren<SkinnedMeshRenderer>(true))
            {
                skinnedMeshRenderer.sharedMesh = GetOrCreateCopy(skinnedMeshRenderer.sharedMesh, meshesFolder, copiedByOriginal);
            }

            foreach (var meshCollider in exporterRoot.GetComponentsInChildren<MeshCollider>(true))
            {
                meshCollider.sharedMesh = GetOrCreateCopy(meshCollider.sharedMesh, meshesFolder, copiedByOriginal);
            }

            foreach (var animator in exporterRoot.GetComponentsInChildren<Animator>(true))
            {
                animator.avatar = GetOrCreateCopy(animator.avatar, meshesFolder, copiedByOriginal);
            }

            return copiedByOriginal.Count;
        }

        /// <summary>Unity 単体では読み込めない形式のモデルファイルの中にあるアセットか</summary>
        internal static bool IsInExternalToolModel(Object asset)
        {
            var assetPath = AssetDatabase.GetAssetPath(asset);
            return !string.IsNullOrEmpty(assetPath) && ExternalToolModelExtensions.Contains(Path.GetExtension(assetPath));
        }

        private static T GetOrCreateCopy<T>(T original, string meshesFolder, Dictionary<Object, Object> copiedByOriginal) where T : Object
        {
            if (original == null || !IsInExternalToolModel(original))
            {
                return original;
            }

            if (copiedByOriginal.TryGetValue(original, out var existingCopy))
            {
                return (T)existingCopy;
            }

            var copy = Object.Instantiate(original);
            copy.name = original.name;
            var copyPath = AssetDatabase.GenerateUniqueAssetPath($"{meshesFolder}/{ToSafeFileName(original.name)}{AssetExtension}");
            AssetDatabase.CreateAsset(copy, copyPath);
            copiedByOriginal.Add(original, copy);
            return copy;
        }

        private static string ToSafeFileName(string assetName)
        {
            foreach (var invalidCharacter in Path.GetInvalidFileNameChars())
            {
                assetName = assetName.Replace(invalidCharacter, '_');
            }

            return string.IsNullOrEmpty(assetName) ? "Mesh" : assetName;
        }
    }
}
