# A method #provider_can_use? should be always available without
# the need of explicitly specifying the caller. You are able to
# call `provider_can_use?(:oauth_proxy)` in controller, view
# but also in a Service.
#
module Logic::RollingUpdates
  class UnknownFeatureError < StandardError; end
  class UnknownFeatureConfigError < StandardError
    include Bugsnag::MetaData

    def initialize(feature)
      name = feature.name
      type = feature.class.name
      config = Features::Yaml.config
      self.bugsnag_meta_data = {
        feature: name,
        config: config
      }
      super "Unknown configuration for feature ':#{name}' of type #{type}"
    end
  end

  def self.config
    Rails.configuration.three_scale.rolling_updates
  end

  def self.enabled?
    config.enabled
  end

  def self.skipped?
    config.skipped
  end

  def self.disabled?
    !enabled?
  end

  def self.feature(name)
    feature = name.to_s.camelize

    if Features.const_defined?(feature, false)
      Features.const_get(feature, false)
    elsif Logic::RollingUpdates.config.raise_error_unknown_features
      raise UnknownFeatureError, "Unknown provider feature #{name}"
    end
  end

  module Features
    class Yaml
      def self.config
        Rails.configuration.three_scale.rolling_updates.features || {}.freeze
      end
    end

    class Base
      attr_reader :provider
      delegate :master?, :provider?, :enterprise?, to: :provider
      delegate :id, to: :provider, prefix: true

      OPENSHIFT_PROVIDER_ID = 2

      def initialize(provider)
        @provider = provider
      end

      def name
        self.class.name.demodulize.underscore.to_sym
      end

      def state
        Yaml.config[name]
      end

      def enabled?
        case state
        when true, false
          state
        when Array
          state.include?(provider_id)
        when nil
          missing_config
        else
          raise_invalid_config
        end
      end

      def raise_invalid_config
        raise UnknownFeatureConfigError, self
      end
    end

    class OldCharts < Base
      def missing_config
        Logic::RollingUpdates::Provider::NOSTALGIC_CHART_USERS.include?(provider_id)
      end
    end

    class CubertRequestLogs < Base
      def missing_config
        Logic::RollingUpdates::Provider::CUBERT_LOGS.include?(provider_id)
      end
    end

    class NewProviderDocumentation < Base
      def missing_config
        Logic::RollingUpdates::Provider::NEW_PROVIDER_DOCUMENTATION.include?(provider_id)
      end
    end

    class ProxyPro < Base
      def missing_config
        Logic::RollingUpdates::Provider::PROXY_PRO.include?(provider_id)
      end
    end

    class InstantBillPlanChange < Base
      def missing_config
        Logic::RollingUpdates::Provider::INSTANT_BILL_CHANGE_PLAN.include?(provider_id)
      end
    end

    class ServicePermissions < Base
      def missing_config
        master? || Logic::RollingUpdates::Provider::SERVICE_PERMISSIONS.include?(provider_id) || enterprise?
      end
    end

    class PublishedServicePlanSignup < Base
      def missing_config
        false
      end
    end

    class AsyncApicastDeploy < Base
      def missing_config
        false
      end
    end

    class DuplicateApplicationId < Base
      def missing_config
        Logic::RollingUpdates::Provider::DUPLICATE_APPLICATION_ID.include?(provider_id)
      end
    end

    class DuplicateUserKey < Base
      def missing_config
        Logic::RollingUpdates::Provider::DUPLICATE_USER_KEY.include?(provider_id)
      end
    end

    class PlanChangesWizard < Base
      def missing_config
        master? || Rails.env.test? || Logic::RollingUpdates::Provider::THREESCALERS.include?(provider_id)
      end
    end

    class RequireCcOnSignup < Base
      def missing_config
        master? || Logic::RollingUpdates::Provider::THREESCALERS.include?(provider_id) || provider_created_at < Date.new(2016, 7, 5)
      end

      def provider_created_at
        provider.created_at.try(:to_date) || Date.today
      end
    end

    class ApicastPerService < Base
      def missing_config
        master? || Logic::RollingUpdates::Provider::APICAST_PER_SERVICE.include?(provider_id) || enterprise?
      end
    end

    class NewNotificationSystem < Base
      def missing_config
        master? || provider?
      end
    end

    class CMSApi < Base
      def missing_config
        master? || Logic::RollingUpdates::Provider::THREESCALERS.include?(provider_id)
      end
    end

    class ApicastV2 < Base
      def missing_config
        master?
      end
    end

    class Forum < Base
      def missing_config
        true
      end
    end

    class Unknown < Base
      def missing_config
        false
      end
    end
  end

  module Provider

    def enterprise?
      # bought plan depends on a bought cinstance
      if has_bought_cinstance? && bought_plan.present?
        bought_plan.system_name.to_s.include?('enterprise')
      end
    end

    THREESCALERS = [
      6, #master
      2, # seed account
      16, #vodafone
      2445581111194, # jmp
      2445581100447, # thomasmaas
      2445581128381, # andrew
      2445581381726, # pili https://pili-admin.3scale.net
      2445581145345, # cubert
      2445579853662, # michal
      2445580756182, # victor
      2445581290781, # https://sidonie3scale-admin.3scale.net #sidonie
      2445581432677, # sidonie
      2445580339951, # toni
      2445579853692, # rai
      2445581429260, # hery https://propitech-admin.3scale.net,
      2445581331813, # kevin
      2445581287385, # vanessa https://test-chrome-admin.3scale.net,
      2445581444271, # daria https://daria-admin.3scale.net
    ].freeze

    PROXY_PRO = [
      2445579853692, # rai
      2445581100447, # thomasmaas
      2445581238999, # victor_testing_again
      2445581155550, # gannett
      2445581443483, # servicemaster test account
      2445581447635, # zdf test account
      2445581448324, # daria-test
      2445581473343, # NewsCorp Australia (production)
      2445581420175, # NewsCorp Australia (PoC)
      2445581515779, # bankaudigroup (Evaluation) Added by Tom Corcoran
      2445581548912, # Dow jones test
      2445581554951, # Dow Jones integration (dowjones-integration)
      2445581448287, # Dow Jones staging (dowjones-staging)
      2445581377290, # Dow Jones production (dowjones)
    ].freeze

    CUBERT_LOGS = [].freeze # TODO: get rid of cubert logs logic and config (settings)

    NEW_PROVIDER_DOCUMENTATION = [2445581316484, # https://josegalisteo0707-admin.3scale.net
                                 ].freeze

    INSTANT_BILL_CHANGE_PLAN = [
      2445579853692, # raimon4 - https://raimon4-admin.3scale.net
      2445581432677, # sidonie - https://mycompany3-admin.3scale.net
      2445581361688, # Indix  - https://ix-admin.3scale.net
    ].freeze

    DUPLICATE_APPLICATION_ID = [
      2445581380086, # https://multitenant-admin.3scale.net/buyers/accounts/2445581380086 / eHealth
      2445581407485, # https://multitenant-admin.3scale.net/buyers/accounts/2445581407485 / Dow Jones testing
      2445581399436, # https://multitenant-admin.3scale.net/buyers/accounts/2445581399436 / sandppoc
      2445581448287, # https://multitenant-admin.3scale.net/buyers/accounts/2445581448287 / Dow Jones staging
      2445581377290, # https://multitenant-admin.3scale.net/buyers/accounts/2445581377290 / Dow Jones production
      2445581554951, # https://multitenant-admin.3scale.net/buyers/accounts/2445581554951 / Dow jones integration
      2445581548912, # https://multitenant-admin.3scale.net/buyers/accounts/2445581548912 / Dow jones Vinay's test account
      2445581416823, # https://multitenant-admin.3scale.net/buyers/accounts/2445581416823 / Putnam
      2445581331813, # https://multitenant-admin.3scale.net/buyers/accounts/2445581331813 / Kevin's personal account for testing
      2445581420175, # NewsCorp Australia (PoC)
      2445581433661, # https://multitenant-admin.3scale.net/buyers/accounts/2445581433661 / ZDF
      2445581473343, # NewsCorp Australia (production)
      2445581609998, # Volvo (PoC)
      2445581827892, # Soap Fuse Demo Account (PoC)
    ].freeze

    DUPLICATE_USER_KEY = [
      6, #master
      2445581444271, # Daria's test account
      2445581420175, # NewsCorp Australia (PoC)
      2445581473343, # NewsCorp Australia (production)
      2445581466260, # Boeing
      2445579973841, # SITA Lab
      2445580806752, # SITA Test account
      2445581551239, # CATHAY PACIFIC POC
      2445581361446, # Servicemaster
      2445580619842, # DrillingInfo
    ].freeze

    NOSTALGIC_CHART_USERS = [].freeze

    SERVICE_PERMISSIONS = (THREESCALERS + [
      2445579973841, # Sita
      2445581447214, # Sita
      2445580806752, # Sita
      2445579851281, # FlightStats
      2445581300291, # OUP
      2445581142860, # Cartera Commerce
      2445581052302, # Weather Decision Technologies
    ]).freeze

    APICAST_PER_SERVICE = (THREESCALERS + [
      2445579973841, # Sita
      2445581447214, # Sita
      2445580806752, # Sita
      2445579851281, # FlightStats
      2445581300291, # OUP
      2445581142860, # Cartera Commerce
      2445581052302, # Weather Decision Technologies
    ]).freeze

    ASYNC_APICAST_DEPLOY = [].freeze


    def provider_can_use?(feature)
      return false if Logic::RollingUpdates.skipped?
      return true if Logic::RollingUpdates.disabled?


      rolling_update(feature).enabled?
    rescue UnknownFeatureConfigError, UnknownFeatureError
      raise
    rescue StandardError => e
      System::ErrorReporting.report_error(e)
      (Rails.env.test? || Rails.env.development?) ? raise(e) : false
    end

    def rolling_update(name)
      feature = ::Logic::RollingUpdates.feature(name) || ::Logic::RollingUpdates.feature(:unknown)
      feature.new(self)
    end
  end

  module Service
    private

    def provider_can_use?(fresh_feature)
      provider.provider_can_use?(fresh_feature)
    end
  end

  module Controller
    def self.included(klass)
      klass.helper_method :provider_can_use?
    end

    protected

    def provider_can_use?(fresh_feature)
      return false if Logic::RollingUpdates.skipped?
      return true if Logic::RollingUpdates.disabled?

      if current_user.try!(:username) == '3scaleadmin'
        true
      else
        current_account.try!(:provider_can_use?, fresh_feature)
      end
    end
  end
end
