using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

namespace TwitchInGames {
    public class Clip {
        public string Id { get; }
        public string Url { get; }
        public string EmbedUrl { get; }
        public string BroadcasterId { get; }
        public string CreatorId { get; }
        public string VideoId { get; }
        public string GameId { get; }
        public string Language { get; }
        public string Title { get; }
        public int ViewCount { get; }
        public string CreatedAt { get; }
        public string ThumbnailUrl { get; }

        private const string _helixUrl = "https://api.twitch.tv/helix/";

        private Clip(HelixClip helixClip) {
            Id = helixClip.id;
            Url = helixClip.url;
            EmbedUrl = helixClip.url;
            BroadcasterId = helixClip.broadcaster_id;
            CreatorId = helixClip.creator_id;
            VideoId = helixClip.video_id;
            GameId = helixClip.game_id;
            Language = helixClip.language;
            Title = helixClip.title;
            ViewCount = helixClip.view_count;
            CreatedAt = helixClip.created_at;
            ThumbnailUrl = helixClip.thumbnail_url;
        }

        public static Coroutine<Clip> Fetch(string clientId, string id) {
            return new Coroutine<Clip>(InternalFetchClip(clientId, id));
        }

        public static Coroutine<string> Create(string clientId, string token, string broadcasterId) {
            return new Coroutine<string>(InternalCreateClip(clientId, token, broadcasterId));
        }

        private static IEnumerator InternalFetchClip(string clientId, string id) {
            // Validate the arguments.
            Utility.CheckArgument(nameof(clientId), clientId);
            Utility.CheckArgument(nameof(id), id);

            // Create and start a Web request for the clip.
            var url = $"{_helixUrl}clips?id={id}";
            var www = UnityWebRequest.Get(url);
            www.SetRequestHeader("Client-ID", clientId);
            yield return www.SendWebRequest();

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

            // Extract the Helix response.
            var jsonEntity = JsonUtility.FromJson<HelixClipResponse>(www.downloadHandler.text);
            if(jsonEntity.data == null || jsonEntity.data.Length < 1) {
                var message = $"Invalid JSON response for Clip.Fetch for clip \"{id}\"";
                Debug.LogError(Utility.FormatMessage("Clip.Fetch", message));
                throw new Exception(message);
            }

            // Convert the Helix clip into an SDK clip.
            var clip = new Clip(jsonEntity.data[0]);
            yield return clip;
        }

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

            // Create and start a Web request for the clip.
            WWWForm form = new WWWForm();
            form.AddField("broadcaster_id", broadcasterId);
            var url = $"{_helixUrl}clips";
            var www = UnityWebRequest.Post(url, form);
            www.SetRequestHeader("Authorization", $"Bearer {token}");
            yield return www.SendWebRequest();

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

            // Extract the Helix response.
            var jsonEntity = JsonUtility.FromJson<CreateClipResponse>(www.downloadHandler.text);
            if(jsonEntity.data == null || jsonEntity.data.Length < 1) {
                var message = "Invalid JSON response for Clip.Create";
                Debug.LogError(Utility.FormatMessage("Clip.Create", message));
                throw new Exception(message);
            }
            
            yield return jsonEntity.data[0].id;
        }


        /// <summary>
        /// Contains Clip information from the Helix API.
        /// </summary>
        [Serializable]
        private class HelixClip {
#pragma warning disable 649 // Field '*' is never assigned to, and will always have its default value *
            public string id;               // ID of the clip being queried.
            public string url;              // URL where the clip can be viewed.
            public string embed_url;        // URL to embed the clip.
            public string broadcaster_id;   // User ID of the stream from which the clip was created.
            public string creator_id;       // ID of the user who created the clip.
            public string video_id;         // ID of the video from which the clip was created.
            public string game_id;          // ID of the game assigned to the stream when the clip was created.
            public string language;         // Language of the stream from which the clip was created.
            public string title;            // Title of the clip.
            public int view_count;          // Number of times the clip has been viewed.
            public string created_at;       // Date when the clip was created.
            public string thumbnail_url;    // URL of the clip thumbnail.
#pragma warning restore 649
        }

        [Serializable]
        private class HelixClipResponse {
#pragma warning disable 649 // Field 'data' is never assigned to, and will always have its default value null
            public HelixClip[] data;
#pragma warning restore 649
        }

        /// <summary>
        /// Contains Clip information from the Helix API.
        /// </summary>
        [Serializable]
        private class CreatedClip {
#pragma warning disable 649 // Field '*' is never assigned to, and will always have its default value *
            public string id;
            public string edit_url;
#pragma warning restore 649
        }

        [Serializable]
        private class CreateClipResponse {
#pragma warning disable 649 // Field 'data' is never assigned to, and will always have its default value null
            public CreatedClip[] data;
#pragma warning restore 649
        }
    }
}
