require 'json/add/struct'

module Mako
  class EmoteUploader < Mako::Base

    self.attributes :code_suffix

    GLOBAL_EMOTES_UPLOADED_WITH_LOCALE_NOTIFICATION_TYPE = "global_emotes_uploaded_with_locale"
    GLOBAL_EMOTE_NOTIFICATION_RECIPIENT_EMAIL = "globalemotes@twitch.tv"
    GLOBAL_EMOTE_NOTIFICATION_RECIPIENT_LOCALE = "en"

    # global emotes use an empty prefix
    GLOBAL_EMOTE_CODE_PREFIX = ''

    # global emotes use a default user_id owner of 0
    GLOBAL_EMOTE_OWNER_ID = '0'

    # global emotes use a default group_id of 0
    GLOBAL_EMOTE_GROUP_ID = '0'

    EMOTE_28_URL_PATH = "%<twitch_cdn_endpoint>s/emoticons/v1/%<emote_id>s/1.0"
    EMOTE_56_URL_PATH = "%<twitch_cdn_endpoint>s/emoticons/v1/%<emote_id>s/2.0"
    EMOTE_128_URL_PATH = "%<twitch_cdn_endpoint>s/emoticons/v1/%<emote_id>s/3.0"

    SUBSCRIPTIONS_EMOTE_GROUP = "Subscriptions"
    GLOBALS_EMOTE_GROUP = "Globals"

    STANDARD_SIZES = [Code::Justin::Tv::Commerce::Mako::Dashboard::EmoteImageSize.resolve(:size_1x),
                      Code::Justin::Tv::Commerce::Mako::Dashboard::EmoteImageSize.resolve(:size_2x),
                      Code::Justin::Tv::Commerce::Mako::Dashboard::EmoteImageSize.resolve(:size_4x)]

    def self.connection
      super(request: :multipart)
    end

    def self.upload(options)
      if options[:code_suffix].blank?
        raise ArgumentError, "code_suffix cannot be empty"
      end

      if options[:data28].blank? || options[:data56].blank? || options[:data112].blank?
        raise ArgumentError, "image files cannot be empty"
      end

      emote_images = [
        {
          width: 28,
          height: 28,
          config: nil,
          image_path: options[:data28].tempfile.path
        },
        {
          width: 56,
          height: 56,
          config: nil,
          image_path: options[:data56].tempfile.path
        },
        {
          width: 112,
          height: 112,
          config: nil,
          image_path: options[:data112].tempfile.path
        }
      ]

      upload_configs = get_emote_upload_config(options[:user_id])
      emote_images[0][:config] = upload_configs[:image_1x]
      emote_images[1][:config] = upload_configs[:image_2x]
      emote_images[2][:config] = upload_configs[:image_4x]

      upload_image_to_url(emote_images[0][:image_path], emote_images[0][:config][:upload_url])
      upload_image_to_url(emote_images[1][:image_path], emote_images[1][:config][:upload_url])
      upload_image_to_url(emote_images[2][:image_path], emote_images[2][:config][:upload_url])

      # Sleep after uploading all images. This allows Uploader service to process the images and propagate them to their destination URLs
      sleep 3

      emote_group = SUBSCRIPTIONS_EMOTE_GROUP
      if options[:user_id] == GLOBAL_EMOTE_OWNER_ID
        emote_group = GLOBALS_EMOTE_GROUP
      end

      # Build the emote code using prefix and suffix inputs
      code = "#{options[:code_prefix]}#{options[:code_suffix]}"

      create_emoticon_params = {
        user_id: options[:user_id],
        code: code,
        code_suffix: options[:code_suffix],
        domain: "emotes",
        emote_group: emote_group,
        state: "active",
        group_id: options[:group_id],
        image28_id: emote_images[0][:config][:image_id],
        image56_id: emote_images[1][:config][:image_id],
        image112_id: emote_images[2][:config][:image_id]
      }

      created_emote_id = create_emoticon(create_emoticon_params)
      if options[:user_id] == GLOBAL_EMOTE_OWNER_ID
        params = {
          uploader_user_name: options[:uploader_user_name],
          uploader_email: options[:uploader_email],
          emote_id: created_emote_id
        }
        publish_global_emote_notification(params)
      end

      return created_emote_id
    end

    def self.get_emote_upload_config(user_id)
      response = dashboard_client.get_emote_upload_config(
        user_id: user_id,
        resize_plan: Code::Justin::Tv::Commerce::Mako::Dashboard::ResizePlan.resolve(:no_resize),
        sizes: STANDARD_SIZES
      )
      raise response.error.msg if response.error

      upload_config = response.data['upload_configs']

      image1x_config = upload_config[0]
      image1x_image_info = image1x_config['images'][0]

      image2x_config = upload_config[1]
      image2x_image_info = image2x_config['images'][0]

      image3x_config = upload_config[2]
      image3x_image_info = image3x_config['images'][0]

      return {
          image_1x: {
              upload_id: image1x_config['id'],
              upload_url: image1x_config['url'],
              image_id: image1x_image_info['id'],
              image_url: image1x_image_info['url']
          },
          image_2x: {
              upload_id: image2x_config['id'],
              upload_url: image2x_config['url'],
              image_id: image2x_image_info['id'],
              image_url: image2x_image_info['url']
          },
          image_4x: {
              upload_id: image3x_config['id'],
              upload_url: image3x_config['url'],
              image_id: image3x_image_info['id'],
              image_url: image3x_image_info['url']
          }
      }
    end

    def self.upload_image_to_url(image_path, presigned_url)
      conn = Faraday.new(presigned_url) do |faraday|
        faraday.request :multipart
        faraday.request :url_encoded
        faraday.adapter Faraday.default_adapter
      end

      resp = conn.put('') do |req|
        req.body = File.binread(image_path)
      end

      unless resp.try(:success?)
        raise UploadRequestError.new('Could not upload image', resp)
      end
    end

    def self.create_emoticon(params)
      response = client.create_emoticon(
        user_id: params[:user_id],
        code: params[:code],
        code_suffix: params[:code_suffix],
        domain: params[:domain],
        emote_group: params[:emote_group],
        state: params[:state],
        group_id: params[:group_id],
        image28_id: params[:image28_id],
        image56_id: params[:image56_id],
        image112_id: params[:image112_id]
      )
      raise response.error.msg if response.error

      emote_id = response.data['id']
      return emote_id
    end

    def self.publish_global_emote_notification(params)
      emote_28_url = format(EMOTE_28_URL_PATH, twitch_cdn_endpoint: Settings.twitch.cdn.endpoint, emote_id: params[:emote_id])
      emote_56_url = format(EMOTE_56_URL_PATH, twitch_cdn_endpoint: Settings.twitch.cdn.endpoint, emote_id: params[:emote_id])
      emote_128_url = format(EMOTE_128_URL_PATH, twitch_cdn_endpoint: Settings.twitch.cdn.endpoint, emote_id: params[:emote_id])

      metadata = {
        "uploader_user_name" => params[:uploader_user_name],
        "uploader_email" => params[:uploader_email],
        "emote_28_url" => emote_28_url,
        "emote_56_url" => emote_56_url,
        "emote_128_url" => emote_128_url,
      }

      DartReceiver::Notification.publish_notification_to_email_address_with_locale(
        GLOBAL_EMOTES_UPLOADED_WITH_LOCALE_NOTIFICATION_TYPE,
        GLOBAL_EMOTE_NOTIFICATION_RECIPIENT_EMAIL,
        GLOBAL_EMOTE_NOTIFICATION_RECIPIENT_LOCALE,
        metadata
      )
    end
  end
end
