using System;
using UnityEngine;
using Random = UnityEngine.Random;

// 발판을 생성하고 주기적으로 재배치하는 스크립트
public class PlatformSpawner : MonoBehaviour {
    private enum TutorialStepKind {
        None,
        Jump,
        DoubleJumpTakeoff,
        Slide,
        ForceHit,
        HealthItem,
        InvincibleItem,
        ShieldItem,
        SpeedItem,
        MileageCardItem
    }

    [Serializable]
    public class TutorialStep {
        public Platform.PlatformPattern platformPattern;
        public Platform.MileagePattern mileagePattern;
        public SlideObstaclePattern.SlideLayout slideLayout;
        public Platform.ItemPattern itemPattern;
        public GameManager.TutorialGuideType guideType;
        public float yPosition;
        public float spawnInterval;
        public string title;
        public string message;
    }

    public GameObject platformPrefab; // 생성할 발판의 원본 프리팹
    public int count = 3; // 생성할 발판의 개수
    public bool usePatternCourse = true; // 검증된 발판 패턴 코스를 사용
    public bool tutorialMode = true; // 처음에는 튜토리얼 패턴을 먼저 배치
    public TutorialStep[] tutorialSteps; // 튜토리얼 발판 순서
    public TutorialStep[] stagePatternSteps; // 튜토리얼 이후 반복할 검증된 발판 순서

    public float timeBetSpawnMin = 1.25f; // 다음 배치까지의 시간 간격 최솟값
    public float timeBetSpawnMax = 2.25f; // 다음 배치까지의 시간 간격 최댓값
    public float tutorialSpawnInterval = 2.2f; // 튜토리얼 발판 배치 간격
    private float timeBetSpawn; // 다음 배치까지의 시간 간격

    public float yMin = -3.5f; // 배치할 위치의 최소 y값
    public float yMax = 1.5f; // 배치할 위치의 최대 y값
    private float xPos = 20f; // 배치할 위치의 x 값

    private GameObject[] platforms; // 미리 생성한 발판들
    private int currentIndex = 0; // 사용할 현재 순번의 발판
    private int courseStepIndex = 0; // 진행 중인 패턴 코스 순번

    private Vector2 poolPosition = new Vector2(0, -25); // 초반에 생성된 발판들을 화면 밖에 숨겨둘 위치
    private float lastSpawnTime; // 마지막 배치 시점

    public void ApplyMap(MapDefinition mapDefinition) {
        if(mapDefinition == null)
        {
            return;
        }

        usePatternCourse = true;
        tutorialMode = mapDefinition.isTutorial;
        tutorialSteps = MapDatabase.CloneSteps(mapDefinition.openingSteps);
        stagePatternSteps = MapDatabase.CloneSteps(mapDefinition.loopSteps);
        courseStepIndex = 0;
        currentIndex = 0;
        lastSpawnTime = Time.time;
        timeBetSpawn = 0f;

        if(platforms != null)
        {
            for(int i = 0; i < platforms.Length; i++)
            {
                if(platforms[i] != null)
                {
                    platforms[i].SetActive(false);
                    platforms[i].transform.position = poolPosition;
                }
            }
        }
    }

    void Start() {
        EnsurePatternSteps();

        if(usePatternCourse && tutorialMode)
        {
            count = Mathf.Max(count, 8);
        }

        // 변수들을 초기화하고 사용할 발판들을 미리 생성
        platforms = new GameObject[count];

        for(int i = 0; i < count; i++)
        {
            platforms[i] = Instantiate(platformPrefab, poolPosition, Quaternion.identity);
        }

        // 마지막 배치 시점 초기화
        lastSpawnTime = 0f;

        // 다음번 배치까지의 시간 간격을 0으로 초기화
        timeBetSpawn = 0f;
    }

    void Update() {
        // 순서를 돌아가며 주기적으로 발판을 배치
        if(GameManager.instance == null || GameManager.instance.isGameover || !GameFlowManager.IsGameplayActive)
        {
            return;
        }

        if(Time.time >= lastSpawnTime + timeBetSpawn)
        {
            lastSpawnTime = Time.time;

            TutorialStep step = usePatternCourse ? GetCurrentCourseStep() : null;
            timeBetSpawn = step != null ? GetStepSpawnInterval(step) : Random.Range(timeBetSpawnMin, timeBetSpawnMax);

            float yPos = step != null
                ? Mathf.Clamp(step.yPosition, yMin, yMax)
                : Random.Range(yMin, yMax);

            platforms[currentIndex].SetActive(false);
            ConfigurePlatform(platforms[currentIndex], step);
            platforms[currentIndex].SetActive(true);

            platforms[currentIndex].transform.position = new Vector2(xPos, yPos);

            currentIndex++;

            if(currentIndex >= count)
            {
                currentIndex = 0;
            }

            if(usePatternCourse)
            {
                courseStepIndex++;
            }
        }
    }

    private void ConfigurePlatform(GameObject platformObject, TutorialStep step) {
        Platform platform = platformObject.GetComponent<Platform>();

        if(platform == null)
        {
            return;
        }

        if(step == null)
        {
            platform.ConfigureForRandom();
            return;
        }

        platform.ConfigureForTutorial(step.platformPattern, step.mileagePattern, step.slideLayout, step.itemPattern);

        if(GameManager.instance != null && step.guideType != GameManager.TutorialGuideType.None)
        {
            GameManager.instance.SetTutorialGuide(step.guideType, step.title, step.message);
        }

        if(GameManager.instance != null
            && step.guideType == GameManager.TutorialGuideType.None
            && (!tutorialMode || courseStepIndex >= tutorialSteps.Length))
        {
            GameManager.instance.SetTutorialGuide(GameManager.TutorialGuideType.None, "", "");
        }
    }

    private float GetStepSpawnInterval(TutorialStep step) {
        if(step != null && step.spawnInterval > 0f)
        {
            return step.spawnInterval;
        }

        return tutorialSpawnInterval;
    }

    private TutorialStep GetCurrentCourseStep() {
        EnsurePatternSteps();

        if(tutorialMode && courseStepIndex < tutorialSteps.Length)
        {
            return tutorialSteps[courseStepIndex];
        }

        int loopIndex = tutorialMode
            ? courseStepIndex - tutorialSteps.Length
            : courseStepIndex;

        if(stagePatternSteps == null || stagePatternSteps.Length == 0)
        {
            return CreateStep(
                Platform.PlatformPattern.Empty,
                Platform.MileagePattern.SafeLine,
                SlideObstaclePattern.SlideLayout.Random,
                GameManager.TutorialGuideType.None,
                -2.2f,
                tutorialSpawnInterval,
                "",
                "");
        }

        loopIndex %= stagePatternSteps.Length;

        if(loopIndex < 0)
        {
            loopIndex += stagePatternSteps.Length;
        }

        return stagePatternSteps[loopIndex];
    }

    private void EnsurePatternSteps() {
        EnsureTutorialSteps();
        EnsureStagePatternSteps();
    }

    private void EnsureTutorialSteps() {
        if(tutorialSteps != null && tutorialSteps.Length > 0)
        {
            return;
        }

        tutorialSteps = new TutorialStep[] {
            CreateTutorialStep(TutorialStepKind.Jump, Platform.PlatformPattern.LowMid, Platform.MileagePattern.JumpArc, SlideObstaclePattern.SlideLayout.Random, -2.4f, 1.05f),
            CreateTutorialStep(TutorialStepKind.None, Platform.PlatformPattern.Empty, Platform.MileagePattern.SafeLine, SlideObstaclePattern.SlideLayout.Random, -2.4f, 1f),
            CreateTutorialStep(TutorialStepKind.DoubleJumpTakeoff, Platform.PlatformPattern.Empty, Platform.MileagePattern.None, SlideObstaclePattern.SlideLayout.Random, -2.4f, 2.10f),
            CreateTutorialStep(TutorialStepKind.None, Platform.PlatformPattern.Empty, Platform.MileagePattern.SafeLine, SlideObstaclePattern.SlideLayout.Random, -2.4f, 1.10f),
            CreateTutorialStep(TutorialStepKind.Slide, Platform.PlatformPattern.Slide, Platform.MileagePattern.SlideLine, SlideObstaclePattern.SlideLayout.CenterPair, -2.4f, 1.15f),
            CreateTutorialStep(TutorialStepKind.None, Platform.PlatformPattern.Empty, Platform.MileagePattern.SafeLine, SlideObstaclePattern.SlideLayout.Random, -2.4f, 1f),
            CreateTutorialStep(TutorialStepKind.ForceHit, Platform.PlatformPattern.ForceHit, Platform.MileagePattern.None, SlideObstaclePattern.SlideLayout.WidePair, -2.4f, 1.15f),
            CreateTutorialStep(TutorialStepKind.HealthItem, Platform.PlatformPattern.Empty, Platform.MileagePattern.None, SlideObstaclePattern.SlideLayout.Random, -2.4f, 0.95f, Platform.ItemPattern.HealthSushi),
            CreateTutorialStep(TutorialStepKind.InvincibleItem, Platform.PlatformPattern.Empty, Platform.MileagePattern.None, SlideObstaclePattern.SlideLayout.Random, -2.4f, 0.9f, Platform.ItemPattern.InvincibleRamen),
            CreateTutorialStep(TutorialStepKind.None, Platform.PlatformPattern.ForceHit, Platform.MileagePattern.None, SlideObstaclePattern.SlideLayout.WidePair, -2.4f, 1.05f),
            CreateTutorialStep(TutorialStepKind.ShieldItem, Platform.PlatformPattern.Empty, Platform.MileagePattern.None, SlideObstaclePattern.SlideLayout.Random, -2.4f, 0.9f, Platform.ItemPattern.Shield),
            CreateTutorialStep(TutorialStepKind.None, Platform.PlatformPattern.ForceHit, Platform.MileagePattern.None, SlideObstaclePattern.SlideLayout.WidePair, -2.4f, 1.05f),
            CreateTutorialStep(TutorialStepKind.SpeedItem, Platform.PlatformPattern.Empty, Platform.MileagePattern.None, SlideObstaclePattern.SlideLayout.Random, -2.4f, 0.95f, Platform.ItemPattern.SpeedShoes),
            CreateTutorialStep(TutorialStepKind.MileageCardItem, Platform.PlatformPattern.Empty, Platform.MileagePattern.None, SlideObstaclePattern.SlideLayout.Random, -2.4f, 0.95f, Platform.ItemPattern.MileageCard),
            CreateTutorialStep(TutorialStepKind.None, Platform.PlatformPattern.Empty, Platform.MileagePattern.SafeLine, SlideObstaclePattern.SlideLayout.Random, -2.4f, 1f),
            CreateTutorialStep(TutorialStepKind.None, Platform.PlatformPattern.Empty, Platform.MileagePattern.SafeLine, SlideObstaclePattern.SlideLayout.Random, -2.4f, 1f),
        };
    }

    private void EnsureStagePatternSteps() {
        if(stagePatternSteps != null && stagePatternSteps.Length > 0)
        {
            return;
        }

        stagePatternSteps = new TutorialStep[] {
            CreateStep(Platform.PlatformPattern.Empty, Platform.MileagePattern.SafeLine, SlideObstaclePattern.SlideLayout.Random, GameManager.TutorialGuideType.None, -2.2f, 2.25f, "", ""),
            CreateStep(Platform.PlatformPattern.LowMid, Platform.MileagePattern.JumpArc, SlideObstaclePattern.SlideLayout.Random, GameManager.TutorialGuideType.None, -2.3f, 2.4f, "", ""),
            CreateStep(Platform.PlatformPattern.Empty, Platform.MileagePattern.SafeLine, SlideObstaclePattern.SlideLayout.Random, GameManager.TutorialGuideType.None, -1.95f, 2.2f, "", ""),
            CreateStep(Platform.PlatformPattern.Slide, Platform.MileagePattern.SlideLine, SlideObstaclePattern.SlideLayout.WidePair, GameManager.TutorialGuideType.None, -2.25f, 2.65f, "", ""),
            CreateStep(Platform.PlatformPattern.Empty, Platform.MileagePattern.SafeLine, SlideObstaclePattern.SlideLayout.Random, GameManager.TutorialGuideType.None, -2.0f, 2.2f, "", ""),
            CreateStep(Platform.PlatformPattern.LowLeft, Platform.MileagePattern.JumpArc, SlideObstaclePattern.SlideLayout.Random, GameManager.TutorialGuideType.None, -2.35f, 2.35f, "", ""),
            CreateStep(Platform.PlatformPattern.Slide, Platform.MileagePattern.SlideLine, SlideObstaclePattern.SlideLayout.LeftPair, GameManager.TutorialGuideType.None, -2.35f, 2.7f, "", ""),
            CreateStep(Platform.PlatformPattern.Empty, Platform.MileagePattern.SafeLine, SlideObstaclePattern.SlideLayout.Random, GameManager.TutorialGuideType.None, -2.1f, 2.2f, "", ""),
            CreateStep(Platform.PlatformPattern.LowRight, Platform.MileagePattern.JumpArc, SlideObstaclePattern.SlideLayout.Random, GameManager.TutorialGuideType.None, -2.0f, 2.35f, "", ""),
            CreateStep(Platform.PlatformPattern.Empty, Platform.MileagePattern.None, SlideObstaclePattern.SlideLayout.Random, GameManager.TutorialGuideType.None, -1.85f, 2.15f, "", ""),
            CreateStep(Platform.PlatformPattern.Slide, Platform.MileagePattern.SlideLine, SlideObstaclePattern.SlideLayout.RightPair, GameManager.TutorialGuideType.None, -2.25f, 2.65f, "", ""),
            CreateStep(Platform.PlatformPattern.LowMid, Platform.MileagePattern.JumpArc, SlideObstaclePattern.SlideLayout.Random, GameManager.TutorialGuideType.None, -2.4f, 2.35f, "", ""),
            CreateStep(Platform.PlatformPattern.Empty, Platform.MileagePattern.SafeLine, SlideObstaclePattern.SlideLayout.Random, GameManager.TutorialGuideType.None, -2.0f, 2.2f, "", ""),
            CreateStep(Platform.PlatformPattern.Slide, Platform.MileagePattern.SlideLine, SlideObstaclePattern.SlideLayout.CenterPair, GameManager.TutorialGuideType.None, -2.2f, 2.7f, "", ""),
        };
    }

    private TutorialStep CreateStep(
        Platform.PlatformPattern platformPattern,
        Platform.MileagePattern mileagePattern,
        SlideObstaclePattern.SlideLayout slideLayout,
        GameManager.TutorialGuideType guideType,
        float yPosition,
        float spawnInterval,
        string title,
        string message,
        Platform.ItemPattern itemPattern = Platform.ItemPattern.None) {
        TutorialStep step = new TutorialStep();
        step.platformPattern = platformPattern;
        step.mileagePattern = mileagePattern;
        step.slideLayout = slideLayout;
        step.itemPattern = itemPattern;
        step.guideType = guideType;
        step.yPosition = yPosition;
        step.spawnInterval = spawnInterval;
        step.title = title;
        step.message = message;
        return step;
    }

    private TutorialStep CreateTutorialStep(
        TutorialStepKind stepKind,
        Platform.PlatformPattern platformPattern,
        Platform.MileagePattern mileagePattern,
        SlideObstaclePattern.SlideLayout slideLayout,
        float yPosition,
        float spawnInterval,
        Platform.ItemPattern itemPattern = Platform.ItemPattern.None) {
        GameManager.TutorialGuideType guideType = GameManager.TutorialGuideType.None;
        string title = "";
        string message = "";

        switch(stepKind)
        {
            case TutorialStepKind.Jump:
                guideType = GameManager.TutorialGuideType.Jump;
                title = "점프";
                message = "가방 장애물이 보이면 왼쪽 클릭으로 뛰어넘습니다.";
                break;
            case TutorialStepKind.DoubleJumpTakeoff:
                guideType = GameManager.TutorialGuideType.DoubleJump;
                title = "2단 점프";
                message = "다음 발판이 멀리 있으면 공중에서 한 번 더 왼쪽 클릭합니다.";
                break;
            case TutorialStepKind.Slide:
                guideType = GameManager.TutorialGuideType.Slide;
                title = "슬라이드";
                message = "천장 표지판이 보이면 오른쪽 클릭을 누르고 지나갑니다.";
                break;
            case TutorialStepKind.ForceHit:
                guideType = GameManager.TutorialGuideType.Damage;
                title = "피격";
                message = "이번 구간은 일부러 맞아 목숨과 속도 감소를 확인합니다.";
                break;
            case TutorialStepKind.HealthItem:
                guideType = GameManager.TutorialGuideType.Items;
                title = "초밥";
                message = "초밥을 먹으면 잃은 목숨을 1 회복합니다.";
                break;
            case TutorialStepKind.InvincibleItem:
                guideType = GameManager.TutorialGuideType.Items;
                title = "라멘";
                message = "라멘을 먹고 다음 충돌을 피해 없이 지나가 봅니다.";
                break;
            case TutorialStepKind.ShieldItem:
                guideType = GameManager.TutorialGuideType.Items;
                title = "방어막";
                message = "방어막을 먹고 다음 충돌 1회를 막아 봅니다.";
                break;
            case TutorialStepKind.SpeedItem:
                guideType = GameManager.TutorialGuideType.Items;
                title = "운동화";
                message = "운동화는 속도를 회복해 일정이 늦어지는 것을 줄입니다.";
                break;
            case TutorialStepKind.MileageCardItem:
                guideType = GameManager.TutorialGuideType.Items;
                title = "마일리지 카드";
                message = "카드를 먹은 뒤 마일리지를 모으면 획득량이 늘어납니다.";
                break;
        }

        return CreateStep(platformPattern, mileagePattern, slideLayout, guideType, yPosition, spawnInterval, title, message, itemPattern);
    }
}
