using UnityEngine;
using UnityEngine.SceneManagement;
using TMPro;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
#endif

// 게임 오버 상태를 표현하고, 게임 점수와 UI를 관리하는 게임 매니저
// 씬에는 단 하나의 게임 매니저만 존재할 수 있다.
public class GameManager : MonoBehaviour {
    public enum TutorialGuideType {
        None,
        Jump,
        DoubleJump,
        Slide,
        Damage,
        Items
    }

    public enum TutorialItemType {
        HealthSushi,
        InvincibleRamen,
        Shield,
        SpeedShoes,
        MileageCard
    }

    public static GameManager instance; // 싱글톤을 할당할 전역 변수

    public bool isGameover = false; // 게임 오버 상태
    public TextMeshProUGUI scoreText; // 점수를 출력할 UI 텍스트
    public GameObject gameoverUI; // 게임 오버시 활성화 할 UI 게임 오브젝트

    private int score = 0; // 게임 점수
    private int currentLife; // 현재 남은 목숨
    private int stageMileage = 0; // 현재 플레이 중 획득한 마일리지
    private int totalMileage = 0; // 누적 보유 마일리지
    private TutorialGuideType tutorialGuideType = TutorialGuideType.None; // 현재 튜토리얼 안내 이미지 종류
    private string tutorialTitle = ""; // 튜토리얼 안내 제목
    private string tutorialMessage = ""; // 튜토리얼 안내 문구
    private Image[] lifeIcons; // 왼쪽 HUD의 목숨 도장 표시
    private GameObject tutorialGuideObject; // 화면 하단 이미지 튜토리얼 패널
    private Image tutorialMainImage; // 점프/슬라이드 대표 이미지
    private TextMeshProUGUI tutorialMultiplierText; // 2단 점프 x2 표시
    private TextMeshProUGUI tutorialTitleText; // 튜토리얼 제목
    private TextMeshProUGUI tutorialBodyText; // 튜토리얼 설명
    private Image[] tutorialItemImages; // 아이템 순서 설명 이미지
    private TMP_FontAsset tutorialFontAsset; // 한글 튜토리얼 문구용 런타임 폰트
    private float tutorialGuideSequenceStartTime; // 튜토리얼 안내 시작 시각
    private int currentTutorialGuideSequenceIndex = -1; // 현재 표시 중인 안내 단계
    private bool tutorialGuideSequenceComplete = false; // 기본 튜토리얼 안내 완료 여부
    private MapDefinition currentMapDefinition; // 현재 플레이 중인 맵 데이터
    private float stageElapsedTime = 0f; // 현재 맵 진행 시간
    private float stageDistance = 0f; // 현재 맵 진행 거리
    private bool stageCompleted = false; // 결과 화면이 이미 처리되었는지 여부

    public int maxLife = 3; // 시작 목숨
    public int initialMileage = 500; // 첫 시작시 지급할 초기 마일리지
    public bool grantInitialMileage = true; // 초기 마일리지 지급 여부
    public bool saveMileageImmediately = true; // 스테이지 정산 전까지는 획득 즉시 누적한다.
    public bool playTutorialGuideSequence = false; // 시작 시 입력/아이템 안내를 시간 기준으로 보여준다.
    public float itemInvincibleDuration = 2.2f; // 라멘 아이템 무적 지속 시간
    public float itemSpeedBoostAmount = 0.35f; // 운동화 아이템 즉시 속도 보정량
    public float mileageBonusDuration = 7f; // 마일리지 카드 보너스 지속 시간
    public int mileageBonusMultiplier = 2; // 마일리지 카드 획득 배율

    private const string TotalMileageKey = "UniRun.TotalMileage";
    private const string InitialMileageGrantedKey = "UniRun.InitialMileageGranted";
    private const string CharacterFaceResourcePath = "UI_JJ_Face";
    private const string PassportPanelResourcePath = "UI_PassportPanel";
    private const string LifeStampResourcePath = "UI_LifeStamp";
    private const string PassportItemSlotResourcePath = "UI_PassportItemSlot";
    private const float TutorialJumpGuideDuration = 3.5f;
    private const float TutorialDoubleJumpGuideDuration = 3.5f;
    private const float TutorialSlideGuideDuration = 3.5f;
    private const float TutorialItemGuideDuration = 5.2f;

    // 게임의 스피드를 올리는 변수
    public float gameSpeed = 1f;
    public float minGameSpeed = 1f;
    public float speedIncreaseRate = 0.05f;
    public float maxGameSpeed = 2f;
    public float hitSpeedPenalty = 0.3f;
    private int shieldCharges = 0; // 방어막 아이템으로 막을 수 있는 남은 피격 횟수
    private float itemInvincibleEndTime = 0f; // 라멘 무적 종료 시각
    private float mileageBonusEndTime = 0f; // 마일리지 카드 보너스 종료 시각

    // 게임 시작과 동시에 싱글톤을 구성
    void Awake() {
        // 싱글톤 변수 instance가 비어있는가?
        if(instance == null)
        {
            // instance가 비어있다면(null) 그곳에 자기 자신을 할당
            instance = this;
        }
        else
        {
            // instance에 이미 다른 GameManager 오브젝트가 할당되어 있는 경우

            // 씬에 두개 이상의 GameManager 오브젝트가 존재한다는 의미.
            // 싱글톤 오브젝트는 하나만 존재해야 하므로 자신의 게임 오브젝트를 파괴
            Debug.LogWarning("씬에 두개 이상의 게임 매니저가 존재합니다!");
            Destroy(gameObject);
        }
    }

    private void Start() {
        currentLife = maxLife;
        totalMileage = GameFlowManager.Instance != null
            ? GameFlowManager.Instance.SaveData.totalMileage
            : PlayerPrefs.GetInt(TotalMileageKey, 0);

        if(grantInitialMileage && PlayerPrefs.GetInt(InitialMileageGrantedKey, 0) == 0)
        {
            totalMileage += initialMileage;
            PlayerPrefs.SetInt(TotalMileageKey, totalMileage);
            PlayerPrefs.SetInt(InitialMileageGrantedKey, 1);
            PlayerPrefs.Save();
        }

        BuildRuntimeHUD();
        StartTutorialGuideSequence();
        BeginMap(GameFlowManager.Instance != null
            ? GameFlowManager.Instance.CurrentMap
            : MapDatabase.GetMap(MapId.TutorialIncheon));
        SyncTotalMileageToFlow();
        UpdateScoreUI();
    }

    void Update() {
        // 게임 오버 상태에서 게임을 재시작할 수 있게 하는 처리
        if(isGameover && GameFlowManager.Instance == null && Input.GetMouseButtonDown(0))
        {
            SceneManager.LoadScene(SceneManager.GetActiveScene().name);
        }

        if(!GameFlowManager.IsGameplayActive)
        {
            UpdateScoreUI();
            return;
        }

        if(!isGameover)
        {
            UpdateTutorialGuideSequence();

            stageElapsedTime += Time.deltaTime;

            // 게임 진행시 게임 속도는 시간에 따라 증가
            gameSpeed += speedIncreaseRate * Time.deltaTime;
            // 게임 최대 속도는 max를 넘어서지 못하게
            gameSpeed = Mathf.Min(gameSpeed, maxGameSpeed);

            stageDistance += 10f * gameSpeed * Time.deltaTime;

            if(currentMapDefinition != null && currentMapDefinition.targetDistance > 0f && stageDistance >= currentMapDefinition.targetDistance)
            {
                CompleteStage(true);
                return;
            }

            if(currentMapDefinition != null && currentMapDefinition.timeLimit > 0f && stageElapsedTime >= currentMapDefinition.timeLimit)
            {
                CompleteStage(false);
                return;
            }

            UpdateScoreUI();
        }
    }

    public void BeginMap(MapDefinition mapDefinition) {
        currentMapDefinition = mapDefinition != null
            ? mapDefinition
            : MapDatabase.GetMap(MapId.TutorialIncheon);

        score = 0;
        stageMileage = 0;
        currentLife = maxLife;
        gameSpeed = minGameSpeed;
        shieldCharges = 0;
        itemInvincibleEndTime = 0f;
        mileageBonusEndTime = 0f;
        stageElapsedTime = 0f;
        stageDistance = 0f;
        stageCompleted = false;
        isGameover = false;
        tutorialGuideSequenceComplete = false;
        currentTutorialGuideSequenceIndex = -1;
        SetTutorialGuide(TutorialGuideType.None, "", "");

        if(gameoverUI != null)
        {
            gameoverUI.SetActive(false);
        }

        PlatformSpawner platformSpawner = FindFirstObjectByType<PlatformSpawner>();
        if(platformSpawner != null)
        {
            platformSpawner.ApplyMap(currentMapDefinition);
        }

        UpdateScoreUI();
    }

    // 점수를 증가시키는 메서드
    public void AddScore(int newScore) {

        if(!isGameover)
        {
            score += newScore;
            UpdateScoreUI();
        }
        
    }

    public void SetTutorialMessage(string message) {
        SetTutorialGuide(TutorialGuideType.None, "", message);
    }

    public void SetTutorialGuide(TutorialGuideType guideType, string title, string message) {
        tutorialGuideType = guideType;
        tutorialTitle = title;
        tutorialMessage = message;
        UpdateScoreUI();
    }

    private void StartTutorialGuideSequence() {
        if(!playTutorialGuideSequence)
        {
            return;
        }

        tutorialGuideSequenceStartTime = Time.time;
        tutorialGuideSequenceComplete = false;
        currentTutorialGuideSequenceIndex = -1;
        UpdateTutorialGuideSequence();
    }

    private void UpdateTutorialGuideSequence() {
        if(!playTutorialGuideSequence || tutorialGuideSequenceComplete)
        {
            return;
        }

        float elapsedTime = Time.time - tutorialGuideSequenceStartTime;
        int guideIndex = GetTutorialGuideSequenceIndex(elapsedTime);

        if(guideIndex == currentTutorialGuideSequenceIndex)
        {
            return;
        }

        currentTutorialGuideSequenceIndex = guideIndex;
        ApplyTutorialGuideSequenceStep(guideIndex);
    }

    private int GetTutorialGuideSequenceIndex(float elapsedTime) {
        if(elapsedTime < TutorialJumpGuideDuration)
        {
            return 0;
        }

        elapsedTime -= TutorialJumpGuideDuration;
        if(elapsedTime < TutorialDoubleJumpGuideDuration)
        {
            return 1;
        }

        elapsedTime -= TutorialDoubleJumpGuideDuration;
        if(elapsedTime < TutorialSlideGuideDuration)
        {
            return 2;
        }

        elapsedTime -= TutorialSlideGuideDuration;
        if(elapsedTime < TutorialItemGuideDuration)
        {
            return 3;
        }

        return 4;
    }

    private void ApplyTutorialGuideSequenceStep(int guideIndex) {
        switch(guideIndex)
        {
            case 0:
                SetTutorialGuide(TutorialGuideType.Jump, "점프", "왼쪽 클릭으로 가볍게 뛰어오릅니다.");
                break;
            case 1:
                SetTutorialGuide(TutorialGuideType.DoubleJump, "2단 점프", "공중에서 왼쪽 클릭을 한 번 더 누릅니다.");
                break;
            case 2:
                SetTutorialGuide(TutorialGuideType.Slide, "슬라이드", "오른쪽 클릭을 누르고 있으면 낮게 지나갑니다.");
                break;
            case 3:
                SetTutorialGuide(TutorialGuideType.Items, "아이템", "초밥 -> 라멘 -> 방어막 -> 운동화 -> 마일리지 카드 순서입니다.");
                break;
            default:
                tutorialGuideSequenceComplete = true;
                SetTutorialGuide(TutorialGuideType.None, "", "");
                break;
        }
    }

    // 마일리지 토큰을 먹었을 때 호출되는 메서드
    public void AddMileage(int amount) {
        if(isGameover)
        {
            return;
        }

        int finalAmount = IsMileageBonusActive()
            ? amount * Mathf.Max(1, mileageBonusMultiplier)
            : amount;

        stageMileage += finalAmount;
        score += Mathf.Max(1, finalAmount / 10);

        if(saveMileageImmediately)
        {
            totalMileage += finalAmount;
            PlayerPrefs.SetInt(TotalMileageKey, totalMileage);
            PlayerPrefs.Save();
            SyncTotalMileageToFlow();
        }

        UpdateScoreUI();
    }

    // 플레이어가 장애물에 부딪혔을 때 목숨과 속도를 줄이는 메서드
    public bool TakeDamage(int damage = 1) {
        if(isGameover || !GameFlowManager.IsGameplayActive)
        {
            return false;
        }

        currentLife -= damage;
        currentLife = Mathf.Max(currentLife, 0);

        gameSpeed -= hitSpeedPenalty;
        gameSpeed = Mathf.Max(gameSpeed, minGameSpeed);

        UpdateScoreUI();

        return currentLife <= 0;
    }

    public void ApplyTutorialItem(TutorialItemType itemType) {
        if(isGameover || !GameFlowManager.IsGameplayActive)
        {
            return;
        }

        switch(itemType)
        {
            case TutorialItemType.HealthSushi:
                currentLife = Mathf.Min(maxLife, currentLife + 1);
                AddScore(3);
                SetTutorialGuide(TutorialGuideType.Items, "초밥", "방금 잃은 목숨을 1 회복합니다.");
                break;
            case TutorialItemType.InvincibleRamen:
                itemInvincibleEndTime = Time.time + itemInvincibleDuration;
                SetTutorialGuide(TutorialGuideType.Items, "라멘", "짧은 시간 동안 다음 충돌 피해를 받지 않습니다.");
                break;
            case TutorialItemType.Shield:
                shieldCharges = Mathf.Max(shieldCharges, 1);
                SetTutorialGuide(TutorialGuideType.Items, "방어막", "다음 충돌 1회를 방어막이 대신 막습니다.");
                break;
            case TutorialItemType.SpeedShoes:
                gameSpeed = Mathf.Min(maxGameSpeed, gameSpeed + itemSpeedBoostAmount);
                SetTutorialGuide(TutorialGuideType.Items, "운동화", "속도를 즉시 회복해 늦어진 일정을 따라잡습니다.");
                break;
            case TutorialItemType.MileageCard:
                mileageBonusEndTime = Time.time + mileageBonusDuration;
                SetTutorialGuide(TutorialGuideType.Items, "마일리지 카드", "잠시 동안 먹는 마일리지가 두 배로 들어옵니다.");
                break;
        }

        UpdateScoreUI();
    }

    public bool TryBlockDamageWithItem() {
        if(isGameover || !GameFlowManager.IsGameplayActive)
        {
            return false;
        }

        if(IsItemInvincibleActive())
        {
            SetTutorialGuide(TutorialGuideType.Items, "라멘", "무적 중이라 이번 충돌은 피해 없이 지나갑니다.");
            return true;
        }

        if(shieldCharges > 0)
        {
            shieldCharges--;
            SetTutorialGuide(TutorialGuideType.Items, "방어막", "방어막이 충돌 피해를 한 번 막았습니다.");
            UpdateScoreUI();
            return true;
        }

        return false;
    }

    private bool IsItemInvincibleActive() {
        return Time.time < itemInvincibleEndTime;
    }

    private bool IsMileageBonusActive() {
        return Time.time < mileageBonusEndTime;
    }

    // 플레이어 캐릭터가 사망시 게임 오버를 실행하는 메서드
    public void OnPlayerDead() {
        isGameover = true;

        if(gameoverUI != null)
        {
            gameoverUI.SetActive(GameFlowManager.Instance == null);
        }

        CompleteStage(false);
    }

    private void CompleteStage(bool cleared) {
        if(stageCompleted)
        {
            return;
        }

        stageCompleted = true;
        isGameover = true;

        if(GameFlowManager.Instance != null)
        {
            GameFlowManager.Instance.CompleteMap(cleared, score, stageMileage, stageElapsedTime, stageDistance);
        }
        else if(gameoverUI != null)
        {
            gameoverUI.SetActive(true);
        }
    }

    private void SyncTotalMileageToFlow() {
        if(GameFlowManager.Instance != null)
        {
            GameFlowManager.Instance.SetTotalMileage(totalMileage);
        }
    }

    private void UpdateScoreUI() {
        if(scoreText != null)
        {
            string mapName = currentMapDefinition != null ? currentMapDefinition.displayName : "RUN";
            string distanceText = currentMapDefinition != null
                ? Mathf.FloorToInt(stageDistance) + "/" + Mathf.FloorToInt(currentMapDefinition.targetDistance) + "m"
                : Mathf.FloorToInt(stageDistance) + "m";
            string timeText = currentMapDefinition != null && currentMapDefinition.timeLimit > 0f
                ? Mathf.Max(0f, currentMapDefinition.timeLimit - stageElapsedTime).ToString("0") + "s"
                : stageElapsedTime.ToString("0") + "s";

            scoreText.text = mapName
                + "\nSCORE " + score
                + "\nMILE " + stageMileage
                + "\nTOTAL " + totalMileage
                + "\nDIST " + distanceText
                + "\nTIME " + timeText
                + "\nSPD " + gameSpeed.ToString("0.0");
        }

        if(lifeIcons != null)
        {
            for(int i = 0; i < lifeIcons.Length; i++)
            {
                if(lifeIcons[i] == null)
                {
                    continue;
                }

                bool hasLife = i < currentLife;
                lifeIcons[i].color = hasLife
                    ? GetActiveLifeIconColor(lifeIcons[i])
                    : new Color(0.34f, 0.34f, 0.34f, 0.72f);
            }
        }

        RefreshTutorialGuide();
    }

    private void RefreshTutorialGuide() {
        if(tutorialGuideObject == null)
        {
            return;
        }

        bool hasGuide = tutorialGuideType != TutorialGuideType.None
            || !string.IsNullOrEmpty(tutorialTitle)
            || !string.IsNullOrEmpty(tutorialMessage);
        tutorialGuideObject.SetActive(hasGuide);

        if(!hasGuide)
        {
            return;
        }

        if(tutorialTitleText != null)
        {
            tutorialTitleText.text = tutorialTitle;
        }

        if(tutorialBodyText != null)
        {
            tutorialBodyText.text = tutorialMessage;
        }

        bool showItemRow = tutorialGuideType == TutorialGuideType.Items;
        Sprite mainSprite = GetTutorialMainSprite(tutorialGuideType);

        if(tutorialMainImage != null)
        {
            tutorialMainImage.gameObject.SetActive(!showItemRow && mainSprite != null);
            tutorialMainImage.sprite = mainSprite;
        }

        if(tutorialMultiplierText != null)
        {
            tutorialMultiplierText.gameObject.SetActive(tutorialGuideType == TutorialGuideType.DoubleJump);
        }

        if(tutorialItemImages != null)
        {
            for(int i = 0; i < tutorialItemImages.Length; i++)
            {
                if(tutorialItemImages[i] != null)
                {
                    tutorialItemImages[i].gameObject.SetActive(showItemRow && tutorialItemImages[i].sprite != null);
                }
            }
        }
    }

    private Color GetActiveLifeIconColor(Image lifeIcon) {
        if(lifeIcon != null && lifeIcon.sprite == null)
        {
            return new Color(0.94f, 0.18f, 0.12f, 0.9f);
        }

        return Color.white;
    }

    private void BuildRuntimeHUD() {
        if(scoreText == null)
        {
            return;
        }

        Transform canvasTransform = scoreText.transform.parent;

        ConfigureRightInfoText();
        CreateStatusHUD(canvasTransform);
        CreateTutorialGuide(canvasTransform);
    }

    private void ConfigureRightInfoText() {
        RectTransform rectTransform = scoreText.rectTransform;
        rectTransform.anchorMin = new Vector2(1f, 1f);
        rectTransform.anchorMax = new Vector2(1f, 1f);
        rectTransform.pivot = new Vector2(1f, 1f);
        rectTransform.anchoredPosition = new Vector2(-12f, -9f);
        rectTransform.sizeDelta = new Vector2(176f, 96f);

        scoreText.alignment = TextAlignmentOptions.TopRight;
        scoreText.fontSize = 8.5f;
        scoreText.lineSpacing = -13f;
        scoreText.color = new Color(0.94f, 0.97f, 1f, 0.92f);
        scoreText.raycastTarget = false;
        scoreText.textWrappingMode = TextWrappingModes.NoWrap;
    }

    private void CreateStatusHUD(Transform canvasTransform) {
        GameObject hudObject = new GameObject("Status HUD", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
        hudObject.transform.SetParent(canvasTransform, false);

        RectTransform hudRect = hudObject.GetComponent<RectTransform>();
        hudRect.anchorMin = new Vector2(0f, 1f);
        hudRect.anchorMax = new Vector2(0f, 1f);
        hudRect.pivot = new Vector2(0f, 1f);
        hudRect.anchoredPosition = new Vector2(8f, -7f);
        hudRect.sizeDelta = new Vector2(108f, 34f);

        Image hudBackground = hudObject.GetComponent<Image>();
        hudBackground.sprite = LoadHudSprite(PassportPanelResourcePath);
        hudBackground.color = hudBackground.sprite != null
            ? Color.white
            : new Color(0.04f, 0.08f, 0.11f, 0.68f);
        hudBackground.raycastTarget = false;

        CreateFaceIcon(hudObject.transform);
        CreateLifeIcons(hudObject.transform);
        CreateItemSlots(hudObject.transform);
    }

    private void CreateFaceIcon(Transform parent) {
        GameObject faceFrame = new GameObject("Face Frame", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
        faceFrame.transform.SetParent(parent, false);

        RectTransform frameRect = faceFrame.GetComponent<RectTransform>();
        frameRect.anchorMin = new Vector2(0f, 1f);
        frameRect.anchorMax = new Vector2(0f, 1f);
        frameRect.pivot = new Vector2(0f, 1f);
        frameRect.anchoredPosition = new Vector2(6f, -6f);
        frameRect.sizeDelta = new Vector2(20f, 20f);

        Image frameImage = faceFrame.GetComponent<Image>();
        frameImage.color = new Color(0.12f, 0.16f, 0.17f, 0.36f);
        frameImage.raycastTarget = false;

        GameObject faceObject = new GameObject("Character Face", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
        faceObject.transform.SetParent(faceFrame.transform, false);

        RectTransform faceRect = faceObject.GetComponent<RectTransform>();
        faceRect.anchorMin = Vector2.zero;
        faceRect.anchorMax = Vector2.one;
        faceRect.offsetMin = new Vector2(2f, 2f);
        faceRect.offsetMax = new Vector2(-2f, -2f);

        Image faceImage = faceObject.GetComponent<Image>();
        faceImage.sprite = LoadHudSprite(CharacterFaceResourcePath);
        faceImage.preserveAspect = true;
        faceImage.raycastTarget = false;
        faceImage.color = faceImage.sprite != null
            ? Color.white
            : new Color(1f, 1f, 1f, 0f);
    }

    private void CreateLifeIcons(Transform parent) {
        Sprite lifeSprite = LoadHudSprite(LifeStampResourcePath);
        int iconCount = Mathf.Max(maxLife, 1);
        lifeIcons = new Image[iconCount];

        for(int i = 0; i < iconCount; i++)
        {
            GameObject lifeObject = new GameObject("Life Stamp " + (i + 1), typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
            lifeObject.transform.SetParent(parent, false);

            RectTransform lifeRect = lifeObject.GetComponent<RectTransform>();
            lifeRect.anchorMin = new Vector2(0f, 1f);
            lifeRect.anchorMax = new Vector2(0f, 1f);
            lifeRect.pivot = new Vector2(0f, 1f);
            lifeRect.anchoredPosition = new Vector2(33f + (i * 13f), -7f);
            lifeRect.sizeDelta = new Vector2(10f, 10f);

            Image lifeImage = lifeObject.GetComponent<Image>();
            lifeImage.sprite = lifeSprite;
            lifeImage.color = lifeSprite != null
                ? Color.white
                : new Color(0.94f, 0.18f, 0.12f, 0.9f);
            lifeImage.preserveAspect = true;
            lifeImage.raycastTarget = false;
            lifeIcons[i] = lifeImage;
        }
    }

    private void CreateItemSlots(Transform parent) {
        Sprite slotSprite = LoadHudSprite(PassportItemSlotResourcePath);

        for(int i = 0; i < 3; i++)
        {
            GameObject slotObject = new GameObject("Item Slot " + (i + 1), typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
            slotObject.transform.SetParent(parent, false);

            RectTransform slotRect = slotObject.GetComponent<RectTransform>();
            slotRect.anchorMin = new Vector2(0f, 1f);
            slotRect.anchorMax = new Vector2(0f, 1f);
            slotRect.pivot = new Vector2(0f, 1f);
            slotRect.anchoredPosition = new Vector2(33f + (i * 13f), -20f);
            slotRect.sizeDelta = new Vector2(10f, 10f);

            Image slotImage = slotObject.GetComponent<Image>();
            slotImage.sprite = slotSprite;
            slotImage.color = slotSprite != null
                ? new Color(1f, 1f, 1f, 0.92f)
                : new Color(0.92f, 0.97f, 1f, 0.22f);
            slotImage.preserveAspect = true;
            slotImage.raycastTarget = false;
        }
    }

    private Sprite LoadHudSprite(string resourcePath) {
        Sprite sprite = Resources.Load<Sprite>(resourcePath);
        if(sprite != null)
        {
            return sprite;
        }

        Texture2D texture = Resources.Load<Texture2D>(resourcePath);
#if UNITY_EDITOR
        if(texture == null)
        {
            sprite = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/Resources/" + resourcePath + ".png");
            if(sprite != null)
            {
                return sprite;
            }

            texture = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/Resources/" + resourcePath + ".png");
        }
#endif
        if(texture == null)
        {
            return null;
        }

        return Sprite.Create(
            texture,
            new Rect(0f, 0f, texture.width, texture.height),
            new Vector2(0.5f, 0.5f),
            100f);
    }

    private void CreateTutorialGuide(Transform canvasTransform) {
        tutorialGuideObject = new GameObject("Tutorial Guide", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
        tutorialGuideObject.transform.SetParent(canvasTransform, false);

        RectTransform guideRect = tutorialGuideObject.GetComponent<RectTransform>();
        guideRect.anchorMin = new Vector2(0.5f, 0f);
        guideRect.anchorMax = new Vector2(0.5f, 0f);
        guideRect.pivot = new Vector2(0.5f, 0f);
        guideRect.anchoredPosition = new Vector2(18f, 12f);
        guideRect.sizeDelta = new Vector2(372f, 72f);

        Image guideBackground = tutorialGuideObject.GetComponent<Image>();
        guideBackground.color = new Color(0.035f, 0.055f, 0.07f, 0.76f);
        guideBackground.raycastTarget = false;

        tutorialFontAsset = CreateKoreanTutorialFontAsset();

        CreateTutorialMainImage(tutorialGuideObject.transform);
        CreateTutorialItemRow(tutorialGuideObject.transform);
        tutorialTitleText = CreateTutorialText(tutorialGuideObject.transform, "Tutorial Title", new Vector2(70f, -9f), new Vector2(286f, 18f), 12f, FontStyles.Bold);
        tutorialBodyText = CreateTutorialText(tutorialGuideObject.transform, "Tutorial Body", new Vector2(70f, -28f), new Vector2(286f, 34f), 9f, FontStyles.Normal);
        tutorialGuideObject.SetActive(false);
    }

    private void CreateTutorialMainImage(Transform parent) {
        GameObject imageObject = new GameObject("Tutorial Main Image", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
        imageObject.transform.SetParent(parent, false);

        RectTransform imageRect = imageObject.GetComponent<RectTransform>();
        imageRect.anchorMin = new Vector2(0f, 0.5f);
        imageRect.anchorMax = new Vector2(0f, 0.5f);
        imageRect.pivot = new Vector2(0f, 0.5f);
        imageRect.anchoredPosition = new Vector2(14f, 0f);
        imageRect.sizeDelta = new Vector2(48f, 48f);

        tutorialMainImage = imageObject.GetComponent<Image>();
        tutorialMainImage.preserveAspect = true;
        tutorialMainImage.raycastTarget = false;

        GameObject multiplierObject = new GameObject("Double Jump Marker", typeof(RectTransform), typeof(CanvasRenderer));
        multiplierObject.transform.SetParent(imageObject.transform, false);

        RectTransform multiplierRect = multiplierObject.GetComponent<RectTransform>();
        multiplierRect.anchorMin = new Vector2(1f, 0f);
        multiplierRect.anchorMax = new Vector2(1f, 0f);
        multiplierRect.pivot = new Vector2(1f, 0f);
        multiplierRect.anchoredPosition = new Vector2(-2f, 1f);
        multiplierRect.sizeDelta = new Vector2(22f, 15f);

        tutorialMultiplierText = multiplierObject.AddComponent<TextMeshProUGUI>();
        tutorialMultiplierText.font = scoreText.font;
        tutorialMultiplierText.text = "x2";
        tutorialMultiplierText.alignment = TextAlignmentOptions.Center;
        tutorialMultiplierText.fontSize = 9f;
        tutorialMultiplierText.fontStyle = FontStyles.Bold;
        tutorialMultiplierText.color = new Color(1f, 0.91f, 0.33f, 1f);
        tutorialMultiplierText.raycastTarget = false;
        tutorialMultiplierText.textWrappingMode = TextWrappingModes.NoWrap;
        tutorialMultiplierText.gameObject.SetActive(false);
    }

    private void CreateTutorialItemRow(Transform parent) {
        string[] itemAssetPaths = {
            "Assets/Sprites/Items/Item_Health_Sushi.png",
            "Assets/Sprites/Items/Item_Invincible_Ramen.png",
            "Assets/Sprites/Items/Item_Shield.png",
            "Assets/Sprites/Items/Item_Speed_Shoes.png",
            "Assets/Sprites/Items/Item_Mileage_Card.png"
        };

        tutorialItemImages = new Image[itemAssetPaths.Length];

        for(int i = 0; i < itemAssetPaths.Length; i++)
        {
            GameObject itemObject = new GameObject("Tutorial Item " + (i + 1), typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
            itemObject.transform.SetParent(parent, false);

            RectTransform itemRect = itemObject.GetComponent<RectTransform>();
            itemRect.anchorMin = new Vector2(0f, 0.5f);
            itemRect.anchorMax = new Vector2(0f, 0.5f);
            itemRect.pivot = new Vector2(0f, 0.5f);
            itemRect.anchoredPosition = new Vector2(11f + (i * 27f), 0f);
            itemRect.sizeDelta = new Vector2(24f, 24f);

            Image itemImage = itemObject.GetComponent<Image>();
            itemImage.sprite = LoadTutorialSprite(itemAssetPaths[i]);
            itemImage.preserveAspect = true;
            itemImage.raycastTarget = false;
            itemImage.gameObject.SetActive(false);
            tutorialItemImages[i] = itemImage;
        }
    }

    private TextMeshProUGUI CreateTutorialText(Transform parent, string objectName, Vector2 anchoredPosition, Vector2 sizeDelta, float fontSize, FontStyles fontStyle) {
        GameObject textObject = new GameObject(objectName, typeof(RectTransform), typeof(CanvasRenderer));
        textObject.transform.SetParent(parent, false);

        RectTransform textRect = textObject.GetComponent<RectTransform>();
        textRect.anchorMin = new Vector2(0f, 1f);
        textRect.anchorMax = new Vector2(0f, 1f);
        textRect.pivot = new Vector2(0f, 1f);
        textRect.anchoredPosition = anchoredPosition;
        textRect.sizeDelta = sizeDelta;

        TextMeshProUGUI text = textObject.AddComponent<TextMeshProUGUI>();
        text.font = tutorialFontAsset != null ? tutorialFontAsset : scoreText.font;
        text.alignment = TextAlignmentOptions.Left;
        text.fontSize = fontSize;
        text.fontStyle = fontStyle;
        text.color = new Color(0.96f, 0.98f, 1f, 0.96f);
        text.raycastTarget = false;
        text.textWrappingMode = TextWrappingModes.Normal;
        return text;
    }

    private TMP_FontAsset CreateKoreanTutorialFontAsset() {
        string[] familyNames = {
            "Malgun Gothic",
            "맑은 고딕",
            "Arial Unicode MS"
        };

        string[] styleNames = {
            "Regular",
            "Normal"
        };

        for(int familyIndex = 0; familyIndex < familyNames.Length; familyIndex++)
        {
            for(int styleIndex = 0; styleIndex < styleNames.Length; styleIndex++)
            {
                TMP_FontAsset fontAsset = TMP_FontAsset.CreateFontAsset(familyNames[familyIndex], styleNames[styleIndex], 80);
                if(fontAsset != null)
                {
                    fontAsset.name = "Runtime Korean Tutorial Font";
                    return fontAsset;
                }
            }
        }

        return null;
    }

    private Sprite GetTutorialMainSprite(TutorialGuideType guideType) {
        if(guideType == TutorialGuideType.Jump || guideType == TutorialGuideType.DoubleJump)
        {
            return LoadTutorialSprite("Assets/Sprites/JJ_Jump.png", "JJ_Jump_2");
        }

        if(guideType == TutorialGuideType.Slide)
        {
            return LoadTutorialSprite("Assets/Sprites/JJ_Slide.png", "JJ_Slide_0");
        }

        if(guideType == TutorialGuideType.Damage)
        {
            return LoadTutorialSprite("Assets/Resources/UI_LifeStamp.png");
        }

        return null;
    }

    private Sprite LoadTutorialSprite(string assetPath, string spriteName = "") {
#if UNITY_EDITOR
        Object[] assets = AssetDatabase.LoadAllAssetRepresentationsAtPath(assetPath);
        Sprite firstSprite = null;

        if(assets != null && assets.Length > 0)
        {
            for(int i = 0; i < assets.Length; i++)
            {
                Sprite sprite = assets[i] as Sprite;
                if(sprite == null)
                {
                    continue;
                }

                if(firstSprite == null)
                {
                    firstSprite = sprite;
                }

                if(sprite.name == spriteName)
                {
                    return sprite;
                }
            }

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

        Sprite directSprite = AssetDatabase.LoadAssetAtPath<Sprite>(assetPath);
        if(directSprite != null)
        {
            return directSprite;
        }

        Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(assetPath);
        if(texture == null)
        {
            return null;
        }

        return Sprite.Create(
            texture,
            new Rect(0f, 0f, texture.width, texture.height),
            new Vector2(0.5f, 0.5f),
            100f);
#else
        return null;
#endif
    }
}
