using System;
using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneLoadManager : MonoBehaviour
{
    public static SceneLoadManager Instance;

    [SerializeField] private GameObject _loadingRoot;
    [SerializeField] private Camera _loadingCam;
    [SerializeField] private Transform _loadingCamTargetTransform;

    private bool _isChangingScene = false;

    public bool IsChangingScene => _isChangingScene;

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this; // 만들어진 자신을 인스턴스로 설정
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject); // 이미 인스턴스가 있으면 자신을 파괴
        }
    }

    private void Start()
    {
        SceneManager.sceneLoaded += OnSceneLoaded;
        OnSceneLoaded(SceneManager.GetActiveScene(), LoadSceneMode.Single);
    }

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

    private void Update()
    {
        if (_loadingRoot != null && _loadingCamTargetTransform != null)
        {
            _loadingRoot.transform.position = _loadingCamTargetTransform.position;
        }
    }

    // 씬이 로드되었을때 호출
    private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
    {
        MonoBehaviour[] allObjs = UnityEngine.Object.FindObjectsByType<MonoBehaviour>(FindObjectsSortMode.None);

        foreach (var obj in allObjs)
        {
            if (obj is ISceneInitializable initializable)
            {
                // 씬에서 ISceneInitializable 인터페이스를 가진 오브젝트의 초기화 로직을 실행
                initializable.OnSceneLoaded();
            }
        }
    }

    public void SetSceneLoadingProgressValue(float value)
    {
        // 여기에 로딩바 UI 연결 예정
    }

    public void RequestSceneChange(string sceneName)
    {
        if (_isChangingScene)
        {
            Debug.Log("이미 씬 전환 중입니다.");
            return;
        }

        if (string.IsNullOrEmpty(sceneName))
        {
            Debug.LogWarning("이동할 씬 이름이 비어있습니다.");
            return;
        }

        _ = SceneChange(sceneName);
    }

    private async Awaitable SceneChange(string sceneName)
    {
        try
        {
            _isChangingScene = true;

            // 로딩바 수치 0으로 설정
            SetSceneLoadingProgressValue(0f);

            if (_loadingRoot != null)
            {
                _loadingRoot.SetActive(true);
            }

            AsyncOperation op = SceneManager.LoadSceneAsync(sceneName);

            if (op == null)
            {
                Debug.LogError($"씬 로드 실패: {sceneName}");
                _isChangingScene = false;
                return;
            }

            // 자동 전환을 하고 싶지 않을 경우 false로 두었다가 true로 바꾸면 그 때 전환됨
            op.allowSceneActivation = false;

            // 화면에 보여줄 로딩 수치
            float displayProgress = 0f;

            // op.progress 0.9가 데이터 로딩이 끝난 기준
            while (op.progress < 0.9f)
            {
                // 실제 로딩 수치
                float realProgress = Mathf.Clamp01(op.progress / 0.9f);

                // 보여줄 값을 실제값을 향해 부드럽게 이동
                displayProgress = Mathf.MoveTowards(displayProgress, realProgress, Time.deltaTime * 0.5f);

                // 로딩바 UI에 값 적용
                SetSceneLoadingProgressValue(displayProgress);

                // 자기자신이 파괴될때 토큰에 취소요청을 보냄
                await Awaitable.NextFrameAsync(this.destroyCancellationToken);
            }

            // 로딩바 수치 1(100%)로 설정
            SetSceneLoadingProgressValue(1f);

            // 잠시 대기했다가 전환
            await Awaitable.WaitForSecondsAsync(1.0f, this.destroyCancellationToken);

            // 다음씬으로 넘어가도 됨을 알림
            op.allowSceneActivation = true;

            // 씬 활성화가 완전히 끝날 때까지 대기
            while (!op.isDone)
            {
                await Awaitable.NextFrameAsync(this.destroyCancellationToken);
            }

            // VR용 로직
            // 트래킹이 중단되면 안되기 때문에 카메라를 유지해야 한다
            if (Camera.main != null)
            {
                _loadingCamTargetTransform = Camera.main.transform;
            }

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

            _isChangingScene = false;
        }
        catch (OperationCanceledException)
        {
            Debug.Log("씬 전환 작업이 취소됨");
            _isChangingScene = false;
        }
    }
}