using System;
using System.Collections.Generic;
using System.IO;
using AnyThinkAds.Api;
using Runtime.ADAggregator;
using UnityEngine;
using UnityEngine.EventSystems;

public sealed class IAAAdDebugSampleGui : MonoBehaviour
{
    private const float ReferenceWidth = 1080f;
    private const float ReferenceHeight = 2340f;
    private const int MaxLogEntries = 120;
    private const float StatusRefreshInterval = 1f;

    public ADConfig adConfig;
    public string userId = "debug_user_001";
    public string rewardedScenario = "reward_debug";
    public string interstitialScenario = "interstitial_debug";
    public bool autoInitialize = true;
    public bool forcePortrait = true;
    public bool captureUnityLogs = true;
    public bool showVerboseState = true;
    public bool openDebugUiOnButton = true;
    public bool autoWriteLogsToFile = true;

    private readonly List<string> _logs = new List<string>(MaxLogEntries);
    private Vector2 _pageScroll;
    private Vector2 _logScroll;
    private Vector2 _rewardedStatusScroll;
    private Vector2 _interstitialStatusScroll;
    private bool _initInvoked;
    private bool _initCallbackReceived;
    private bool _stylesInitialized;
    private GUIStyle _titleStyle;
    private GUIStyle _sectionStyle;
    private GUIStyle _textStyle;
    private GUIStyle _buttonStyle;
    private GUIStyle _textFieldStyle;
    private GUIStyle _logStyle;
    private bool _rewardedReadyCache;
    private bool _interstitialReadyCache;
    private bool _maskOpen;
    private string _lastEventLog = "<none>";
    private string _rewardedStatusCache = "Not refreshed yet.";
    private string _interstitialStatusCache = "Not refreshed yet.";
    private float _nextStatusRefreshTime;
    private string _sessionLogFilePath;
    private string _sessionLogDirectory;

    private void Awake()
    {
        Application.targetFrameRate = 60;
        Screen.sleepTimeout = SleepTimeout.NeverSleep;
        if (forcePortrait)
        {
            Screen.orientation = ScreenOrientation.Portrait;
        }

        if (captureUnityLogs)
        {
            Application.logMessageReceived += OnLogMessageReceived;
        }

        ADManager.Instance.GLOBAL_ShowAwardVideoBefore += OnRewardedBefore;
        ADManager.Instance.GLOBAL_ShowAwardVideoComplete += OnRewardedComplete;
        InitializeLogFile();
        AppendLog("IAA Ad Debug Sample is ready.");
    }

    private void Start()
    {
        if (autoInitialize)
        {
            TryInitialize();
        }

        RefreshStatusCaches(true);
    }

    private void OnDestroy()
    {
        if (captureUnityLogs)
        {
            Application.logMessageReceived -= OnLogMessageReceived;
        }

        ADManager.Instance.GLOBAL_ShowAwardVideoBefore -= OnRewardedBefore;
        ADManager.Instance.GLOBAL_ShowAwardVideoComplete -= OnRewardedComplete;
    }

    private void OnGUI()
    {
        EnsureStyles();

        var safeArea = Screen.safeArea;
        var scaleX = safeArea.width / ReferenceWidth;
        var scaleY = safeArea.height / ReferenceHeight;
        var previousMatrix = GUI.matrix;
        GUI.matrix = Matrix4x4.TRS(
            new Vector3(safeArea.x, safeArea.y, 0f),
            Quaternion.identity,
            new Vector3(scaleX, scaleY, 1f));

        GUILayout.BeginArea(new Rect(24f, 24f, ReferenceWidth - 48f, ReferenceHeight - 48f), GUI.skin.box);
        _pageScroll = GUILayout.BeginScrollView(_pageScroll, false, true);
        DrawHeader();
        DrawSetupSection();
        DrawStatusSection();
        DrawControlSection();
        DrawLogSection();
        GUILayout.EndScrollView();
        GUILayout.EndArea();

        GUI.matrix = previousMatrix;
    }

    private void DrawHeader()
    {
        GUILayout.Label("IAA Ad Debug Sample", _titleStyle);
        GUILayout.Label(
            $"Screen: {Screen.width} x {Screen.height} | SafeArea: {Screen.safeArea.width:0} x {Screen.safeArea.height:0}",
            _textStyle);
        GUILayout.Label(
            $"Reference Layout: {ReferenceWidth:0} x {ReferenceHeight:0} | Orientation: {Screen.orientation}",
            _textStyle);
        GUILayout.Space(10f);
    }

    private void DrawSetupSection()
    {
        GUILayout.Label("Setup", _sectionStyle);
        GUILayout.Label(
            $"ADConfig Asset: {(adConfig != null ? adConfig.name : "Missing")} | Init Invoked: {_initInvoked} | Init Callback: {_initCallbackReceived}",
            _textStyle);

        userId = DrawLabeledTextField("User ID", userId);
        rewardedScenario = DrawLabeledTextField("Rewarded Scenario", rewardedScenario);
        interstitialScenario = DrawLabeledTextField("Interstitial Scenario", interstitialScenario);

        var rewardedPlacement = adConfig?.BaseAwardAdKeyValue?.value ?? string.Empty;
        var interstitialPlacement = adConfig?.BaseInteractionAdKeyValue?.value ?? string.Empty;
        GUILayout.Label($"Rewarded Placement: {DisplayValue(rewardedPlacement)}", _textStyle);
        GUILayout.Label($"Interstitial Placement: {DisplayValue(interstitialPlacement)}", _textStyle);
        GUILayout.Space(6f);

        GUILayout.BeginHorizontal();
        if (GUILayout.Button("Initialize ADManager", _buttonStyle, GUILayout.Height(78f)))
        {
            TryInitialize();
        }
        if (GUILayout.Button("Open DebugUI", _buttonStyle, GUILayout.Height(78f)))
        {
            if (EnsureInitialized("Open DebugUI"))
            {
                OpenDebugUi();
            }
        }
        GUILayout.EndHorizontal();

        GUILayout.Space(10f);
    }

    private void DrawStatusSection()
    {
        GUILayout.Label("Runtime Status", _sectionStyle);

        if (_initInvoked)
        {
            RefreshStatusCaches();
            GUILayout.Label($"Rewarded Ready: {_rewardedReadyCache}", _textStyle);
            GUILayout.Label($"Interstitial Ready: {_interstitialReadyCache}", _textStyle);
            GUILayout.Label($"Mask Open: {_maskOpen}", _textStyle);
            GUILayout.Label($"Last EventLog: {_lastEventLog}", _textStyle);
        }
        else
        {
            GUILayout.Label("Rewarded Ready: not initialized", _textStyle);
            GUILayout.Label("Interstitial Ready: not initialized", _textStyle);
        }

        var options = ToponAdController.CurrentOptions;
        if (options != null)
        {
            GUILayout.Label(
                $"Rewarded Auto/Prewarm/Retry: {options.RewardedAutoLoad}/{options.RewardedPrewarmOnInit}/{options.RewardedMaxLoadAttempts}@{options.RewardedLoadRetryDelayMs}ms",
                _textStyle);
            GUILayout.Label(
                $"Interstitial Auto/Prewarm/Retry: {options.InterstitialAutoLoad}/{options.InterstitialPrewarmOnInit}/{options.InterstitialMaxLoadAttempts}@{options.InterstitialLoadRetryDelayMs}ms",
                _textStyle);
            GUILayout.Label(
                $"Local Strategy Asset: {DisplayValue(options.LocalStrategyAssetPath)}",
                _textStyle);
        }

        if (showVerboseState && _initInvoked)
        {
            RefreshStatusCaches();
            GUILayout.Space(6f);
            if (GUILayout.Button("Refresh Status Snapshot", _buttonStyle, GUILayout.Height(64f)))
            {
                RefreshStatusCaches(true);
                AppendLog("Manual status snapshot refreshed.");
            }

            GUILayout.Label("Rewarded Status", _sectionStyle);
            _rewardedStatusScroll = GUILayout.BeginScrollView(_rewardedStatusScroll, GUILayout.Height(240f));
            GUILayout.Label(_rewardedStatusCache, _logStyle);
            GUILayout.EndScrollView();

            GUILayout.Label("Interstitial Status", _sectionStyle);
            _interstitialStatusScroll = GUILayout.BeginScrollView(_interstitialStatusScroll, GUILayout.Height(220f));
            GUILayout.Label(_interstitialStatusCache, _logStyle);
            GUILayout.EndScrollView();
        }

        GUILayout.Space(10f);
    }

    private void DrawControlSection()
    {
        GUILayout.Label("Ad Actions", _sectionStyle);

        GUILayout.BeginHorizontal();
        if (GUILayout.Button("Enter Rewarded Scene", _buttonStyle, GUILayout.Height(84f)))
        {
            if (EnsureInitialized("Enter Rewarded Scene"))
            {
                ADManager.Instance.EnterAdScenario(AD_Type.AwardVideo, rewardedScenario);
                AppendLog($"Enter rewarded scene requested. scenario={rewardedScenario}");
            }
        }

        if (GUILayout.Button("Enter Interstitial Scene", _buttonStyle, GUILayout.Height(84f)))
        {
            if (EnsureInitialized("Enter Interstitial Scene"))
            {
                ADManager.Instance.EnterAdScenario(AD_Type.Interaction, interstitialScenario);
                AppendLog($"Enter interstitial scene requested. scenario={interstitialScenario}");
            }
        }
        GUILayout.EndHorizontal();

        GUILayout.BeginHorizontal();
        if (GUILayout.Button("Load Rewarded", _buttonStyle, GUILayout.Height(84f)))
        {
            if (EnsureInitialized("Load Rewarded"))
            {
                ADManager.Instance.LoadAD(AD_Type.AwardVideo);
                AppendLog("Manual rewarded load requested.");
            }
        }

        if (GUILayout.Button("Async Play Rewarded", _buttonStyle, GUILayout.Height(84f)))
        {
            if (EnsureInitialized("Play Rewarded"))
            {
                ADManager.Instance.AsyncPlayAD(AD_Type.AwardVideo, rewardedScenario, result =>
                {
                    AppendLog($"Rewarded callback: {result}");
                });
                AppendLog($"AsyncPlayAD rewarded requested. scenario={rewardedScenario}");
            }
        }
        GUILayout.EndHorizontal();

        GUILayout.BeginHorizontal();
        if (GUILayout.Button("Load Interstitial", _buttonStyle, GUILayout.Height(84f)))
        {
            if (EnsureInitialized("Load Interstitial"))
            {
                ADManager.Instance.LoadAD(AD_Type.Interaction);
                AppendLog("Manual interstitial load requested.");
            }
        }

        if (GUILayout.Button("Async Play Interstitial", _buttonStyle, GUILayout.Height(84f)))
        {
            if (EnsureInitialized("Play Interstitial"))
            {
                ADManager.Instance.AsyncPlayAD(AD_Type.Interaction, interstitialScenario, result =>
                {
                    AppendLog($"Interstitial callback: {result}");
                });
                AppendLog($"AsyncPlayAD interstitial requested. scenario={interstitialScenario}");
            }
        }
        GUILayout.EndHorizontal();

        GUILayout.Space(10f);
    }

    private void DrawLogSection()
    {
        GUILayout.Label("Diagnostics", _sectionStyle);
        GUILayout.BeginHorizontal();
        if (GUILayout.Button("Clear Logs", _buttonStyle, GUILayout.Height(70f)))
        {
            _logs.Clear();
            AppendLog("Logs cleared.");
        }

        if (GUILayout.Button("Copy All Logs", _buttonStyle, GUILayout.Height(70f)))
        {
            GUIUtility.systemCopyBuffer = string.Join("\n", _logs);
            AppendLog($"Copied {_logs.Count} log lines to clipboard.");
        }

        if (GUILayout.Button("Log Current Strategy", _buttonStyle, GUILayout.Height(70f)))
        {
            var options = ToponAdController.CurrentOptions;
            if (options == null)
            {
                AppendLog("Current strategy unavailable: controller not initialized.");
            }
            else
            {
                AppendLog(
                    $"Strategy => RewardedAuto={options.RewardedAutoLoad}, RewardedPrewarm={options.RewardedPrewarmOnInit}, InterAuto={options.InterstitialAutoLoad}, InterPrewarm={options.InterstitialPrewarmOnInit}");
            }
        }
        GUILayout.EndHorizontal();

        GUILayout.BeginHorizontal();
        if (GUILayout.Button("Export Logs To File", _buttonStyle, GUILayout.Height(70f)))
        {
            ExportVisibleLogsToFile();
        }

        if (GUILayout.Button("Copy Log File Path", _buttonStyle, GUILayout.Height(70f)))
        {
            if (string.IsNullOrWhiteSpace(_sessionLogFilePath))
            {
                AppendLog("Log file path unavailable.");
            }
            else
            {
                GUIUtility.systemCopyBuffer = _sessionLogFilePath;
                AppendLog($"Copied log file path: {_sessionLogFilePath}");
            }
        }
        GUILayout.EndHorizontal();

        GUILayout.Label($"Log File: {DisplayValue(_sessionLogFilePath)}", _textStyle);

        _logScroll = GUILayout.BeginScrollView(_logScroll, GUILayout.Height(520f));
        foreach (var line in _logs)
        {
            GUILayout.Label(line, _logStyle);
        }
        GUILayout.EndScrollView();
    }

    private string DrawLabeledTextField(string label, string value)
    {
        GUILayout.BeginHorizontal();
        GUILayout.Label(label, _textStyle, GUILayout.Width(260f));
        value = GUILayout.TextField(value ?? string.Empty, _textFieldStyle, GUILayout.Height(64f));
        GUILayout.EndHorizontal();
        return value;
    }

    private bool TryInitialize()
    {
        if (_initInvoked)
        {
            AppendLog("Initialize skipped: ADManager already initialized by sample.");
            return true;
        }

        if (adConfig == null)
        {
            AppendLog("Initialize failed: sample ADConfig asset is missing.");
            return false;
        }

        if (string.IsNullOrWhiteSpace(adConfig.Id) || string.IsNullOrWhiteSpace(adConfig.Key))
        {
            AppendLog("Initialize warning: Taku AppId/AppKey is empty. Please fill the sample ADConfig asset.");
        }

        _initInvoked = true;
        var controller = new SampleToponAdController(
            onMaskChanged: isOpen =>
            {
                _maskOpen = isOpen;
                AppendLog($"Mask changed => open={isOpen}");
            },
            onEventLog: (eventTable, eventValue, eventMessage) =>
            {
                _lastEventLog = $"{eventTable}:{eventValue}:{eventMessage}";
                AppendLog($"EventLog => table={eventTable}, value={eventValue}, message={eventMessage}");
            });
        ADManager.Instance.Init(() =>
        {
            _initCallbackReceived = true;
            AppendLog("ADManager.Init callback received.");
        }, userId, adConfig, controller);
        AppendLog($"ADManager.Init invoked with userId={userId}");
        AppendLog($"Rewarded placement={adConfig.BaseAwardAdKeyValue?.value}, interstitial placement={adConfig.BaseInteractionAdKeyValue?.value}");
        return true;
    }

    private bool EnsureInitialized(string actionName)
    {
        if (TryInitialize())
        {
            return true;
        }

        AppendLog($"{actionName} cancelled: initialization failed.");
        return false;
    }

    private void RefreshStatusCaches(bool force = false)
    {
        if (!force && Time.unscaledTime < _nextStatusRefreshTime)
        {
            return;
        }

        _nextStatusRefreshTime = Time.unscaledTime + StatusRefreshInterval;
        _rewardedReadyCache = _initInvoked && ADManager.Instance.IsRealy(AD_Type.AwardVideo);
        _interstitialReadyCache = _initInvoked && ADManager.Instance.IsRealy(AD_Type.Interaction);
        _rewardedStatusCache = GetRewardedStatusText();
        _interstitialStatusCache = GetInterstitialStatusText();
    }

    private string GetRewardedStatusText()
    {
        var placement = adConfig?.BaseAwardAdKeyValue?.value;
        if (string.IsNullOrWhiteSpace(placement))
        {
            return "No rewarded placement configured.";
        }

        var options = ToponAdController.CurrentOptions;
        if (options?.RewardedAutoLoad ?? true)
        {
            return ATRewardedAutoVideo.Instance.checkAutoAdStatus(placement);
        }

        return ATRewardedVideo.Instance.checkAdStatus(placement);
    }

    private string GetInterstitialStatusText()
    {
        var placement = adConfig?.BaseInteractionAdKeyValue?.value;
        if (string.IsNullOrWhiteSpace(placement))
        {
            return "No interstitial placement configured.";
        }

        var options = ToponAdController.CurrentOptions;
        if (options?.InterstitialAutoLoad ?? true)
        {
            return ATInterstitialAutoAd.Instance.checkAutoAdStatus(placement);
        }

        return ATInterstitialAd.Instance.checkAdStatus(placement);
    }

    private void OnRewardedBefore(string placementId, string scenario)
    {
        AppendLog($"Rewarded before show => placement={placementId}, scenario={scenario}");
    }

    private void OnRewardedComplete(bool completed)
    {
        AppendLog($"Rewarded global complete => reward={completed}");
    }

    private void OnLogMessageReceived(string condition, string stackTrace, LogType type)
    {
        if (type != LogType.Error && type != LogType.Exception && type != LogType.Warning)
        {
            if (!condition.Contains("[Topon]") &&
                !condition.Contains("ATSDK") &&
                !condition.Contains("Rewarded") &&
                !condition.Contains("Interstitial") &&
                !condition.Contains("Taku"))
            {
                return;
            }
        }

        AppendLog($"[{type}] {condition}");
    }

    private void OpenDebugUi()
    {
        var options = ToponAdController.CurrentOptions;
        if (!openDebugUiOnButton)
        {
            AppendLog("DebugUI launch disabled by sample toggle.");
            return;
        }

        if (options != null && !string.IsNullOrWhiteSpace(options.DebuggerKey))
        {
            ATSDKAPI.showDebuggerUI(options.DebuggerKey);
            AppendLog($"Open DebugUI with debugger key={options.DebuggerKey}");
            return;
        }

        ATSDKAPI.showDebuggerUI();
        AppendLog("Open DebugUI with current SDK configuration.");
    }

    private void AppendLog(string message)
    {
        var line = $"{DateTime.Now:HH:mm:ss} {message}";
        if (_logs.Count >= MaxLogEntries)
        {
            _logs.RemoveAt(0);
        }

        _logs.Add(line);
        TryAppendLineToFile(line);
        _logScroll.y = float.MaxValue;
    }

    private void InitializeLogFile()
    {
        try
        {
            _sessionLogDirectory = Path.Combine(Application.persistentDataPath, "IAAAdDebugLogs");
            Directory.CreateDirectory(_sessionLogDirectory);
            var fileName = $"iaa-ad-debug-{DateTime.Now:yyyyMMdd-HHmmss}.log";
            _sessionLogFilePath = Path.Combine(_sessionLogDirectory, fileName);
            File.WriteAllText(_sessionLogFilePath,
                $"IAA Ad Debug Sample Log{Environment.NewLine}" +
                $"Created: {DateTime.Now:yyyy-MM-dd HH:mm:ss}{Environment.NewLine}" +
                $"persistentDataPath: {Application.persistentDataPath}{Environment.NewLine}" +
                $"----------------------------------------{Environment.NewLine}");
        }
        catch (Exception exception)
        {
            _sessionLogFilePath = null;
            Debug.LogWarning($"[IAA Sample] Failed to initialize log file: {exception.Message}");
        }
    }

    private void TryAppendLineToFile(string line)
    {
        if (!autoWriteLogsToFile || string.IsNullOrWhiteSpace(_sessionLogFilePath))
        {
            return;
        }

        try
        {
            File.AppendAllText(_sessionLogFilePath, line + Environment.NewLine);
        }
        catch (Exception exception)
        {
            Debug.LogWarning($"[IAA Sample] Failed to append log file: {exception.Message}");
        }
    }

    private void ExportVisibleLogsToFile()
    {
        try
        {
            if (string.IsNullOrWhiteSpace(_sessionLogDirectory))
            {
                InitializeLogFile();
            }

            var exportPath = Path.Combine(
                _sessionLogDirectory ?? Application.persistentDataPath,
                $"iaa-ad-debug-export-{DateTime.Now:yyyyMMdd-HHmmss}.log");
            File.WriteAllLines(exportPath, _logs);
            AppendLog($"Exported {_logs.Count} visible log lines to file: {exportPath}");
        }
        catch (Exception exception)
        {
            AppendLog($"Export log file failed: {exception.Message}");
        }
    }

    private static string DisplayValue(string value)
    {
        return string.IsNullOrWhiteSpace(value) ? "<empty>" : value;
    }

    private void EnsureStyles()
    {
        if (_stylesInitialized)
        {
            return;
        }

        _stylesInitialized = true;
        _titleStyle = new GUIStyle(GUI.skin.label)
        {
            fontSize = 54,
            fontStyle = FontStyle.Bold,
            alignment = TextAnchor.MiddleCenter,
            wordWrap = true
        };
        _sectionStyle = new GUIStyle(GUI.skin.label)
        {
            fontSize = 34,
            fontStyle = FontStyle.Bold,
            wordWrap = true
        };
        _textStyle = new GUIStyle(GUI.skin.label)
        {
            fontSize = 28,
            wordWrap = true
        };
        _buttonStyle = new GUIStyle(GUI.skin.button)
        {
            fontSize = 28,
            wordWrap = true
        };
        _textFieldStyle = new GUIStyle(GUI.skin.textField)
        {
            fontSize = 28
        };
        _logStyle = new GUIStyle(GUI.skin.label)
        {
            fontSize = 24,
            wordWrap = true,
            richText = false
        };
    }

    private sealed class SampleToponAdController : IAdController
    {
        private readonly ToponAdController _inner = new ToponAdController();
        private readonly Action<bool> _onMaskChanged;
        private readonly Action<string, string, string> _onEventLog;

        public SampleToponAdController(Action<bool> onMaskChanged, Action<string, string, string> onEventLog)
        {
            _onMaskChanged = onMaskChanged;
            _onEventLog = onEventLog;
        }

        public void Init(ADConfig adConfig, object[] args)
        {
            _inner.Init(adConfig, args);
        }

        public ADPlayer CreateAdPlayer(AD_Type type)
        {
            return _inner.CreateAdPlayer(type);
        }

        public void EventLog(string eventTable, string eventValue, string eventMessage = null)
        {
            _onEventLog?.Invoke(eventTable, eventValue, eventMessage);
            _inner.EventLog(eventTable, eventValue, eventMessage);
        }

        public void SetMask(bool isOpen)
        {
            _onMaskChanged?.Invoke(isOpen);
            _inner.SetMask(isOpen);
        }
    }
}
