# frozen_string_literal: true
require "discourse_subscription_client"

class CustomWizard::Subscription
  PRODUCT_HIERARCHY = %w[
    community
    standard
    business
  ]

  def self.attributes
    {
      wizard: {
        required: {
          none: [],
          standard: ['*'],
          business: ['*'],
          community: ['*']
        },
        permitted: {
          none: [],
          standard: ['*'],
          business: ['*'],
          community: ['*', "!#{CustomWizard::Wizard::GUEST_GROUP_ID}"]
        },
        restart_on_revisit: {
          none: [],
          standard: ['*'],
          business: ['*'],
          community: ['*']
        }
      },
      step: {
        condition: {
          none: [],
          standard: ['*'],
          business: ['*'],
          community: ['*']
        },
        required_data: {
          none: [],
          standard: ['*'],
          business: ['*'],
          community: ['*']
        },
        permitted_params: {
          none: [],
          standard: ['*'],
          business: ['*'],
          community: ['*']
        }
      },
      field: {
        condition: {
          none: [],
          standard: ['*'],
          business: ['*'],
          community: ['*']
        },
        type: {
          none: ['text', 'textarea', 'text_only', 'date', 'time', 'date_time', 'number', 'checkbox', 'dropdown', 'upload'],
          standard: ['*'],
          business: ['*'],
          community: ['*']
        },
        realtime_validations: {
          none: [],
          standard: ['*'],
          business: ['*'],
          community: ['*']
        }
      },
      action: {
        type: {
          none: ['create_topic', 'update_profile', 'open_composer', 'route_to'],
          standard: ['create_topic', 'update_profile', 'open_composer', 'route_to', 'send_message', 'watch_categories', 'watch_tags', 'add_to_group'],
          business: ['*'],
          community: ['*']
        }
      },
      custom_field: {
        klass: {
          none: ['topic', 'post'],
          standard: ['topic', 'post'],
          business: ['*'],
          community: ['*']
        },
        type: {
          none: ['string', 'boolean', 'integer'],
          standard: ['string', 'boolean', 'integer'],
          business: ['*'],
          community: ['*']
        }
      },
      api: {
        all: {
          none: [],
          standard: [],
          business: ['*'],
          community: ['*']
        }
      }
    }
  end

  attr_accessor :product_id,
                :product_slug

  def initialize(update = false)
    if update
      ::DiscourseSubscriptionClient::Subscriptions.update
    end

    result = ::DiscourseSubscriptionClient.find_subscriptions("discourse-custom-wizard")

    if result&.any?
      ids_and_slugs = result.subscriptions.map do |subscription|
        {
          id: subscription.product_id,
          slug: result.products[subscription.product_id]
        }
      end

      id_and_slug = ids_and_slugs.sort do |a, b|
        PRODUCT_HIERARCHY.index(b[:slug]) - PRODUCT_HIERARCHY.index(a[:slug])
      end.first

      @product_id = id_and_slug[:id]
      @product_slug = id_and_slug[:slug]
    end

    @product_slug ||= ENV["CUSTOM_WIZARD_PRODUCT_SLUG"]
  end

  def includes?(feature, attribute, value = nil)
    attributes = self.class.attributes[feature]

    ## Attribute is not part of a subscription
    return true unless attributes.present? && attributes.key?(attribute)

    values = attributes[attribute][type]

    ## Subscription type does not support the attribute.
    return false if values.blank?

    ## Value is an exception for the subscription type
    if (exceptions = get_exceptions(values)).any?
      value = mapped_output(value) if CustomWizard::Mapper.mapped_value?(value)
      value = [*value].map(&:to_s)
      return false if (exceptions & value).length > 0
    end

    ## Subscription type supports all values of the attribute.
    return true if values.include?("*")

    ## Subscription type supports some values of the attributes.
    values.include?(value)
  end

  def type
    return :none unless subscribed?
    return :business if business?
    return :standard if standard?
    :community if community?
  end

  def subscribed?
    standard? || business? || community?
  end

  def standard?
    product_slug === "standard"
  end

  def business?
    product_slug === "business"
  end

  def community?
    product_slug === "community"
  end

  # TODO candidate for removal once code that depends on it externally is no longer used.
  def self.client_installed?
    defined?(DiscourseSubscriptionClient) == 'constant' && DiscourseSubscriptionClient.class == Module
  end

  def self.subscribed?
    new.subscribed?
  end

  def self.business?
    new.business?
  end

  def self.community?
    new.community?
  end

  def self.standard?
    new.standard?
  end

  def self.type
    new.type
  end

  def self.includes?(feature, attribute, value)
    new.includes?(feature, attribute, value)
  end

  protected

  def get_exceptions(values)
    values.reduce([]) do |result, value|
      result << value.split("!").last if value.start_with?("!")
      result
    end
  end

  def mapped_output(value)
    value.reduce([]) do |result, v|
      ## We can only validate mapped assignment values at the moment
      result << v["output"] if v.is_a?(Hash) && v["type"] === "assignment"
      result
    end.flatten
  end
end