using Cysharp.Threading.Tasks;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using TMPro;
using Unity.Netcode;
using Unity.Services.Authentication;
using Unity.Services.Lobbies.Models;
using Unity.Services.Relay;
using UnityEngine;
using UnityEngine.Events;

public class ReconnectMatchModule : MonoBehaviour
{
    public MatchmakingService matchmakingService;
    public TextMeshProUGUI statusText;

    public UnityEvent OnAttemptReconnect;
    public UnityEvent OnAttemptCancel;
    public UnityEvent OnAttemptSuccesfull;

    private CancellationTokenSource cancelToken;

    private void Start()
    {
        //NetworkManager.Singleton.OnClientDisconnectCallback += OnClientDisconnectCallback;
    }

    private void OnDestroy()
    {
        cancelToken?.Cancel();
        //if (NetworkManager.Singleton) {
        //    NetworkManager.Singleton.OnClientDisconnectCallback -= OnClientDisconnectCallback;
        //}
    }

    void OnClientDisconnectCallback(ulong id)
    {
        if (NetworkManager.Singleton.IsServer) {
            return;
        }
        if (matchmakingService.InLobby()) {
            AttemptReconnect();
        }
    }

    public void AttemptReconnect()
    {
        OnAttemptReconnect?.Invoke();
        _ = _AttemptReconnect();
    }

    public void Cancel()
    {
        cancelToken?.Cancel();
        OnAttemptCancel?.Invoke();
    }

    public async Task _AttemptReconnect()
    {
        cancelToken = new CancellationTokenSource();
        while (!cancelToken.IsCancellationRequested) {
            await UniTask.Delay(TimeSpan.FromMilliseconds(1000), ignoreTimeScale: false);
            if (cancelToken.IsCancellationRequested) {
                return;
            }
            Debug.Log("Attempting Reconnect...");
            matchmakingService.LeaveLobby();
            await UniTask.Delay(TimeSpan.FromMilliseconds(2000), ignoreTimeScale: false);
            if (cancelToken.IsCancellationRequested) {
                return;
            }

            if (Authentication.IsLoggedIn() == false) {
                Debug.Log("Will log in");
                statusText.text = "Loging in to service...";
                await Authentication.Login();
                statusText.text = "Logged into service...";
                Debug.Log("Did log in");
            }

            if (Authentication.IsLoggedIn() == false) {
                statusText.text = "Login failed, trying again...";
                await UniTask.Delay(TimeSpan.FromMilliseconds(2000), ignoreTimeScale: false);
                continue;
            }

            statusText.text = "Reconnecting to Match";
            var lobbie = await matchmakingService.FindLobbyWithSessionID(matchmakingService.LastHostSessionID());
            if (lobbie == null) {
                Debug.Log("Could not find Match, looking again...");
                await UniTask.Delay(TimeSpan.FromMilliseconds(2000), ignoreTimeScale: false);
                continue;
            }
            statusText.text = "Found Match";
            Debug.Log("Did find match");

            try {
                statusText.text = "Joining Match";
                Debug.Log("Will join match");
                await matchmakingService.JoinLobbyWithAllocation(lobbie.Id);

                if (NetworkManager.Singleton.StartClient() == false) {
                    statusText.text = "Could not join match, trying again...";
                    await UniTask.Delay(TimeSpan.FromMilliseconds(2000), ignoreTimeScale: false);
                    continue;
                }
                NetworkSceneDirector.instance.OnClientStarted(true);
                Debug.Log("Did join match");
                statusText.text = "Did join Match";
                OnAttemptSuccesfull?.Invoke();
                return;
            }
            catch (Exception e) {
                Debug.LogError(e);
            }

            Debug.Log("Could not find match");
            statusText.text = "Could not find a Match, trying again...";
            await UniTask.Delay(TimeSpan.FromMilliseconds(2000), ignoreTimeScale: false);
        }
    }
}
