require 'httparty'
require 'yaml'
require 'erb'
require './core/utils/gmail_utils'
require './core/utils/heimdall_utils'
require './core/configs/environment/kraken_environment'
require './core/utils/logger_utils'
require './core/configs/environment/kraken_environment'
require 'smoca_common/utils/twitch_utils'

class UserData
  attr_reader :username, :password, :email, :id, :recurly_id, :inbox, :user_type, :oauth

  DEFAULT_OPTIONS = {heimdall: HeimdallUtils.enabled?, user_type: nil}

  include LoggerUtils

  class << self
    # @return [Hash] Usernames and associated data
    def get_user_data
      @users ||= load_yaml_with_erb
    end

    # @return Returns a boolean indicating if a user is currently live or not
    def live?(username)
      endpoint = "#{KrakenEnvironment.url}/streams/#{username}"

      response = HTTParty.get(endpoint,
        headers: {'Accept' => 'application/vnd.twitchtv.v3+json',
                  'Client-ID' => '7j65feoxl6nd7q7c5jt7uu61j60eqhp'})

      if response.code == 200
        body = JSON.parse(response.body)
        return !!body['stream']
      else
        raise Exception.new("API request error.\n\tEndpoint: GET #{endpoint}\n\tResponse Code: #{response.code}")
      end
    end

    # Loads a YAML File, decoding any embedded ruby
    # @return [Hash] Usernames and associated data
    private def load_yaml_with_erb
      begin
        YAML.load(ERB.new(File.read('./core/data/user_data.yml')).result)
      rescue Exception => e # Example exception: Psych::SyntaxError
        logger.error "[UserData] While reading UserData YAML, encountered exception [#{e.class}] #{e.message}"
        return {}
      end
    end
  end

  # @param user_type [UserType] The UserType to return a username for
  # @param opts [Hash] The options for creating an account
  # @option opts [Boolean] :heimdall (false) To utilize the heimdall service
  # @option opts [String] :user_type (nil) The user type the user belongs to
  # @return [Boolean] Whether the account was created
  def initialize(user_type, opts = {})
    options = DEFAULT_OPTIONS.merge(opts)
    @heimdall = options[:heimdall]
    @user_type = user_type.type
    @rspec_id = user_type.rspec_id

    if @heimdall
      user = HeimdallUtils.get_user(user_type, timeout: 60)
      logger.info "[UserData] Registered #{user} with Heimdall."
    else
      user = user_type.type
    end

    @username = user

    users = self.class.get_user_data
    if users.has_key?(user) # Get credential data
      user_data = users[user]
      @password = user_data['password']
      @email = user_data['email']
      @recurly_id = user_data['recurly_id']
      @oauth = user_data['oauth']
      @id = user_data['id'] if user_data.has_key?('id')

      if user_data.dig('gmail') && user_data.dig('gmail_password')
        connect_to_gmail(user_data['gmail_password'])
      end

      logger.debug "[UserData] Initialized. Username: #{@username} | UserType: #{@user_type} | Heimdall: #{@heimdall}"

      return true
    else
      self.unlock # Release the account from Heimdall before raising exception
      raise Exception.new("Unable to find username #{user} within user_data.rb")
    end
  end

  # Unlocks an account from Heimdall
  # @return [Boolean] Whether or not the unlock was successful
  def unlock
    if @user_type && @heimdall
      return HeimdallUtils.unlock_user(@user_type, @username, timeout: 180)
    end
  end

  def is_partner?
    return get_channel_results['partner']
  end

  # @param [String] user The target user to check if you're following
  # @return [Boolean] Whether or not a follow relationship exists
  def is_following?(user)
    return !!follow_data(user)
  end

  def receives_channel_notifications?(target)
    data = follow_data(target)

    if data
      return data['notifications']
    else
      raise Exception.new("#{self.username} does not follow #{target}."\
                          ' Cannot check channel notifications unless a relationship exists.')
    end
  end

  def stream_status
    return get_channel_results['status']
  end

  def display_name
    return get_channel_results['display_name']
  end

  def game_playing
    return get_channel_results['game']
  end

  def teams
    endpoint = "#{KrakenEnvironment.url}/channels/#{@username}/teams"
    response = HTTParty.get(endpoint,
      headers: {'Accept' => 'application/vnd.twitchtv.v3+json',
                'Client-ID' => '7j65feoxl6nd7q7c5jt7uu61j60eqhp'})

    if response.code == 200
      if response['teams'].length > 0
        # Returns Array
        display_names = Array.new
        response['teams'].each { |key| display_names.push(key['display_name']) }
        return display_names
      else
        return nil
      end
    else
      raise Exception.new("API request error.\n\tEndpoint: GET #{endpoint}\n\tResponse Code: #{response.code}")
    end
  end

  def view_count
    return get_channel_results['views']
  end

  def follower_count
    return get_channel_results['followers']
  end

  # Follows a user
  # @param target_user [String] The user to follow
  # @return [Boolean] if successful, true
  def follow(target_user)
    if TwitchUtils.nil_or_empty?(@oauth)
      raise Exception.new("#{@username} doesn't have an oauth property")
    end
    # rubocop:disable Style/AlignParameters
    endpoint = "#{KrakenEnvironment.url}/users/#{@username}/follows/channels/#{target_user}"
    response = HTTParty.put(endpoint,
        headers: {'Accept' => 'application/vnd.twitchtv.v3+json',
                  'Authorization' => "OAuth #{@oauth}",
                  'Client-ID' => '7j65feoxl6nd7q7c5jt7uu61j60eqhp'})
    if response.code == 200
      return true
    else
      raise Exception.new("API request error.\n\tEndpoint: PUT #{endpoint}\n\tResponse Code: #{response.code}")
    end
  end

  # Unfollows a user
  # @param target_user [String] The user to unfollow
  # @return [Boolean] if successful, true
  def unfollow(target_user)
    if TwitchUtils.nil_or_empty?(@oauth)
      raise Exception.new("#{@username} doesn't have an oauth property")
    end
    endpoint = "#{KrakenEnvironment.url}/users/#{@username}/follows/channels/#{target_user}"
    response = HTTParty.delete(endpoint,
        headers: {'Accept' => 'application/vnd.twitchtv.v3+json',
                  'Authorization' => "OAuth #{@oauth}",
                  'Client-ID' => '7j65feoxl6nd7q7c5jt7uu61j60eqhp'})

    if response.code == 204
      return true
    else
      raise Exception.new("API request error.\n\tEndpoint: DELETE #{endpoint}\n\tResponse Code: #{response.code}")
    end
  end

  private

  def get_channel_results
    endpoint = "#{KrakenEnvironment.url}/channels/#{@username}"
    response = HTTParty.get(endpoint,
      headers: {'Accept' => 'application/vnd.twitchtv.v3+json',
                'Client-ID' => '7j65feoxl6nd7q7c5jt7uu61j60eqhp'})

    if response.code == 200
      return response
    else
      raise Exception.new("API request error.\n\tEndpoint: GET #{endpoint}\n\tResponse Code: #{response.code}")
    end
  end

  # @param target String The user who is followed by the source user (Source User follows Target User)
  # @return Follow Data if user is following. Nil if user is not following.
  def follow_data(target)
    # GET /users/:user/follows/channels/:target
    # Returns 404 Not Found if :user is not following :target. Returns a follow object otherwise.
    endpoint = "#{KrakenEnvironment.url}/users/#{@username}/follows/channels/#{target}"

    response = HTTParty.get(endpoint,
      headers: {'Accept' => 'application/vnd.twitchtv.v3+json',
                'Client-ID' => '7j65feoxl6nd7q7c5jt7uu61j60eqhp'})

    if response.code == 200
      return response
    elsif response.code == 404
      return nil
    else
      raise Exception.new("API request error.\n\tEndpoint: GET #{endpoint}\n\tResponse Code: #{response.code}")
    end
  end

  def connect_to_gmail(password)
    @inbox = GmailUtils.new(@email, password)
  end
end
