using System;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace Streamingle.Editor.Utilities
{
    public static class ResolutionScreenshotMenu
    {
        private const string MenuRoot = "Tools/Streamingle/Capture/Screenshots/Resolution/";
        private const int MenuPriority = 500;
        private const string SaveFolderPrefsKey = "ScreenshotTool_SaveFolder";
        private const string PrefixPrefsKey = "ScreenshotTool_Prefix";

        [MenuItem(MenuRoot + "Custom...", false, MenuPriority)]
        private static void ShowCustomWindow()
        {
            ResolutionScreenshotWindow.ShowWindow();
        }

        [MenuItem(MenuRoot + "Capture 1280x720 (HD)", false, MenuPriority + 10)]
        private static void Capture1280x720()
        {
            Capture(1280, 720);
        }

        [MenuItem(MenuRoot + "Capture 1920x1080 (FHD)", false, MenuPriority + 11)]
        private static void Capture1920x1080()
        {
            Capture(1920, 1080);
        }

        [MenuItem(MenuRoot + "Capture 2560x1440 (QHD)", false, MenuPriority + 12)]
        private static void Capture2560x1440()
        {
            Capture(2560, 1440);
        }

        [MenuItem(MenuRoot + "Capture 3840x2160 (4K)", false, MenuPriority + 13)]
        private static void Capture3840x2160()
        {
            Capture(3840, 2160);
        }

        [MenuItem(MenuRoot + "Capture 1080x1920 (Portrait)", false, MenuPriority + 20)]
        private static void Capture1080x1920()
        {
            Capture(1080, 1920);
        }

        [MenuItem(MenuRoot + "Capture 1080x1080 (Square)", false, MenuPriority + 21)]
        private static void Capture1080x1080()
        {
            Capture(1080, 1080);
        }

        [MenuItem(MenuRoot + "Open Screenshot Folder", false, MenuPriority + 100)]
        private static void OpenScreenshotFolder()
        {
            string folder = GetScreenshotFolder();
            Directory.CreateDirectory(folder);
            EditorUtility.RevealInFinder(folder);
        }

        internal static void Capture(int width, int height, Camera camera = null, bool transparentBackground = false, bool revealAfterCapture = false)
        {
            if (width <= 0 || height <= 0)
            {
                EditorUtility.DisplayDialog("Screenshot", "Width and height must be greater than zero.", "OK");
                return;
            }

            camera ??= FindDefaultCaptureCamera();
            if (camera == null)
            {
                EditorUtility.DisplayDialog(
                    "Screenshot",
                    "No capture camera found. Select a Camera in the hierarchy or add a MainCamera-tagged camera.",
                    "OK");
                return;
            }

            string folder = GetScreenshotFolder();
            Directory.CreateDirectory(folder);

            string filePath = BuildFilePath(folder, camera, width, height);
            try
            {
                RenderCameraToPng(camera, width, height, filePath, transparentBackground);
                Debug.Log($"[ResolutionScreenshot] Saved {width}x{height} screenshot: {filePath}");

                if (IsPathUnderAssets(filePath))
                {
                    AssetDatabase.Refresh();
                }

                if (revealAfterCapture)
                {
                    EditorUtility.RevealInFinder(filePath);
                }
            }
            catch (Exception ex)
            {
                Debug.LogError($"[ResolutionScreenshot] Capture failed: {ex.Message}");
                EditorUtility.DisplayDialog("Screenshot Failed", ex.Message, "OK");
            }
        }

        internal static Camera FindDefaultCaptureCamera()
        {
            var selectedObject = Selection.activeGameObject;
            if (selectedObject != null)
            {
                var selectedCamera = selectedObject.GetComponent<Camera>();
                if (IsUsableSceneCamera(selectedCamera))
                {
                    return selectedCamera;
                }

                selectedCamera = selectedObject.GetComponentInChildren<Camera>(true);
                if (IsUsableSceneCamera(selectedCamera))
                {
                    return selectedCamera;
                }
            }

            var mainCamera = Camera.main;
            if (IsUsableSceneCamera(mainCamera))
            {
                return mainCamera;
            }

            Camera bestCamera = null;
            var cameras = UnityEngine.Object.FindObjectsByType<Camera>(FindObjectsInactive.Exclude, FindObjectsSortMode.None);
            foreach (var sceneCamera in cameras)
            {
                if (!IsUsableSceneCamera(sceneCamera))
                {
                    continue;
                }

                if (bestCamera == null || sceneCamera.depth > bestCamera.depth)
                {
                    bestCamera = sceneCamera;
                }
            }

            if (bestCamera != null)
            {
                return bestCamera;
            }

            return SceneView.lastActiveSceneView != null ? SceneView.lastActiveSceneView.camera : null;
        }

        internal static string GetScreenshotFolder()
        {
            string defaultFolder = Path.GetFullPath(Path.Combine(Application.dataPath, "..", "Screenshots"));
            string savedFolder = EditorPrefs.GetString(SaveFolderPrefsKey, defaultFolder);
            return string.IsNullOrWhiteSpace(savedFolder) ? defaultFolder : Path.GetFullPath(savedFolder);
        }

        private static void RenderCameraToPng(Camera camera, int width, int height, string filePath, bool transparentBackground)
        {
            RenderTexture previousTargetTexture = camera.targetTexture;
            RenderTexture previousActiveTexture = RenderTexture.active;
            CameraClearFlags previousClearFlags = camera.clearFlags;
            Color previousBackgroundColor = camera.backgroundColor;

            RenderTexture renderTexture = null;
            Texture2D texture = null;

            try
            {
                int antiAliasing = Mathf.Max(1, QualitySettings.antiAliasing);
                renderTexture = RenderTexture.GetTemporary(
                    width,
                    height,
                    24,
                    RenderTextureFormat.ARGB32,
                    RenderTextureReadWrite.sRGB,
                    antiAliasing);

                camera.targetTexture = renderTexture;

                if (transparentBackground)
                {
                    camera.clearFlags = CameraClearFlags.SolidColor;
                    camera.backgroundColor = new Color(0f, 0f, 0f, 0f);
                }

                camera.Render();

                TextureFormat textureFormat = transparentBackground ? TextureFormat.ARGB32 : TextureFormat.RGB24;
                texture = new Texture2D(width, height, textureFormat, false);

                RenderTexture.active = renderTexture;
                texture.ReadPixels(new Rect(0, 0, width, height), 0, 0);
                texture.Apply();

                File.WriteAllBytes(filePath, texture.EncodeToPNG());
            }
            finally
            {
                camera.targetTexture = previousTargetTexture;
                camera.clearFlags = previousClearFlags;
                camera.backgroundColor = previousBackgroundColor;
                RenderTexture.active = previousActiveTexture;

                if (texture != null)
                {
                    UnityEngine.Object.DestroyImmediate(texture);
                }

                if (renderTexture != null)
                {
                    RenderTexture.ReleaseTemporary(renderTexture);
                }
            }
        }

        private static bool IsUsableSceneCamera(Camera camera)
        {
            return camera != null
                   && camera.isActiveAndEnabled
                   && camera.gameObject.scene.IsValid()
                   && !EditorUtility.IsPersistent(camera);
        }

        private static string BuildFilePath(string folder, Camera camera, int width, int height)
        {
            string prefix = SanitizeFilePart(EditorPrefs.GetString(PrefixPrefsKey, "Screenshot"));
            string sceneName = SanitizeFilePart(GetSceneName(camera));
            string cameraName = SanitizeFilePart(camera.name);
            string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmssfff");
            string fileName = $"{prefix}_{sceneName}_{cameraName}_{width}x{height}_{timestamp}.png";
            return Path.Combine(folder, fileName);
        }

        private static string GetSceneName(Camera camera)
        {
            Scene scene = camera.gameObject.scene;
            if (scene.IsValid() && !string.IsNullOrWhiteSpace(scene.name))
            {
                return scene.name;
            }

            Scene activeScene = SceneManager.GetActiveScene();
            return !string.IsNullOrWhiteSpace(activeScene.name) ? activeScene.name : "UntitledScene";
        }

        private static string SanitizeFilePart(string value)
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                return "Screenshot";
            }

            foreach (char invalidChar in Path.GetInvalidFileNameChars())
            {
                value = value.Replace(invalidChar, '_');
            }

            return value.Trim();
        }

        private static bool IsPathUnderAssets(string filePath)
        {
            string fullPath = Path.GetFullPath(filePath).Replace('\\', '/');
            string assetsPath = Path.GetFullPath(Application.dataPath).Replace('\\', '/');
            return fullPath.StartsWith(assetsPath + "/", StringComparison.OrdinalIgnoreCase);
        }
    }

    public sealed class ResolutionScreenshotWindow : EditorWindow
    {
        private Camera captureCamera;
        private int width = 1920;
        private int height = 1080;
        private bool transparentBackground;
        private bool revealAfterCapture = true;

        public static void ShowWindow()
        {
            var window = GetWindow<ResolutionScreenshotWindow>("Resolution Screenshot");
            window.minSize = new Vector2(360f, 230f);
            window.Show();
        }

        private void OnEnable()
        {
            captureCamera = ResolutionScreenshotMenu.FindDefaultCaptureCamera();
        }

        private void OnGUI()
        {
            EditorGUILayout.Space(8f);
            EditorGUILayout.LabelField("Resolution Screenshot", EditorStyles.boldLabel);
            EditorGUILayout.Space(4f);

            captureCamera = (Camera)EditorGUILayout.ObjectField("Camera", captureCamera, typeof(Camera), true);
            if (captureCamera == null)
            {
                EditorGUILayout.HelpBox("Uses selected Camera, Main Camera, then the first active scene Camera.", MessageType.Info);
            }

            EditorGUILayout.Space(6f);
            EditorGUILayout.BeginHorizontal();
            width = Mathf.Max(1, EditorGUILayout.IntField("Width", width));
            height = Mathf.Max(1, EditorGUILayout.IntField("Height", height));
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.Space(4f);
            EditorGUILayout.BeginHorizontal();
            AddPresetButton("HD", 1280, 720);
            AddPresetButton("FHD", 1920, 1080);
            AddPresetButton("QHD", 2560, 1440);
            AddPresetButton("4K", 3840, 2160);
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginHorizontal();
            AddPresetButton("Portrait", 1080, 1920);
            AddPresetButton("Square", 1080, 1080);
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.Space(6f);
            transparentBackground = EditorGUILayout.Toggle("Transparent Background", transparentBackground);
            revealAfterCapture = EditorGUILayout.Toggle("Reveal After Capture", revealAfterCapture);

            EditorGUILayout.Space(8f);
            EditorGUILayout.LabelField("Save Folder", ResolutionScreenshotMenu.GetScreenshotFolder(), EditorStyles.miniLabel);

            EditorGUILayout.Space(8f);
            if (GUILayout.Button("Capture", GUILayout.Height(32f)))
            {
                ResolutionScreenshotMenu.Capture(width, height, captureCamera, transparentBackground, revealAfterCapture);
            }
        }

        private void AddPresetButton(string label, int presetWidth, int presetHeight)
        {
            if (GUILayout.Button(label))
            {
                width = presetWidth;
                height = presetHeight;
            }
        }
    }
}
