using System;
using System.Net;
using System.Net.Http;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using DesktopVision;
using DesktopVision.DVWebRTC;
using Unity.Plastic.Newtonsoft.Json;

using UnityEngine;

namespace DesktopVision.DVAuth
{

    /// <summary>
    /// This class is used to pass auth events.
    /// </summary>
    public class AuthEventArgs : EventArgs
    {
        /// <summary>
        /// The auth code which needs to be validated by users
        /// </summary>
        /// <remarks>
        /// This is the code which is displayed to the user to validate the auth request here: https://desktop.vision/app/#/authorize
        /// </remarks>
        public AuthCode authcode { get; set; }
        /// <summary>
        /// The auth token which is used to retrieve computers from the platform api
        /// </summary>
        public AuthToken authToken { get; set; }
        /// <summary>
        /// Any errors encountered when using the auth class
        /// </summary>
        public Exception authError { get; set; }
    }

    /// <summary>
    /// This class is used to store an access token and UID for use with Desktop Vision
    /// </summary>
    public class Auth
    {
        /// <summary>
        /// The App ID from the partner portal used to make API requests to the platform
        /// </summary>
        public string DesktopVisionAppId = "";
        /// <summary>
        /// The API key from the partner portal used to make API requests to the platform
        /// </summary>
        public string DesktopVisionApiKey = "";

        /// <summary>
        /// The auth code used to authenticate with Desktop Vision
        /// </summary>
        public AuthCode DesktopVisionAuthCode;


        /// <summary>
        /// The access token used to make API requests to the platform
        /// </summary>
        public AuthToken DesktopVisionAuthToken;

        /// <summary>
        /// The Event Dispatcher used to send out auth events
        /// </summary>
        /// <remarks>
        /// Use this to listen for auth events and update UI or store an auth code or auth token for use with Desktop Vision
        /// </remarks>
        public event EventHandler<AuthEventArgs> Dispatch;

        /// <summary>
        /// The Auth manager for accessing the Desktop Vision API
        /// </summary>
        /// <param name="DesktopVisionApiKey">The api key found in the partner portal, used for accessing the Desktop Vision API</param>
        /// <param name="DesktopVisionAppId">The app id found in the partner portal, used for accessing the Desktop Vision API</param>
        /// 
        public Auth() { }
        protected virtual void DispatchEvent(AuthEventArgs e)
        {
            EventHandler<AuthEventArgs> handler = Dispatch;
            Dispatch?.Invoke(this, e);
        }


        /// <summary>
        /// This method is used to get an auth code from Desktop Vision
        /// </summary>
        /// <param name="DesktopVisionAppId">The app id found in the partner portal, used for accessing the Desktop Vision API</param>
        /// <param name="DesktopVisionApiKey">The api key found in the partner portal, used for accessing the Desktop Vision API</param>
        /// <returns>
        /// The auth code used to authenticate with Desktop Vision
        ///</returns>
        /// <remarks>
        /// This method is used to get an auth code from Desktop Vision. This method is asynchronous and will dispatch the code through the Dispatcher.
        /// </remarks>
        public async Task<AuthCode> generateCode(string DesktopVisionAppId, string DesktopVisionApiKey)
        {
            //if missing app id or api key, throw error
            if (DesktopVisionApiKey == "" || DesktopVisionAppId == "")
            {
                dispatchError(new Exception("Missing App ID or API Key"));
            }
            HttpClient client = new HttpClient();
            client.DefaultRequestHeaders.Add("X-App-Id", DesktopVisionAppId);
            client.DefaultRequestHeaders.Add("X-Api-Key", DesktopVisionApiKey);
            HttpResponseMessage response = await client.PostAsync("https://desktop.vision/api/oauth/create-code", null);
            var responseString = await response.Content.ReadAsStringAsync();
            DesktopVisionAuthCode = JsonConvert.DeserializeObject<AuthCode>(responseString);
            DispatchEvent(new AuthEventArgs { authcode = DesktopVisionAuthCode });
            return DesktopVisionAuthCode;
        }

        /// <summary>
        /// This method is used to wait for an auth token from Desktop Vision
        /// </summary>
        /// <param name="appId">The app id found in the partner portal, used for accessing the Desktop Vision API</param>
        /// <param name="key">The api key found in the partner portal, used for accessing the Desktop Vision API</param>
        /// <param name="code">The auth code used to authenticate with Desktop Vision</param>
        /// <returns>
        /// The auth token used to retrieve and connect to computers
        ///</returns>
        /// <remarks>
        /// This method is used to get an auth token from Desktop Vision. This method is asynchronous and will dispatch the token through the Dispatcher.
        /// </remarks>
        public async Task<AuthToken> waitForAccessToken(string appId, string key, string code) {
            while (DesktopVisionAuthCode != null && DesktopVisionAuthCode.code == code) {
                await Task.Delay(5000);
                await generateAccessToken(appId, key, code);
            }
            return DesktopVisionAuthToken;
        }


        /// <summary>
        /// This method is used to get an auth token from Desktop Vision
        /// </summary>
        /// <param name="DesktopVisionAppId">The app id found in the partner portal, used for accessing the Desktop Vision API</param>
        /// <param name="DesktopVisionApiKey">The api key found in the partner portal, used for accessing the Desktop Vision API</param>
        /// <param name="DesktopVisionAuthCode">The auth code used to authenticate with Desktop Vision</param>
        /// <returns>
        /// The auth token used to retrieve and connect to computers
        ///</returns>
        /// <remarks>
        /// This method is used to get an auth token from Desktop Vision. This method is asynchronous and will dispatch the token through the Dispatcher.
        /// </remarks>
        public async Task<AuthToken> generateAccessToken(string DesktopVisionAppId, string DesktopVisionApiKey, string DesktopVisionAuthCode)
        {
            //if missing app id or api key, throw error
            if (DesktopVisionApiKey == "" || DesktopVisionAppId == "")
            {
                dispatchError(new Exception("Missing App ID or API Key"));
            }

            //if missing auth code, throw error
            if (DesktopVisionAuthCode == "")
            {
                dispatchError(new Exception("Missing Auth Code"));
            }

            try
            {
                HttpClient client = new HttpClient();
                client.DefaultRequestHeaders.Add("X-APP-ID", DesktopVisionAppId);
                client.DefaultRequestHeaders.Add("X-API-KEY", DesktopVisionApiKey);
                string json = "{\"code\":\"" + DesktopVisionAuthCode + "\"}";
                var content = new StringContent(json, Encoding.UTF8, "application/json");
                HttpResponseMessage response = await client.PostAsync("https://desktop.vision/api/oauth/access-token", content);
                var responseString = await response.Content.ReadAsStringAsync();
                if (responseString == "false") throw new Exception("Invalid Auth Code");

                Debug.Log("Auth Token: " + responseString);

                DesktopVisionAuthToken = JsonConvert.DeserializeObject<AuthToken>(responseString);
                DispatchEvent(new AuthEventArgs { authToken = DesktopVisionAuthToken });
                if (DesktopVisionAuthToken != null && DesktopVisionAuthToken.access_token != null) {
                    DesktopVisionAuthCode = null;
                }
                return DesktopVisionAuthToken;
            }
            catch (Exception e)
            {
                dispatchError(e);
                return null;
            }

        }

        /// <summary>
        /// This Task is used to list all user computers from an auth token & uid
        /// </summary>
        /// <param name="DesktopVisionAuthToken">The auth token used to retrieve and connect to computers</param>
        /// <param name="DesktopVisionUid">The uid used to retrieve and connect to computers</param>
        /// <returns>
        /// The list of computers
        ///</returns>
        public async Task<List<DVComputerRecord>> listUserComputers(string DesktopVisionAuthToken, string DesktopVisionUID)
        {
            if (DesktopVisionAuthToken == "")
            {
                dispatchError(new Exception("Auth Token is required"));
            }

            if (DesktopVisionUID == "")
            {
                dispatchError(new Exception("User ID is required"));
            }

            try
            {
                HttpClient client = new HttpClient();
                HttpResponseMessage response = await client.GetAsync($"https://desktop.vision/api/users/{DesktopVisionUID}/computers?access_token={DesktopVisionAuthToken}");
                var responseString = await response.Content.ReadAsStringAsync();
                if (responseString == "false") throw new Exception("Invalid Auth token & UID combination");
                List<DVComputerRecord> computerRecords = JsonConvert.DeserializeObject<List<DVComputerRecord>>(responseString);
                return computerRecords;
            }
            catch (Exception e)
            {
                dispatchError(e);
                return null;
            }

        }

        /// <summary>
        /// This Task is used to get connection information for a computer
        /// </summary>
        /// <param name="DesktopVisionAuthToken">The auth token used to retrieve and connect to the computer</param>
        /// <param name="ChannelID">The channel id used to identify a computer</param>

        public async Task<RoomOptions> getUserComputerConnectionOptions(string DesktopVisionAuthToken, string ChannelID)
        {
            if (DesktopVisionAuthToken == "")
            {
                dispatchError(new Exception("Auth Token is required"));
            }

            if (ChannelID == "")
            {
                dispatchError(new Exception("Channel ID is required"));
            }

            HttpClient client = new HttpClient();
            try
            {
                string json = "{\"channel_name\":\"" + ChannelID + "\"}";
                var content = new StringContent(json, Encoding.UTF8, "application/json");
                HttpResponseMessage response = await client.PostAsync($"https://desktop.vision/api/connect?access_token={DesktopVisionAuthToken}", content);

                var responseString = await response.Content.ReadAsStringAsync();
                if (responseString == "false") throw new Exception("Invalid Auth token & UID combination");
                RoomOptionsContainer roomOptionsContainer = JsonConvert.DeserializeObject<RoomOptionsContainer>(responseString);
                return roomOptionsContainer.roomOptions;
            }
            catch (Exception e)
            {
                dispatchError(e);
                return null;
            }
        }
        private void dispatchError(Exception e)
        {
            // log errors here if desired
            // Debug.Log(e.Message);
            DispatchEvent(new AuthEventArgs { authError = e });
        }

    }


    /// <summary>
    /// This class is used to store an auth code for use with Desktop Vision
    /// </summary>
    public class AuthCode
    {
        /// <summary>
        /// The auth code used to authenticate with Desktop Vision
        /// This code will need to be validated on the platform
        /// Then it may be used to create an access token
        /// </summary>
        public string code;

        /// <summary>
        /// The Auth Code for accessing the Desktop Vision API
        /// </summary>
        /// <param name="code">The auth code</param>
        public AuthCode(string code)
        {
            this.code = code;
        }
    }
    /// <summary>
    /// This class is used to store an auth token for use with Desktop Vision
    /// </summary>
    public class AuthToken
    {
        /// <summary>
        /// The auth code used to authenticate with Desktop Vision
        /// This code will need to be validated on the platform
        /// Then it may be used to create an access token
        /// </summary>
        public string access_token;

        /// <summary>
        /// The UID of the desktop vision user
        /// </summary>
        public string uid;

        ///<summary>
        /// the computer id linked to the auth token
        ///</summary>
        public string computerId;

        /// <summary>
        /// The Auth Code for accessing the Desktop Vision API
        /// </summary>
        /// <param name="access_token">The access token</param>
        /// <param name="uid">The UID of the desktop vision user</param>
        public AuthToken(string access_token, string uid, string computer)
        {
            this.access_token = access_token;
            this.uid = uid;
            this.computerId = computer;
        }
    }
}