using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Networking;

namespace TwitchInGames {
    public class ClientAuth : MonoBehaviour {
        public string AccessToken { get; private set; } = "";

        private AndroidJavaObject clientAuth;

        private const string urlFormat = "https://id.twitch.tv/oauth2/authorize?response_type=token&client_id={0}&redirect_uri={1}&scope={2}";
        private string state;

        public Coroutine<string> StartAuth(string clientId, string redirectUri, IEnumerable<string> scopes, RectOffset margins) {
            return new Coroutine<string>(InternalStartAuth(clientId, redirectUri, scopes, margins));
        }

        public void Cancel() {
            if(clientAuth != null) {
                clientAuth.Call("Destroy");
                clientAuth = null;
            }
        }

        public void ClearCookies() {
            if(clientAuth != null) {
                clientAuth.Call("ClearCookies");
            }
        }

        private void OnDestroy() {
            Cancel();
        }

        private bool HasReceivedToken() {
            if(String.IsNullOrEmpty(clientAuth.Call<string>("GetToken"))) {
                return false;
            }
            AccessToken = UnityWebRequest.UnEscapeURL(clientAuth.Call<string>("GetToken"));
            return true;
        }

        private IEnumerator InternalStartAuth(string clientId, string redirectUri, IEnumerable<string> scopes, RectOffset margins) {
            // Validate the arguments.
            Utility.CheckArgument(nameof(clientId), clientId);
            Utility.CheckArgument(nameof(redirectUri), redirectUri);
            if(clientAuth != null) {
                throw new InvalidOperationException("client auth already started");
            }

            // Compose the authentication URL.
            var escapedClientId = UnityWebRequest.EscapeURL(clientId);
            var scope = scopes != null ? String.Join(" ", scopes.ToArray()) : "";
            var escapedScope = UnityWebRequest.EscapeURL(scope);
            var url = String.Format(urlFormat, escapedClientId, redirectUri, escapedScope);

            // Include thirty-two characters of random state.
            var r = new System.Random();
            var q = Enumerable.Range(0, 32).Select(_ => (char)(((r.Next() & 0x6f) | 0x40) + 1));
            state = new String(q.ToArray());
            url += "&state=";
            url += state;

            yield return DoClientAuth(url, redirectUri, state, margins);
        }

        private IEnumerator DoClientAuth(string url, string redirectUrl, string state, RectOffset margins) {
            clientAuth = new AndroidJavaObject("tv.twitch.unitywebview.CWebViewPlugin");
            clientAuth.Call("Init", redirectUrl, state, name);
            clientAuth.Call("SetVisibility", true);
            clientAuth.Call("SetMargins", margins.left, margins.top, margins.right, margins.bottom);
            AccessToken = "";

            clientAuth.Call("LoadURL", url);

            yield return new WaitUntil(HasReceivedToken);
        }
    }
}
