using System;
using System.Collections;
using UnityEngine;

/// <summary>
/// Reusable subtitle player that renders and plays subtitle lines loaded from JSON.
/// </summary>
public class SubtitleSequencePlayer : MonoBehaviour
{
    [Serializable]
    private struct SubtitleLine
    {
        public string speaker;
        public string text;
        public float duration;
    }

    [Serializable]
    private struct SubtitleFile
    {
        public SubtitleLine[] lines;
    }

    [Header("Optional Default Data")]
    [Tooltip("Used only if trigger zone calls PlayDefault().")]
    [SerializeField] private TextAsset defaultSubtitleJson;

    [SerializeField] private float typewriterCharsPerSecond = 40f;
    [SerializeField] private float fadeDuration = 0.2f;
    [SerializeField] private float gapBetweenLines = 0.15f;

    [Header("Visual")]
    [SerializeField] private int fontSize = 28;
    [SerializeField] private int speakerFontSize = 18;
    [SerializeField] private float horizontalPadding = 28f;
    [SerializeField] private float bottomOffset = 56f;
    [SerializeField] private Color textColor = new Color(1f, 1f, 1f, 1f);
    [SerializeField] private Color speakerColor = new Color(1f, 0.85f, 0.35f, 1f);
    [SerializeField] private Color backgroundColor = new Color(0f, 0f, 0f, 0.62f);

    private string m_currentSpeaker;
    private string m_currentText;
    private GUIStyle m_textStyle;
    private GUIStyle m_speakerStyle;
    private Texture2D m_background;
    private bool m_isShowing;
    private bool m_isPlaying;
    private float m_alpha;
    private SubtitleLine[] m_runtimeLines = Array.Empty<SubtitleLine>();

    public bool IsPlaying => m_isPlaying;

    public bool TryPlay(TextAsset subtitleJson, float initialDelay = 0f)
    {
        if (m_isPlaying)
            return false;

        if (!TryReadLinesFromJson(subtitleJson, out SubtitleLine[] parsedLines))
            return false;

        m_runtimeLines = parsedLines;
        StartCoroutine(PlaySequence(initialDelay));
        return true;
    }

    public bool PlayDefault(float initialDelay = 0f)
    {
        return TryPlay(defaultSubtitleJson, initialDelay);
    }

    private IEnumerator PlaySequence(float initialDelay)
    {
        m_isPlaying = true;

        if (m_runtimeLines == null || m_runtimeLines.Length == 0)
        {
            m_isPlaying = false;
            yield break;
        }

        if (initialDelay > 0f)
            yield return new WaitForSeconds(initialDelay);

        for (int i = 0; i < m_runtimeLines.Length; i++)
        {
            if (string.IsNullOrWhiteSpace(m_runtimeLines[i].text) || m_runtimeLines[i].duration <= 0f)
                continue;

            yield return StartCoroutine(ShowLine(m_runtimeLines[i]));

            if (gapBetweenLines > 0f)
                yield return new WaitForSeconds(gapBetweenLines);
        }

        m_currentSpeaker = string.Empty;
        m_currentText = string.Empty;
        m_isPlaying = false;
    }

    private bool TryReadLinesFromJson(TextAsset subtitleJson, out SubtitleLine[] parsedLines)
    {
        parsedLines = Array.Empty<SubtitleLine>();

        if (subtitleJson == null || string.IsNullOrWhiteSpace(subtitleJson.text))
            return false;

        SubtitleFile file;
        try
        {
            file = JsonUtility.FromJson<SubtitleFile>(subtitleJson.text);
        }
        catch
        {
            return false;
        }

        if (file.lines == null || file.lines.Length == 0)
            return false;

        parsedLines = file.lines;
        return true;
    }

    private IEnumerator ShowLine(SubtitleLine line)
    {
        m_currentSpeaker = line.speaker;
        m_currentText = string.Empty;
        m_isShowing = true;

        if (fadeDuration > 0f)
        {
            float fadeIn = 0f;
            while (fadeIn < fadeDuration)
            {
                fadeIn += Time.deltaTime;
                m_alpha = Mathf.Clamp01(fadeIn / fadeDuration);
                yield return null;
            }
        }
        else
        {
            m_alpha = 1f;
        }

        float typeTime = 0f;
        int totalChars = line.text.Length;
        int visibleChars = 0;
        if (typewriterCharsPerSecond > 0f)
        {
            while (m_currentText.Length < totalChars)
            {
                typeTime += Time.deltaTime;
                visibleChars = Mathf.Clamp(Mathf.FloorToInt(typeTime * typewriterCharsPerSecond), 0, totalChars);
                m_currentText = line.text.Substring(0, visibleChars);
                yield return null;
            }
        }
        else
        {
            m_currentText = line.text;
        }

        float holdDuration = Mathf.Max(0f, line.duration - (typewriterCharsPerSecond > 0f ? typeTime : 0f));
        if (holdDuration > 0f)
            yield return new WaitForSeconds(holdDuration);

        if (fadeDuration > 0f)
        {
            float fadeOut = fadeDuration;
            while (fadeOut > 0f)
            {
                fadeOut -= Time.deltaTime;
                m_alpha = Mathf.Clamp01(fadeOut / fadeDuration);
                yield return null;
            }
        }

        m_alpha = 0f;
        m_isShowing = false;
    }

    private void OnGUI()
    {
        if (!m_isShowing || string.IsNullOrEmpty(m_currentText))
            return;

        EnsureStyles();

        float maxWidth = Mathf.Min(Screen.width - 24f, 940f);
        float textWidth = maxWidth - horizontalPadding * 2f;
        float speakerHeight = string.IsNullOrEmpty(m_currentSpeaker)
            ? 0f
            : m_speakerStyle.CalcHeight(new GUIContent(m_currentSpeaker), textWidth);
        float textHeight = m_textStyle.CalcHeight(new GUIContent(string.IsNullOrEmpty(m_currentText) ? " " : m_currentText), textWidth);

        float boxWidth = maxWidth;
        float boxHeight = speakerHeight + textHeight + 28f;
        float boxX = (Screen.width - boxWidth) * 0.5f;
        float boxY = Screen.height - bottomOffset - boxHeight;

        Rect boxRect = new Rect(boxX, boxY, boxWidth, boxHeight);
        Color previousColor = GUI.color;
        GUI.color = new Color(1f, 1f, 1f, m_alpha);
        GUI.DrawTexture(boxRect, m_background);

        float yOffset = boxRect.y + 10f;
        if (!string.IsNullOrEmpty(m_currentSpeaker))
        {
            Rect speakerRect = new Rect(
                boxRect.x + horizontalPadding,
                yOffset,
                textWidth,
                speakerHeight);
            GUI.Label(speakerRect, m_currentSpeaker, m_speakerStyle);
            yOffset += speakerHeight + 2f;
        }

        Rect textRect = new Rect(
            boxRect.x + horizontalPadding,
            yOffset,
            textWidth,
            textHeight);

        GUI.Label(textRect, m_currentText, m_textStyle);
        GUI.color = previousColor;
    }

    private void EnsureStyles()
    {
        if (m_textStyle != null)
            return;

        m_background = new Texture2D(1, 1);
        m_background.SetPixel(0, 0, backgroundColor);
        m_background.Apply();

        m_textStyle = new GUIStyle(GUI.skin.label)
        {
            alignment = TextAnchor.MiddleCenter,
            fontSize = fontSize,
            wordWrap = true,
            richText = false,
            clipping = TextClipping.Clip,
        };
        m_textStyle.normal.textColor = textColor;

        m_speakerStyle = new GUIStyle(GUI.skin.label)
        {
            alignment = TextAnchor.MiddleCenter,
            fontSize = speakerFontSize,
            fontStyle = FontStyle.Bold,
            wordWrap = false,
            clipping = TextClipping.Clip,
        };
        m_speakerStyle.normal.textColor = speakerColor;
    }

    private void OnDestroy()
    {
        if (m_background != null)
            Destroy(m_background);
    }
}
