using UnityEngine;
using UnityEngine.SceneManagement;
using TMPro;

// 게임 오버 상태를 표현하고, 게임 점수와 UI를 관리하는 게임 매니저
// 씬에는 단 하나의 게임 매니저만 존재할 수 있다.
public class GameManager : MonoBehaviour {
    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; // 누적 보유 마일리지

    public int maxLife = 3; // 시작 목숨
    public int initialMileage = 500; // 첫 시작시 지급할 초기 마일리지
    public bool grantInitialMileage = true; // 초기 마일리지 지급 여부
    public bool saveMileageImmediately = true; // 스테이지 정산 전까지는 획득 즉시 누적한다.

    private const string TotalMileageKey = "UniRun.TotalMileage";
    private const string InitialMileageGrantedKey = "UniRun.InitialMileageGranted";

    // 게임의 스피드를 올리는 변수
    public float gameSpeed = 1f;
    public float minGameSpeed = 1f;
    public float speedIncreaseRate = 0.05f;
    public float maxGameSpeed = 2f;
    public float hitSpeedPenalty = 0.3f;

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

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

    private void Start() {
        currentLife = maxLife;
        totalMileage = PlayerPrefs.GetInt(TotalMileageKey, 0);

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

        UpdateScoreUI();
    }

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

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

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

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

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

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

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

        UpdateScoreUI();
    }

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

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

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

        UpdateScoreUI();

        return currentLife <= 0;
    }

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

    private void UpdateScoreUI() {
        scoreText.text = "Score : " + score
            + "\nLife : " + currentLife + "/" + maxLife
            + "\nMileage : " + stageMileage
            + "\nTotal : " + totalMileage
            + "\nSpeed : " + gameSpeed.ToString("0.0");
    }
}
