using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Symphonia.Core;
using Symphonia.Core.Entities;
using Symphonia.Core.Utils;

namespace Symphonia.Infrastructure.Service {
  public class TwitterService {
    private const string TWITTER_API = "https://api.twitter.com/1.1";
    private const string FRIENDS = "friends/list.json";
    private const string OAUTH_VERSION = "1.0";
    private const string OAUTH_METHOD = "HMAC-SHA1";

    private readonly TwitterSettings Twitter;
    private readonly HMACSHA1 Hasher;
    private readonly ILogger log;

    public TwitterService(IOptions<AppSettings> appSettings, ILogger<TwitterService> log) {
      Twitter = appSettings.Value.Twitter;
      var compositeKey = $"{Uri.EscapeDataString(Twitter.ConsumerSecret)}&{Uri.EscapeDataString(Twitter.AccessTokenSecret)}";
      Hasher = new HMACSHA1(ASCIIEncoding.ASCII.GetBytes(compositeKey));
      this.log = log;
    }

    public List<TwUser> GetFriends(int cursor = -1) {
      return GetFriends(Twitter.ScreenName, cursor);
    }

    public List<TwUser> GetFriends(string screenName, int cursor = -1) {
      var url = $"{TWITTER_API}/{FRIENDS}";
      var urlParameters = new Dictionary<string, object>() {
        ["screen_name"] = Uri.EscapeDataString(screenName.DefaultIfEmpty(Twitter.ScreenName)),
        ["count"] = 200,
        ["cursor"] = cursor,
      };

      var oauthBasicInfo = GetOAuthBasicInfo();
      var oauthSignature = GetOAuthSignature(url, oauthBasicInfo.Merge(urlParameters));
      oauthBasicInfo.Add("oauth_signature", oauthSignature);
      var authHeader = $"OAuth {oauthBasicInfo.ToQueryString(s => $"\"{s}\"", ",")}";

      var response = Request($"{url}?{urlParameters.ToQueryString()}", authHeader);
      var responseData = JsonUtil.Decode<TwUserResponse>(response);

      return responseData.Users;
    }

    private Dictionary<string, object> GetOAuthBasicInfo() {
      return new Dictionary<string, object> {
        ["oauth_consumer_key"] = Twitter.ConsumerKey,
        ["oauth_nonce"] = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString())),
        ["oauth_signature_method"] = OAUTH_METHOD,
        ["oauth_timestamp"] = DateUtil.GetCurrentTimestamp().ToString(),
        ["oauth_token"] = Twitter.AccessToken,
        ["oauth_version"] = OAUTH_VERSION,
      };
    }

    private string GetOAuthSignature(string url, Dictionary<string, object> parameters) {
      var baseString = $"GET&{Uri.EscapeDataString(url)}&{Uri.EscapeDataString(parameters.ToQueryString())}";
      return Convert.ToBase64String(Hasher.ComputeHash(ASCIIEncoding.ASCII.GetBytes(baseString)));
    }

    private string Request(string url, string authHeader) {
      log.LogInformation($"Requesting to '{url}' with authorization = '{authHeader}'");
      var request = (HttpWebRequest)WebRequest.Create(url);
      request.Headers["Authorization"] = authHeader;
      request.Method = "GET";
      request.ContentType = "application/x-www-form-urlencoded";

      var response = request.GetResponse();
      return new StreamReader(response.GetResponseStream()).ReadToEnd();
    }
  }
}
