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

public class GameFlowManager : MonoBehaviour {
    public enum FlowScreen {
        Intro,
        Game,
        Result,
        DutyFreeShop,
        MapPreview
    }

    public static GameFlowManager Instance { get; private set; }

    public static bool IsGameplayActive {
        get {
            return Instance == null || Instance.gameplayActive;
        }
    }

    public GameSaveData SaveData {
        get { return saveData; }
    }

    public MapDefinition CurrentMap {
        get { return currentMap; }
    }

    private const string IntroSceneName = "Intro";
    private const string GameSceneName = "Main";
    private const string ResultSceneName = "Result";
    private const string DutyFreeShopSceneName = "DutyFreeShop";
    private const string MapPreviewSceneName = "MapPreview";
    private const int ShieldCost = 120;
    private const int LunchboxCost = 80;
    private const int SpeedShoesCost = 140;
    private const int MileageCardCost = 160;

    private GameSaveData saveData;
    private MapDefinition currentMap;
    private MapDefinition completedMap;
    private FlowScreen currentScreen = FlowScreen.Intro;
    private bool gameplayActive = false;
    private bool pendingStartGameplay = false;
    private bool lastResultCleared = false;
    private int lastResultScore = 0;
    private int lastResultMileage = 0;
    private float lastResultTime = 0f;
    private float lastResultDistance = 0f;

    private Canvas overlayCanvas;
    private RectTransform overlayRoot;
    private TMP_FontAsset runtimeFont;

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    private static void Bootstrap() {
        if(Instance != null)
        {
            return;
        }

        GameObject flowObject = new GameObject("Game Flow Manager");
        flowObject.AddComponent<GameFlowManager>();
        DontDestroyOnLoad(flowObject);
    }

    private void Awake() {
        if(Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }

        Instance = this;
        DontDestroyOnLoad(gameObject);
        saveData = GameSaveManager.Load();
        currentMap = MapDatabase.GetMapByIndex(saveData.currentMapIndex);
        SceneManager.sceneLoaded += OnSceneLoaded;
    }

    private void Start() {
        ShowIntro();
    }

    private void OnDestroy() {
        if(Instance == this)
        {
            SceneManager.sceneLoaded -= OnSceneLoaded;
            Instance = null;
        }
    }

    private void OnSceneLoaded(Scene scene, LoadSceneMode mode) {
        EnsureOverlay();

        if(pendingStartGameplay)
        {
            StartCoroutine(StartGameplayAfterSceneLoad());
            return;
        }

        if(currentScreen != FlowScreen.Game)
        {
            RefreshCurrentScreen();
        }
    }

    public void SetTotalMileage(int totalMileage) {
        if(saveData == null)
        {
            return;
        }

        saveData.totalMileage = Mathf.Max(0, totalMileage);
        GameSaveManager.Save(saveData);
    }

    public void AddMileage(int amount) {
        if(saveData == null)
        {
            return;
        }

        GameSaveManager.AddMileage(saveData, amount);
    }

    public void CompleteMap(bool cleared, int score, int stageMileage, float elapsedTime, float distance) {
        if(!gameplayActive && currentScreen == FlowScreen.Result)
        {
            return;
        }

        gameplayActive = false;
        currentScreen = FlowScreen.Result;
        completedMap = currentMap;
        lastResultCleared = cleared;
        lastResultScore = score;
        lastResultMileage = stageMileage;
        lastResultTime = elapsedTime;
        lastResultDistance = distance;

        if(cleared)
        {
            saveData.bestScore = Mathf.Max(saveData.bestScore, score);
            GameSaveManager.AddMileage(saveData, completedMap.clearMileageReward);

            if(completedMap.hasNextMap)
            {
                GameSaveManager.UnlockMap(saveData, completedMap.nextMapId);
            }
            else
            {
                GameSaveManager.Save(saveData);
            }
        }

        ShowResult();
    }

    private void StartNewJourney() {
        saveData = GameSaveManager.ResetSave();
        currentMap = MapDatabase.GetMapByIndex(saveData.currentMapIndex);
        completedMap = null;
        ShowMapPreview();
    }

    private void ContinueJourney() {
        saveData = GameSaveManager.Load();
        currentMap = MapDatabase.GetMapByIndex(saveData.currentMapIndex);
        completedMap = null;
        ShowMapPreview();
    }

    private void RetryCurrentMap() {
        currentMap = MapDatabase.GetMapByIndex(saveData.currentMapIndex);
        LoadGameSceneForCurrentMap();
    }

    private void LoadGameSceneForCurrentMap() {
        currentScreen = FlowScreen.Game;
        gameplayActive = false;
        pendingStartGameplay = true;
        ShowLoading("탑승 준비 중", currentMap.displayName + " 코스를 불러오는 중입니다.");

        if(Application.CanStreamedLevelBeLoaded(GameSceneName))
        {
            SceneManager.LoadScene(GameSceneName);
            return;
        }

        Scene activeScene = SceneManager.GetActiveScene();
        if(!string.IsNullOrEmpty(activeScene.name))
        {
            SceneManager.LoadScene(activeScene.name);
        }
    }

    private IEnumerator StartGameplayAfterSceneLoad() {
        yield return null;

        pendingStartGameplay = false;
        currentScreen = FlowScreen.Game;
        gameplayActive = true;

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

        if(GameManager.instance != null)
        {
            GameManager.instance.BeginMap(currentMap);
        }
    }

    private void ShowIntro() {
        currentScreen = FlowScreen.Intro;
        gameplayActive = false;
        pendingStartGameplay = false;
        completedMap = null;

        if(TryLoadFlowScene(IntroSceneName))
        {
            return;
        }

        EnsureOverlay();
        ClearOverlay();
        SetOverlayVisible(true);

        AddTitle("Uni-Run");
        AddBody("여권을 챙기고 공항으로 출발합니다.\n진행도는 자동 저장됩니다.");
        AddBody("현재 경로: " + currentMap.displayName + "\n해금: " + MapDatabase.GetMapByIndex(saveData.unlockedMapIndex).displayName + "\n마일리지: " + saveData.totalMileage);
        AddButton("이어하기", ContinueJourney);
        AddButton("새 여행", StartNewJourney);
        AddButton("저장 초기화", ResetAndStayIntro);
    }

    private void ShowResult() {
        if(TryLoadFlowScene(ResultSceneName))
        {
            return;
        }

        EnsureOverlay();
        ClearOverlay();
        SetOverlayVisible(true);

        string title = lastResultCleared ? "맵 클리어" : "일정 실패";
        string body = completedMap.displayName
            + "\n점수: " + lastResultScore
            + "\n획득 마일리지: " + lastResultMileage
            + "\n거리: " + Mathf.FloorToInt(lastResultDistance) + "m / " + Mathf.FloorToInt(completedMap.targetDistance) + "m"
            + "\n시간: " + lastResultTime.ToString("0.0") + "s";

        if(lastResultCleared)
        {
            body += "\n클리어 보상: " + completedMap.clearMileageReward + " 마일리지";
        }

        AddTitle(title);
        AddBody(body);

        if(lastResultCleared)
        {
            AddButton("면세점으로", ShowDutyFreeShop);
        }
        else
        {
            AddButton("다시 도전", RetryCurrentMap);
        }

        AddButton("인트로", ShowIntro);
    }

    private void ShowDutyFreeShop() {
        currentScreen = FlowScreen.DutyFreeShop;
        gameplayActive = false;
        pendingStartGameplay = false;

        if(TryLoadFlowScene(DutyFreeShopSceneName))
        {
            return;
        }

        EnsureOverlay();
        ClearOverlay();
        SetOverlayVisible(true);

        AddTitle("면세점");
        AddBody("보유 마일리지: " + saveData.totalMileage
            + "\n도시락: " + saveData.lunchboxStock
            + " / 방어막: " + saveData.shieldStock
            + "\n운동화: " + saveData.speedShoesStock
            + " / 마일리지 카드: " + saveData.mileageCardStock);

        AddButton("도시락 구매 " + LunchboxCost, BuyLunchbox);
        AddButton("방어막 구매 " + ShieldCost, BuyShield);
        AddButton("운동화 구매 " + SpeedShoesCost, BuySpeedShoes);
        AddButton("마일리지 카드 구매 " + MileageCardCost, BuyMileageCard);
        AddButton("다음 맵 보기", ShowMapPreview);
    }

    private void ShowMapPreview() {
        currentScreen = FlowScreen.MapPreview;
        gameplayActive = false;
        pendingStartGameplay = false;

        if(completedMap != null && lastResultCleared && completedMap.hasNextMap)
        {
            currentMap = MapDatabase.GetMap(completedMap.nextMapId);
        }
        else
        {
            currentMap = MapDatabase.GetMapByIndex(saveData.currentMapIndex);
        }

        if(TryLoadFlowScene(MapPreviewSceneName))
        {
            return;
        }

        EnsureOverlay();
        ClearOverlay();
        SetOverlayVisible(true);

        AddTitle("다음 경로");
        AddBody(currentMap.displayName
            + "\n" + currentMap.routeName
            + "\n목표 거리: " + Mathf.FloorToInt(currentMap.targetDistance) + "m"
            + "\n제한 시간: " + currentMap.timeLimit.ToString("0") + "s");
        AddButton("출발", ConfirmMapAndStart);
        AddButton("인트로", ShowIntro);
    }

    private void ConfirmMapAndStart() {
        saveData.currentMapIndex = MapDatabase.GetIndex(currentMap.mapId);
        saveData.unlockedMapIndex = Mathf.Max(saveData.unlockedMapIndex, saveData.currentMapIndex);
        GameSaveManager.Save(saveData);
        LoadGameSceneForCurrentMap();
    }

    private void RefreshCurrentScreen() {
        switch(currentScreen)
        {
            case FlowScreen.Result:
                ShowResult();
                break;
            case FlowScreen.DutyFreeShop:
                ShowDutyFreeShop();
                break;
            case FlowScreen.MapPreview:
                ShowMapPreview();
                break;
            case FlowScreen.Intro:
            default:
                ShowIntro();
                break;
        }
    }

    private void ResetAndStayIntro() {
        saveData = GameSaveManager.ResetSave();
        currentMap = MapDatabase.GetMapByIndex(saveData.currentMapIndex);
        ShowIntro();
    }

    private void BuyLunchbox() {
        if(TrySpendMileage(LunchboxCost))
        {
            saveData.lunchboxStock++;
            GameSaveManager.Save(saveData);
        }

        ShowDutyFreeShop();
    }

    private void BuyShield() {
        if(TrySpendMileage(ShieldCost))
        {
            saveData.shieldStock++;
            GameSaveManager.Save(saveData);
        }

        ShowDutyFreeShop();
    }

    private void BuySpeedShoes() {
        if(TrySpendMileage(SpeedShoesCost))
        {
            saveData.speedShoesStock++;
            GameSaveManager.Save(saveData);
        }

        ShowDutyFreeShop();
    }

    private void BuyMileageCard() {
        if(TrySpendMileage(MileageCardCost))
        {
            saveData.mileageCardStock++;
            GameSaveManager.Save(saveData);
        }

        ShowDutyFreeShop();
    }

    private bool TrySpendMileage(int cost) {
        if(saveData.totalMileage < cost)
        {
            return false;
        }

        saveData.totalMileage -= cost;
        return true;
    }

    private void ShowLoading(string title, string body) {
        EnsureOverlay();
        ClearOverlay();
        SetOverlayVisible(true);
        AddTitle(title);
        AddBody(body);
    }

    private bool TryLoadFlowScene(string sceneName) {
        Scene activeScene = SceneManager.GetActiveScene();
        if(activeScene.name == sceneName)
        {
            return false;
        }

        if(!Application.CanStreamedLevelBeLoaded(sceneName))
        {
            return false;
        }

        SceneManager.LoadScene(sceneName);
        return true;
    }

    private void EnsureOverlay() {
        if(overlayCanvas != null)
        {
            return;
        }

        EnsureEventSystem();

        GameObject canvasObject = new GameObject("Flow Overlay", typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
        DontDestroyOnLoad(canvasObject);

        overlayCanvas = canvasObject.GetComponent<Canvas>();
        overlayCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
        overlayCanvas.sortingOrder = 100;

        CanvasScaler scaler = canvasObject.GetComponent<CanvasScaler>();
        scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
        scaler.referenceResolution = new Vector2(1280f, 720f);
        scaler.matchWidthOrHeight = 0.5f;

        GameObject rootObject = new GameObject("Flow Overlay Root", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
        rootObject.transform.SetParent(canvasObject.transform, false);

        overlayRoot = rootObject.GetComponent<RectTransform>();
        overlayRoot.anchorMin = Vector2.zero;
        overlayRoot.anchorMax = Vector2.one;
        overlayRoot.offsetMin = Vector2.zero;
        overlayRoot.offsetMax = Vector2.zero;

        Image background = rootObject.GetComponent<Image>();
        background.color = new Color(0.02f, 0.035f, 0.045f, 0.92f);
        background.raycastTarget = true;

        runtimeFont = CreateKoreanFontAsset();
    }

    private void EnsureEventSystem() {
        if(EventSystem.current != null)
        {
            return;
        }

        GameObject eventSystemObject = new GameObject("EventSystem", typeof(EventSystem), typeof(StandaloneInputModule));
        DontDestroyOnLoad(eventSystemObject);
    }

    private void ClearOverlay() {
        if(overlayRoot == null)
        {
            return;
        }

        for(int i = overlayRoot.childCount - 1; i >= 0; i--)
        {
            Destroy(overlayRoot.GetChild(i).gameObject);
        }
    }

    private void SetOverlayVisible(bool visible) {
        if(overlayCanvas != null)
        {
            overlayCanvas.gameObject.SetActive(visible);
        }
    }

    private void AddTitle(string text) {
        TextMeshProUGUI title = AddText("Title", text, 34f, FontStyles.Bold);
        RectTransform rectTransform = title.rectTransform;
        rectTransform.anchoredPosition = new Vector2(0f, 160f);
        rectTransform.sizeDelta = new Vector2(760f, 62f);
    }

    private void AddBody(string text) {
        TextMeshProUGUI body = AddText("Body", text, 17f, FontStyles.Normal);
        RectTransform rectTransform = body.rectTransform;
        rectTransform.anchoredPosition = new Vector2(0f, 52f - (GetBodyCount() * 82f));
        rectTransform.sizeDelta = new Vector2(760f, 96f);
    }

    private void AddButton(string label, UnityEngine.Events.UnityAction action) {
        int buttonIndex = GetButtonCount();

        GameObject buttonObject = new GameObject("Button " + label, typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button));
        buttonObject.transform.SetParent(overlayRoot, false);

        RectTransform buttonRect = buttonObject.GetComponent<RectTransform>();
        buttonRect.anchorMin = new Vector2(0.5f, 0.5f);
        buttonRect.anchorMax = new Vector2(0.5f, 0.5f);
        buttonRect.pivot = new Vector2(0.5f, 0.5f);
        buttonRect.anchoredPosition = new Vector2(0f, -82f - (buttonIndex * 46f));
        buttonRect.sizeDelta = new Vector2(260f, 34f);

        Image buttonImage = buttonObject.GetComponent<Image>();
        buttonImage.color = new Color(0.92f, 0.96f, 1f, 0.95f);

        Button button = buttonObject.GetComponent<Button>();
        button.onClick.AddListener(action);

        TextMeshProUGUI buttonText = AddTextTo(buttonObject.transform, "Label", label, 15f, FontStyles.Bold);
        RectTransform textRect = buttonText.rectTransform;
        textRect.anchorMin = Vector2.zero;
        textRect.anchorMax = Vector2.one;
        textRect.offsetMin = Vector2.zero;
        textRect.offsetMax = Vector2.zero;
        buttonText.color = new Color(0.04f, 0.07f, 0.09f, 1f);
    }

    private int GetBodyCount() {
        int count = 0;

        for(int i = 0; i < overlayRoot.childCount; i++)
        {
            if(overlayRoot.GetChild(i).name.StartsWith("Body"))
            {
                count++;
            }
        }

        return count;
    }

    private int GetButtonCount() {
        int count = 0;

        for(int i = 0; i < overlayRoot.childCount; i++)
        {
            if(overlayRoot.GetChild(i).name.StartsWith("Button"))
            {
                count++;
            }
        }

        return count;
    }

    private TextMeshProUGUI AddText(string objectName, string text, float fontSize, FontStyles fontStyle) {
        GameObject textObject = new GameObject(objectName + " " + GetBodyCount(), typeof(RectTransform), typeof(CanvasRenderer));
        textObject.transform.SetParent(overlayRoot, false);

        RectTransform rectTransform = textObject.GetComponent<RectTransform>();
        rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
        rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
        rectTransform.pivot = new Vector2(0.5f, 0.5f);

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

    private TextMeshProUGUI AddTextTo(Transform parent, string objectName, string text, float fontSize, FontStyles fontStyle) {
        GameObject textObject = new GameObject(objectName, typeof(RectTransform), typeof(CanvasRenderer));
        textObject.transform.SetParent(parent, false);
        TextMeshProUGUI label = textObject.AddComponent<TextMeshProUGUI>();
        label.text = text;
        label.font = runtimeFont != null ? runtimeFont : label.font;
        label.fontSize = fontSize;
        label.fontStyle = fontStyle;
        label.alignment = TextAlignmentOptions.Center;
        label.raycastTarget = false;
        label.textWrappingMode = TextWrappingModes.NoWrap;
        return label;
    }

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

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

        return null;
    }
}
