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

namespace TwitchInGames {
    public class StreamsRequest {
        public enum PaginationDirection { None, Forward, Backward };

        public string Cursor;
        public PaginationDirection Direction;
        public uint Count;
        public string CommunityId;
        public string GameId;
        public string Language;
        public string UserId;
        public string UserLogin;

        private const string helixStreamRootUrl = "https://api.twitch.tv/helix/streams?";

        public Coroutine<StreamsResponse> Fetch(string clientId, string token) {
            return new Coroutine<StreamsResponse>(InternalFetch(clientId, token));
        }

        private IEnumerator InternalFetch(string clientId, string token) {
            // Validate the arguments.
            Utility.CheckArgument(nameof(clientId), clientId);

            // Construct the request URL.
            string url = helixStreamRootUrl;
            if(Count != 0) {
                url += $"&first={Count}";
            }
            if(Direction != PaginationDirection.None && !String.IsNullOrEmpty(Cursor)) {
                url += Direction == PaginationDirection.Forward ? "&after=" : "&before=";
                url += Cursor;
            }
            if(!String.IsNullOrEmpty(CommunityId)) {
                url += $"&community_id={CommunityId}";
            }
            if(!String.IsNullOrEmpty(GameId)) {
                url += $"&game_id={GameId}";
            }
            if(!String.IsNullOrEmpty(Language)) {
                url += $"&language={Language}";
            }
            if(!String.IsNullOrEmpty(UserId)) {
                url += $"&user_id={UserId}";
            }
            if(!String.IsNullOrEmpty(UserLogin)) {
                url += $"&user_login={UserLogin}";
            }

            // Create and start a Web request for the stream.
            var www = UnityWebRequest.Get(url);
            if(!String.IsNullOrEmpty(token)) {
                www.SetRequestHeader("Authorization", $"Bearer {token}");
            }
            www.SetRequestHeader("Client-ID", clientId);
            yield return www.SendWebRequest();

            // Check for request failure.
            if(www.isNetworkError || www.isHttpError) {
                Debug.LogError(Utility.FormatMessage("StreamsRequest.Fetch", www.error));
                throw new Exception(www.error);
            }

            // Extract the Helix response.
            var jsonEntity = JsonUtility.FromJson<HelixStreamsResponse>(www.downloadHandler.text);

            // Convert the Helix stream into an SDK stream.
            var streamsResponse = new StreamsResponse(jsonEntity);
            yield return streamsResponse;
        }
    }

    public class StreamsResponse {
        public List<Stream> Streams;
        public string Cursor;

        internal StreamsResponse(HelixStreamsResponse jsonEntity) {
            Streams = jsonEntity.data.Select(s => new Stream(s)).ToList();
            Cursor = jsonEntity.pagination.cursor;
        }
    }

    public class Stream {
        public enum StreamType { None, Live, Vodcast };

        public string[] CommunityIds { get; }
        public string GameId { get; }
        public string Id { get; }
        public string Language { get; }
        public DateTime StartedAt { get; }
        public string ThumbnailUrl { get; }
        public string Title { get; }
        public StreamType Type { get; }
        public string UserId { get; }
        public uint ViewerCount { get; }

        internal Stream(HelixStream data) {
            CommunityIds = data.community_ids;
            GameId = data.game_id;
            Id = data.id;
            Language = data.language;
            StartedAt = Utility.StringToDateTime(data.started_at).ToUniversalTime();
            ThumbnailUrl = data.thumbnail_url;
            Title = data.title;
            if(data.type == "live") {
                this.Type = StreamType.Live;
            } else if(data.type == "vodcast") {
                this.Type = StreamType.Vodcast;
            } else {
                this.Type = StreamType.None;
            }
            UserId = data.user_id;
            ViewerCount = data.viewer_count;
        }
    }

    /// <summary>
    /// Contains Stream information from the Helix API.
    /// </summary>
#pragma warning disable 649 // Field '*' is never assigned to, and will always have its default value *
    [Serializable]
    internal class HelixStream {
        public string[] community_ids;    // Array of community IDs.
        public string game_id;            // ID of the game being played on the stream.
        public string id;                 // Stream ID.
        public string language;           // Stream language.
        public string started_at;         // UTC timestamp.
        public string thumbnail_url;      // Thumbnail URL of the stream. All image URLs have variable width and height. You can replace {width} and {height} with any values to get that size image
        public string title;              // Stream title.
        public string type;               // Stream type: "live", "vodcast", or "".
        public string user_id;            // ID of the user who is streaming.
        public uint viewer_count;         // Number of viewers watching the stream at the time of the query.
    }

    [Serializable]
    internal class HelixStreamsPagination {
        public string cursor;             // A cursor value, to be used in a subsequent request to specify the starting point of the next set of results.
    }

    [Serializable]
    internal class HelixStreamsResponse {
        public HelixStream[] data;
        public HelixStreamsPagination pagination;
    }
#pragma warning restore 649
}
