module PayoutEntities
  class Invitation < PayoutEntities::Base
    attributes :user_id, :user_login, :user_category, :terms, :is_esports

    validates :user_id, :user_category, presence: true
    validates :terms, presence: true, if: :partner?

    AUDIT_ACTION_INVITE = "invite"
    AUDIT_ACTION_REVOKE_INVITATION = "revoke_invitation"
    AUDIT_ACTION_OFFBOARD = "offboard"
    AUDIT_TWITCH_USER = "twitch_user"
    AUDIT_LDAP_USER = "ldap_user"
    AUDIT_CHANGE_CATEGORY = "category"
    AUDIT_CHANGE_TAGS = "tags"
    AUDIT_CHANGE_FEATURES = "features"

    CATEGORY_0CPM = "0cpm"
    CATEGORY_AFFILIATE = "affiliate"
    CATEGORY_PARTNER = "partner"
    CATEGORY_DEVELOPER = "developer"

    TERMS_STANDARD = "Standard"
    TERMS_PREMIUM = "Premium"
    TERMS_CUSTOM = "Custom"

    ACTION_DOWNGRADE = "Downgrade to Affiliate"

    # This is a mapping of invite categories to the permissions required to operate on them
    AVAILABLE_CATEGORIES_AND_PERMISSIONS = {
      CATEGORY_0CPM => :manage_partner_status,
      CATEGORY_AFFILIATE => :manage_affiliate_status,
      CATEGORY_PARTNER => :manage_partner_status,
      CATEGORY_DEVELOPER => :manage_developer_status
    }

    # Only certain progressions make sense, so we use this to display only valid options in the UI
    ALLOWED_PROGRESSIONS = {
      CATEGORY_0CPM => [CATEGORY_PARTNER],
      CATEGORY_AFFILIATE => [CATEGORY_PARTNER],
      CATEGORY_PARTNER => [CATEGORY_PARTNER]
    }

    def save(options = {})
      return false unless valid?

      begin
        user = Twitch::User.find(user_id)
      rescue RuntimeError
        self.errors.add(:base, "Invalid user ID provided")
        return false
      end

      if user.blank?
        self.errors.add(:base, "User #{user_id} does not exist")
        return false
      end

      warnings.append("<strong>Warning: #{user.login}</strong> (#{user_id}) was invited and is a staff user. If this was intentional, please contact HR immediately so this user can be paid via payroll") if user.admin
      warnings.append("<strong>Warning: #{user.login}</strong> (#{user_id}) was invited and is TOS banned") if user.terms_of_service_violation
      warnings.append("<strong>Warning: #{user.login}</strong> (#{user_id}) was invited and is DMCA banned") if user.dmca_violation
      warnings.append("<strong>Warning: #{user.login}</strong> (#{user_id}) was invited and is a deleted user") if user.deleted_on

      response = post "/payout/invite/#{user_id}", body: save_params.to_json, headers: PayoutEntity.ldap_headers(options)

      unless response.success?
        msg = response.body["message"].present? ? response.body["message"] : "Internal server error"
        self.errors.add(:base, msg)

        return false
      end

      options.merge!(
        user_id: user_id,
        user_category: user_category.downcase(),
        tags: tags.join(", "),
        features: features
      )

      self.class.audit_invite(options)
      response.success?
    end

    def self.invitations(user_id)
      response = get "/payout/invites/#{user_id}"

      return [] unless response.success?
      return response.body
    end

    def self.destroy(invitation_id, options = {})
      response = delete "/payout/invite/revoke/#{invitation_id}", headers: ldap_headers(options)

      if response.success?
        Invitation.audit_revoke(options)
      end
      return response.success?
    end

    def self.remove(user_id, old_category, new_category, options = {})
      remove_params = {category: new_category}
      response = post "/payout/#{user_id}/offboard", body: remove_params.to_json, headers: ldap_headers(options)

      if response.success?
        options.merge!(
          user_id: user_id,
          old_category: old_category.downcase,
          new_category: new_category.downcase,
        )
        Invitation.audit_offboard(options)
      end
      return response
    end

    def self.partner_terms_from_tags(tags)
      if tags.present?
        tags.each do |tag|
          if [
            TERMS_STANDARD,
            TERMS_PREMIUM,
            TERMS_CUSTOM
          ].include? tag.humanize
            return tag.humanize
          end
        end
      end

      return ""
    end

    def save_params
      {
        category: user_category.downcase(),
        tags: tags,
        features: features
      }
    end

    def self.audit_invite(options)
      category_change = History::ChangeSet.new(
        attribute: AUDIT_CHANGE_CATEGORY,
        new_value: options[:user_category]
      )
      tags_change = History::ChangeSet.new(
        attribute: AUDIT_CHANGE_TAGS,
        new_value: options[:tags]
      )
      features_change = History::ChangeSet.new(
        attribute: AUDIT_CHANGE_FEATURES,
        new_value: options[:features]
      )

      changes = [category_change, tags_change, features_change]

      ldap_login = options[:ldap_login] || ""
      audit = History::Audit.new(
        action: AUDIT_ACTION_INVITE,
        user_type: AUDIT_LDAP_USER,
        user_id: ldap_login,
        resource_type: AUDIT_TWITCH_USER,
        resource_id: options[:user_id],
        description: "",
        changes: changes
      )

      History::AddAudit.add(audit)
    end

    def self.audit_revoke(options)
      ldap_login = options[:ldap_login] || ""
      audit = History::Audit.new(
        action: AUDIT_ACTION_REVOKE_INVITATION,
        user_type: AUDIT_LDAP_USER,
        user_id: ldap_login,
        resource_type: AUDIT_TWITCH_USER,
        resource_id: options[:user_id],
        description: options[:category],
        changes: []
      )

      History::AddAudit.add(audit)
    end

    def self.audit_offboard(options)
      change = History::ChangeSet.new(
        attribute: AUDIT_CHANGE_CATEGORY,
        old_value: options[:old_category],
        new_value: options[:new_category]
      )

      ldap_login = options[:ldap_login] || ""
      audit = History::Audit.new(
        action: AUDIT_ACTION_OFFBOARD,
        user_type: AUDIT_LDAP_USER,
        user_id: ldap_login,
        resource_type: AUDIT_TWITCH_USER,
        resource_id: options[:user_id],
        description: "",
        changes: [change]
      )

      History::AddAudit.add(audit)
    end

    def warnings
      @warnings ||= []
    end

    def affiliate?
      user_category == CATEGORY_AFFILIATE
    end

    def partner?
      user_category == CATEGORY_PARTNER
    end

    def esports?
      self.class.booleanize is_esports
    end

    def tags
      tags = []
      tags.append(terms.downcase()) if terms.present?
      tags.append("esports") if esports?
      tags
    end

    def features
      features = {}
      if affiliate?
        features = {
          subs: true,
          bits: true,
          pre_roll_ads: false,
          post_roll_ads: false
        }
      elsif partner?
        features = {
          subs: true,
          bits: true,
          pre_roll_ads: true,
          post_roll_ads: true
        }
      end
      features
    end
  end
end
