using System;
using System.Collections;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace IndieExchange
{
    internal sealed class AdView : MonoBehaviour
    {
        private PreparedAd ad;
        private Action opened, clicked, closed;
        private RectTransform safe;
        private bool banner, ending, notified, paused;
        private double visibleSince;
        private GameObject ownEventSystem;
        private Text status;

        public static AdView Create(Transform parent, PreparedAd ad, IndiePlacement placement, Action opened, Action clicked, Action closed)
        {
            var go = new GameObject("Indie Exchange Ad", typeof(RectTransform));
            go.transform.SetParent(parent, false);
            var view = go.AddComponent<AdView>();
            try { view.Build(ad, placement, opened, clicked, closed); return view; }
            catch { view.closed = null; Destroy(go); throw; }
        }

        private void Build(PreparedAd source, IndiePlacement placement, Action onOpen, Action onClick, Action onClose)
        {
            ad = source; opened = onOpen; clicked = onClick; closed = onClose;
            banner = placement.format == IndieAdFormat.Banner;
            var canvas = gameObject.AddComponent<Canvas>();
            canvas.renderMode = RenderMode.ScreenSpaceOverlay;
            canvas.sortingOrder = 32760;
            var scaler = gameObject.AddComponent<CanvasScaler>();
            scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
            scaler.referenceResolution = new Vector2(1080, 1920);
            scaler.matchWidthOrHeight = .5f;
            gameObject.AddComponent<GraphicRaycaster>();
            EnsureEvents();
            if (!banner)
            {
                var background = Panel(transform, "Background", new Color(.035f,.05f,.075f,1));
                Stretch(background);
            }
            safe = new GameObject("Safe area", typeof(RectTransform)).GetComponent<RectTransform>();
            safe.SetParent(transform, false);
            UpdateSafeArea();
            if (banner)
            {
                // Fixed logical slot scaled to fit the safe width; never crop the creative.
                var dimensions = placement.bannerSize.Split('x');
                var width = float.Parse(dimensions[0], System.Globalization.CultureInfo.InvariantCulture);
                var height = float.Parse(dimensions[1], System.Globalization.CultureInfo.InvariantCulture);
                var panel = Panel(safe, "Banner", Color.black);
                var edge = placement.bannerPosition == "top" ? 1f : 0f;
                panel.anchorMin = panel.anchorMax = new Vector2(.5f, edge);
                panel.pivot = new Vector2(.5f, edge);
                panel.sizeDelta = new Vector2(width, height);
                var layout = panel.gameObject.AddComponent<BannerLayout>();
                layout.logicalSize = new Vector2(width, height);
                layout.safe = safe;
                var raw = Image(panel, ad.texture); Stretch(raw.rectTransform);
                raw.raycastTarget = true;
                var button = raw.gameObject.AddComponent<Button>(); button.onClick.AddListener(Click);
                var label = Label(panel, ad.test ? "TEST AD" : "Ad", 12, TextAnchor.UpperLeft);
                label.color = Color.white;
                label.gameObject.AddComponent<Outline>();
                Stretch(label.rectTransform);
            }
            else
            {
                var title = Label(safe, ad.test ? "INDIE EXCHANGE  /  TEST AD" : "ADVERTISEMENT", 25, TextAnchor.MiddleLeft);
                Box(title.rectTransform, new Vector2(.04f,.92f), new Vector2(.77f,.99f));
                var close = Button(safe, "Close", Close);
                Box(close, new Vector2(.79f,.92f), new Vector2(.97f,.985f));
                var content = new GameObject("Creative area", typeof(RectTransform)).GetComponent<RectTransform>();
                content.SetParent(safe, false); Box(content, new Vector2(.03f,.19f), new Vector2(.97f,.9f));
                var raw = Image(content, ad.texture);
                var fit = raw.gameObject.AddComponent<AspectRatioFitter>();
                fit.aspectMode = AspectRatioFitter.AspectMode.FitInParent;
                fit.aspectRatio = (float)ad.texture.width / ad.texture.height;
                var headline = Label(safe, ad.response.headline ?? ad.response.gameName, 35, TextAnchor.MiddleCenter);
                Box(headline.rectTransform, new Vector2(.04f,.12f), new Vector2(.96f,.19f));
                var install = Button(safe, ad.test ? "Test install button" : string.IsNullOrWhiteSpace(ad.response.ctaText) ? "Install" : ad.response.ctaText, Click);
                Box(install, new Vector2(.12f,.035f), new Vector2(.88f,.11f));
                status = Label(safe, ad.test ? "Preview only. No credits or tracking." : "", 20, TextAnchor.MiddleCenter);
                Box(status.rectTransform, new Vector2(.04f,0), new Vector2(.96f,.035f));
            }
            visibleSince = Time.realtimeSinceStartupAsDouble;
            if (ad.video != null)
            {
                ad.video.SetDirectAudioMute(0, false);
                ad.video.Play();
            }
            StartCoroutine(Visible());
        }

        private IEnumerator Visible()
        {
            yield return null;
            while (!ending && !notified)
            {
                if (!paused && (Application.isFocused || ad.test && Application.isBatchMode) && (!banner || Time.realtimeSinceStartupAsDouble - visibleSince >= 1))
                {
                    notified = true;
                    opened?.Invoke();
                }
                yield return null;
            }
        }

        private void Update()
        {
            UpdateSafeArea();
            if (ad != null && ad.failed) Close();
            if (!banner)
            {
#if ENABLE_LEGACY_INPUT_MANAGER || !ENABLE_INPUT_SYSTEM
                if (Input.GetKeyDown(KeyCode.Escape)) Close();
#endif
            }
            if (paused || !(Application.isFocused || ad.test && Application.isBatchMode)) visibleSince = Time.realtimeSinceStartupAsDouble;
        }

        private void OnApplicationPause(bool value)
        {
            paused = value;
            visibleSince = Time.realtimeSinceStartupAsDouble;
            if (ad?.video != null) { if (value) ad.video.Pause(); else ad.video.Play(); }
        }

        private void UpdateSafeArea()
        {
            if (safe == null || Screen.width == 0 || Screen.height == 0) return;
            var area = Screen.safeArea;
            safe.anchorMin = new Vector2(area.xMin / Screen.width, area.yMin / Screen.height);
            safe.anchorMax = new Vector2(area.xMax / Screen.width, area.yMax / Screen.height);
            safe.offsetMin = safe.offsetMax = Vector2.zero;
        }

        private void Click()
        {
            if (ad.test) { if (status != null) status.text = "Install button works. Live ads open the store."; return; }
            if (!AdTransport.StoreUrl(ad.response.destinationUrl)) return;
            // Preserve impression-before-click ordering even if tapped immediately.
            if (!notified && !banner) { notified = true; opened?.Invoke(); }
            if (notified) clicked?.Invoke();
            Application.OpenURL(ad.response.destinationUrl);
        }

        internal void Close()
        {
            if (ending) return;
            ending = true;
            gameObject.SetActive(false);
            closed?.Invoke();
            Destroy(gameObject);
        }
        private void OnDestroy()
        {
            if (ownEventSystem != null) Destroy(ownEventSystem);
            if (!ending) { ending = true; closed?.Invoke(); }
        }

        private void EnsureEvents()
        {
            if (EventSystem.current != null) return;
            ownEventSystem = new GameObject("Indie Exchange UI input", typeof(EventSystem));
            ownEventSystem.transform.SetParent(transform, false);
#if ENABLE_INPUT_SYSTEM
            var type = Type.GetType("UnityEngine.InputSystem.UI.InputSystemUIInputModule, Unity.InputSystem");
            if (type == null) throw new InvalidOperationException("Input System UI module is missing.");
            ownEventSystem.AddComponent(type);
#else
            ownEventSystem.AddComponent<StandaloneInputModule>();
#endif
        }

        private static RectTransform Panel(Transform parent, string name, Color color)
        {
            var go = new GameObject(name, typeof(RectTransform), typeof(Image));
            go.transform.SetParent(parent, false); go.GetComponent<Image>().color = color;
            return go.GetComponent<RectTransform>();
        }
        private static RawImage Image(Transform parent, Texture texture)
        {
            var go = new GameObject("Artwork", typeof(RectTransform), typeof(RawImage));
            go.transform.SetParent(parent, false);
            var image = go.GetComponent<RawImage>(); image.texture = texture; image.raycastTarget = false;
            return image;
        }
        private static Text Label(Transform parent, string text, int size, TextAnchor align)
        {
            var go = new GameObject("Label", typeof(RectTransform), typeof(Text)); go.transform.SetParent(parent, false);
            var label = go.GetComponent<Text>(); label.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
            label.text = text; label.fontSize = size; label.alignment = align; label.color = Color.white; label.raycastTarget = false;
            label.supportRichText = false;
            label.resizeTextForBestFit = true; label.resizeTextMinSize = Math.Max(10, size / 2); label.resizeTextMaxSize = size;
            return label;
        }
        private static RectTransform Button(Transform parent, string text, UnityEngine.Events.UnityAction action)
        {
            var rect = Panel(parent, text, new Color(.12f,.48f,.39f));
            var button = rect.gameObject.AddComponent<Button>(); button.onClick.AddListener(action);
            var label = Label(rect, text, 28, TextAnchor.MiddleCenter);
            Stretch(label.rectTransform);
            return rect;
        }
        private static void Stretch(RectTransform rect) => Box(rect, Vector2.zero, Vector2.one);
        private static void Box(RectTransform rect, Vector2 min, Vector2 max)
        {
            rect.anchorMin = min; rect.anchorMax = max; rect.offsetMin = rect.offsetMax = Vector2.zero;
        }
    }

    internal sealed class BannerLayout : MonoBehaviour
    {
        public RectTransform safe;
        public Vector2 logicalSize;
        private void LateUpdate()
        {
            if (safe == null) return;
            // Match a dp-style logical slot, using 160 dpi when the device reports no density.
            var canvas = GetComponentInParent<Canvas>();
            var density = Screen.dpi > 0 ? Mathf.Clamp(Screen.dpi / 160f, 1, 4) : 1;
            var scale = Mathf.Min(density / canvas.scaleFactor, safe.rect.width / logicalSize.x, safe.rect.height / logicalSize.y);
            ((RectTransform)transform).sizeDelta = logicalSize * scale;
        }
    }
}
