using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEditor;

namespace MCPForUnity.Editor.Helpers
{
    internal static class RenderPipelineUtility
    {
        internal enum PipelineKind
        {
            BuiltIn,
            Universal,
            HighDefinition,
            Custom
        }

        internal enum VFXComponentType
        {
            ParticleSystem,
            LineRenderer,
            TrailRenderer
        }

        private static Dictionary<string, Material> s_DefaultVFXMaterials = new Dictionary<string, Material>();
        private static Dictionary<string, Material> s_DefaultSceneMaterials = new Dictionary<string, Material>();

        private static readonly string[] BuiltInLitShaders = { "Standard", "Legacy Shaders/Diffuse" };
        private static readonly string[] BuiltInUnlitShaders = { "Unlit/Color", "Unlit/Texture" };
        private static readonly string[] BuiltInParticleShaders = { "Particles/Standard Unlit", "Particles/Alpha Blended", "Particles/Additive" };
        private static readonly string[] UrpLitShaders = { "Universal Render Pipeline/Lit", "Universal Render Pipeline/Simple Lit" };
        private static readonly string[] UrpUnlitShaders = { "Universal Render Pipeline/Unlit" };
        private static readonly string[] UrpParticleShaders = {
            "Universal Render Pipeline/Particles/Unlit",
            "Universal Render Pipeline/Particles/Simple Lit",
            "Universal Render Pipeline/Particles/Lit",
        };
        private static readonly string[] HdrpLitShaders = { "HDRP/Lit", "High Definition Render Pipeline/Lit" };
        private static readonly string[] HdrpUnlitShaders = { "HDRP/Unlit", "High Definition Render Pipeline/Unlit" };

        internal static PipelineKind GetActivePipeline()
        {
            var asset = GraphicsSettings.currentRenderPipeline;
            if (asset == null)
            {
                return PipelineKind.BuiltIn;
            }

            var typeName = asset.GetType().FullName ?? string.Empty;
            if (typeName.IndexOf("HighDefinition", StringComparison.OrdinalIgnoreCase) >= 0 ||
                typeName.IndexOf("HDRP", StringComparison.OrdinalIgnoreCase) >= 0)
            {
                return PipelineKind.HighDefinition;
            }

            if (typeName.IndexOf("Universal", StringComparison.OrdinalIgnoreCase) >= 0 ||
                typeName.IndexOf("URP", StringComparison.OrdinalIgnoreCase) >= 0)
            {
                return PipelineKind.Universal;
            }

            return PipelineKind.Custom;
        }

        internal static Shader ResolveShader(string requestedNameOrAlias)
        {
            var pipeline = GetActivePipeline();

            if (!string.IsNullOrWhiteSpace(requestedNameOrAlias))
            {
                var alias = requestedNameOrAlias.Trim();
                var aliasMatch = ResolveAlias(alias, pipeline);
                if (aliasMatch != null)
                {
                    WarnIfPipelineMismatch(aliasMatch.name, pipeline);
                    return aliasMatch;
                }

                var direct = Shader.Find(alias);
                if (direct != null)
                {
                    WarnIfPipelineMismatch(direct.name, pipeline);
                    return direct;
                }

                McpLog.Warn($"Shader '{alias}' not found. Falling back to {pipeline} defaults.");
            }

            var fallback = ResolveDefaultLitShader(pipeline)
                           ?? ResolveDefaultLitShader(PipelineKind.BuiltIn)
                           ?? Shader.Find("Unlit/Color");

            if (fallback != null)
            {
                WarnIfPipelineMismatch(fallback.name, pipeline);
            }

            return fallback;
        }

        internal static Shader ResolveDefaultLitShader(PipelineKind pipeline)
        {
            return pipeline switch
            {
                PipelineKind.HighDefinition => TryFindShader(HdrpLitShaders) ?? TryFindShader(UrpLitShaders),
                PipelineKind.Universal => TryFindShader(UrpLitShaders) ?? TryFindShader(HdrpLitShaders),
                PipelineKind.Custom => TryFindShader(BuiltInLitShaders) ?? TryFindShader(UrpLitShaders) ?? TryFindShader(HdrpLitShaders),
                _ => TryFindShader(BuiltInLitShaders) ?? Shader.Find("Unlit/Color")
            };
        }

        internal static Shader ResolveDefaultUnlitShader(PipelineKind pipeline)
        {
            return pipeline switch
            {
                PipelineKind.HighDefinition => TryFindShader(HdrpUnlitShaders) ?? TryFindShader(UrpUnlitShaders) ?? TryFindShader(BuiltInUnlitShaders),
                PipelineKind.Universal => TryFindShader(UrpUnlitShaders) ?? TryFindShader(HdrpUnlitShaders) ?? TryFindShader(BuiltInUnlitShaders),
                PipelineKind.Custom => TryFindShader(BuiltInUnlitShaders) ?? TryFindShader(UrpUnlitShaders) ?? TryFindShader(HdrpUnlitShaders),
                _ => TryFindShader(BuiltInUnlitShaders)
            };
        }

        private static Shader ResolveAlias(string alias, PipelineKind pipeline)
        {
            if (string.Equals(alias, "lit", StringComparison.OrdinalIgnoreCase) ||
                string.Equals(alias, "default", StringComparison.OrdinalIgnoreCase) ||
                string.Equals(alias, "default_lit", StringComparison.OrdinalIgnoreCase) ||
                string.Equals(alias, "standard", StringComparison.OrdinalIgnoreCase))
            {
                return ResolveDefaultLitShader(pipeline);
            }

            if (string.Equals(alias, "unlit", StringComparison.OrdinalIgnoreCase))
            {
                return ResolveDefaultUnlitShader(pipeline);
            }

            if (string.Equals(alias, "urp_lit", StringComparison.OrdinalIgnoreCase))
            {
                return TryFindShader(UrpLitShaders);
            }

            if (string.Equals(alias, "hdrp_lit", StringComparison.OrdinalIgnoreCase))
            {
                return TryFindShader(HdrpLitShaders);
            }

            if (string.Equals(alias, "built_in_lit", StringComparison.OrdinalIgnoreCase))
            {
                return TryFindShader(BuiltInLitShaders);
            }

            return null;
        }

        private static Shader TryFindShader(params string[] shaderNames)
        {
            foreach (var shaderName in shaderNames)
            {
                var shader = Shader.Find(shaderName);
                if (shader != null)
                {
                    return shader;
                }
            }
            return null;
        }

        private static void WarnIfPipelineMismatch(string shaderName, PipelineKind activePipeline)
        {
            if (string.IsNullOrEmpty(shaderName))
            {
                return;
            }

            var lowerName = shaderName.ToLowerInvariant();
            bool shaderLooksUrp = lowerName.Contains("universal render pipeline") || lowerName.Contains("urp/");
            bool shaderLooksHdrp = lowerName.Contains("high definition render pipeline") || lowerName.Contains("hdrp/");
            bool shaderLooksSrp = shaderLooksUrp || shaderLooksHdrp;
            bool shaderLooksBuiltin = LooksLikeBuiltInShader(lowerName, shaderLooksSrp);

            switch (activePipeline)
            {
                case PipelineKind.HighDefinition:
                    if (shaderLooksUrp)
                    {
                        McpLog.Warn($"[RenderPipelineUtility] Active pipeline is HDRP but shader '{shaderName}' looks URP-based. Asset may appear incorrect.");
                    }
                    else if (shaderLooksBuiltin && !shaderLooksHdrp)
                    {
                        McpLog.Warn($"[RenderPipelineUtility] Active pipeline is HDRP but shader '{shaderName}' looks Built-in. Consider using an HDRP shader for correct results.");
                    }
                    break;
                case PipelineKind.Universal:
                    if (shaderLooksHdrp)
                    {
                        McpLog.Warn($"[RenderPipelineUtility] Active pipeline is URP but shader '{shaderName}' looks HDRP-based. Asset may appear incorrect.");
                    }
                    else if (shaderLooksBuiltin && !shaderLooksUrp)
                    {
                        McpLog.Warn($"[RenderPipelineUtility] Active pipeline is URP but shader '{shaderName}' looks Built-in. Consider using a URP shader for correct results.");
                    }
                    break;
                case PipelineKind.BuiltIn:
                    if (shaderLooksSrp)
                    {
                        McpLog.Warn($"[RenderPipelineUtility] Active pipeline is Built-in but shader '{shaderName}' targets URP/HDRP. Asset may not render as expected.");
                    }
                    break;
            }
        }

        internal static bool IsMaterialInvalidForActivePipeline(Material material, out string reason)
        {
            reason = null;
            if (material == null)
            {
                reason = "missing_material";
                return true;
            }

            Shader shader = material.shader;
            if (shader == null)
            {
                reason = "missing_shader";
                return true;
            }

            if (IsErrorShader(shader))
            {
                reason = "error_shader";
                return true;
            }

            var pipeline = GetActivePipeline();
            if (IsPipelineMismatch(shader.name, pipeline))
            {
                reason = "pipeline_mismatch";
                return true;
            }

            return false;
        }

        private static bool IsErrorShader(Shader shader)
        {
            if (shader == null)
            {
                return true;
            }

            if (shader == Shader.Find("Hidden/InternalErrorShader"))
            {
                return true;
            }

            string shaderName = shader.name ?? string.Empty;
            return shaderName.IndexOf("InternalErrorShader", StringComparison.OrdinalIgnoreCase) >= 0;
        }

        private static bool IsPipelineMismatch(string shaderName, PipelineKind activePipeline)
        {
            if (string.IsNullOrEmpty(shaderName))
            {
                return true;
            }

            string lowerName = shaderName.ToLowerInvariant();
            bool shaderLooksUrp = lowerName.Contains("universal render pipeline") || lowerName.Contains("urp/");
            bool shaderLooksHdrp = lowerName.Contains("high definition render pipeline") || lowerName.Contains("hdrp/");
            bool shaderLooksSrp = shaderLooksUrp || shaderLooksHdrp;
            bool shaderLooksBuiltin = LooksLikeBuiltInShader(lowerName, shaderLooksSrp);

            return activePipeline switch
            {
                PipelineKind.HighDefinition => shaderLooksUrp || (shaderLooksBuiltin && !shaderLooksHdrp),
                PipelineKind.Universal => shaderLooksHdrp || (shaderLooksBuiltin && !shaderLooksUrp),
                PipelineKind.BuiltIn => shaderLooksSrp,
                _ => false,
            };
        }

        internal static Material GetOrCreateDefaultVFXMaterial(VFXComponentType componentType)
        {
            var pipeline = GetActivePipeline();
            string cacheKey = $"{pipeline}_{componentType}";

            if (s_DefaultVFXMaterials.TryGetValue(cacheKey, out Material cachedMaterial) && cachedMaterial != null)
            {
                return cachedMaterial;
            }

            Material material = null;

            if (pipeline == PipelineKind.BuiltIn)
            {
                string builtinPath = componentType == VFXComponentType.ParticleSystem
                    ? "Default-Particle.mat"
                    : "Default-Line.mat";

                material = AssetDatabase.GetBuiltinExtraResource<Material>(builtinPath);
            }

            if (material == null)
            {
                Shader shader = ResolveDefaultVFXShader(pipeline, componentType);
                if (shader == null)
                {
                    shader = Shader.Find("Unlit/Color");
                }

                if (shader != null)
                {
                    material = new Material(shader);
                    material.name = $"Auto_Default_{componentType}_{pipeline}";

                    // Set default color (white is standard for VFX)
                    if (material.HasProperty("_Color"))
                    {
                        material.SetColor("_Color", Color.white);
                    }
                    if (material.HasProperty("_BaseColor"))
                    {
                        material.SetColor("_BaseColor", Color.white);
                    }

                    if (componentType == VFXComponentType.ParticleSystem)
                    {
                        material.renderQueue = 3000;
                        if (material.HasProperty("_Mode"))
                        {
                            material.SetFloat("_Mode", 2);
                        }
                        if (material.HasProperty("_SrcBlend"))
                        {
                            material.SetFloat("_SrcBlend", (float)UnityEngine.Rendering.BlendMode.SrcAlpha);
                        }
                        if (material.HasProperty("_DstBlend"))
                        {
                            material.SetFloat("_DstBlend", (float)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
                        }
                        if (material.HasProperty("_ZWrite"))
                        {
                            material.SetFloat("_ZWrite", 0);
                        }
                    }

                    McpLog.Info($"[RenderPipelineUtility] Created default VFX material for {componentType} using {shader.name}");
                }
            }

            if (material != null)
            {
                s_DefaultVFXMaterials[cacheKey] = material;
            }

            return material;
        }

        private static Shader ResolveDefaultVFXShader(PipelineKind pipeline, VFXComponentType componentType)
        {
            if (componentType == VFXComponentType.ParticleSystem)
            {
                return pipeline switch
                {
                    PipelineKind.Universal => TryFindShader(UrpParticleShaders) ?? ResolveDefaultUnlitShader(pipeline),
                    PipelineKind.HighDefinition => TryFindShader(HdrpUnlitShaders) ?? ResolveDefaultUnlitShader(pipeline),
                    PipelineKind.BuiltIn => TryFindShader(BuiltInParticleShaders) ?? ResolveDefaultUnlitShader(pipeline),
                    PipelineKind.Custom => TryFindShader(UrpParticleShaders)
                                           ?? TryFindShader(BuiltInParticleShaders)
                                           ?? TryFindShader(HdrpUnlitShaders)
                                           ?? ResolveDefaultUnlitShader(pipeline),
                    _ => ResolveDefaultUnlitShader(pipeline),
                };
            }

            return ResolveDefaultUnlitShader(pipeline);
        }

        private static bool LooksLikeBuiltInShader(string lowerName, bool shaderLooksSrp)
        {
            if (string.IsNullOrEmpty(lowerName))
            {
                return false;
            }

            if (lowerName == "standard" ||
                lowerName.StartsWith("legacy shaders/", StringComparison.Ordinal) ||
                lowerName.StartsWith("mobile/", StringComparison.Ordinal))
            {
                return true;
            }

            // Built-in non-SRP shader families commonly seen on particles/old content.
            if (!shaderLooksSrp &&
                (lowerName.StartsWith("particles/", StringComparison.Ordinal) ||
                 lowerName.StartsWith("unlit/", StringComparison.Ordinal)))
            {
                return true;
            }

            return false;
        }

        internal static Material GetOrCreateDefaultSceneMaterial()
        {
            var pipeline = GetActivePipeline();
            string cacheKey = $"{pipeline}_scene";
            if (s_DefaultSceneMaterials.TryGetValue(cacheKey, out Material cached) && cached != null)
            {
                return cached;
            }

            Material material = null;
            Shader shader = ResolveDefaultLitShader(pipeline) ?? ResolveDefaultUnlitShader(pipeline);
            if (shader == null)
            {
                shader = Shader.Find("Unlit/Color");
            }

            if (shader != null)
            {
                material = new Material(shader);
                material.name = $"Auto_Default_Scene_{pipeline}";
                if (material.HasProperty("_Color"))
                {
                    material.SetColor("_Color", Color.white);
                }
                if (material.HasProperty("_BaseColor"))
                {
                    material.SetColor("_BaseColor", Color.white);
                }
                McpLog.Info($"[RenderPipelineUtility] Created default scene material using {shader.name}");
            }

            if (material != null)
            {
                s_DefaultSceneMaterials[cacheKey] = material;
            }

            return material;
        }
    }
}
