class UserDecorator < ApplicationDecorator
  WHITE_TEXT_COLORS = [
    {
      bg: "#093656",
      text: "#ffffff"
    },
    {
      bg: "#61122f",
      text: "#ffffff"
    },
    {
      bg: "#2e0338",
      text: "#ffffff"
    },
    {
      bg: "#080E3B",
      text: "#ffffff"
    },
  ].freeze

  DEFAULT_PROFILE_SUMMARY = -> { I18n.t("stories_controller.404_bio_not_found") }

  # The relevant attribute names for cached tags.  These are the attributes that we'll make
  # available in the front-end.  The list comes from the two places (see below for that list).
  #
  # @see app/controllers/async_info_controller.rb
  # @see app/services/articles/feeds/article_score_calculator_for_user.rb
  CACHED_TAGGED_BY_USER_ATTRIBUTES = %i[bg_color_hex hotness_score id name points text_color_hex].freeze

  # A proxy for a Tag object.  In app/services/articles/feeds/article_score_calculator_for_user.rb
  # we rely on method calls to the object.  (e.g. "tag.name").  This class helps us conform to that
  # expectation.
  #
  # @note A Struct in Rails can be cast "to_json" and uses its attributes.
  #
  # @see https://github.com/rails/rails/blob/main/activesupport/lib/active_support/core_ext/object/json.rb#L68-L72
  CachedTagByUser = Struct.new(*CACHED_TAGGED_BY_USER_ATTRIBUTES, keyword_init: true)

  # Return the relevant tags that the user follows and their points.
  #
  # @note We want to avoid caching ActiveRecord objects.
  #
  # @return [Array<UserDecorator::CachedTagByUser>]
  def cached_followed_tags
    cached_tag_attributes = Rails.cache.fetch(
      "#{cache_key}-#{last_followed_at&.rfc3339}/followed_tags/#{RequestStore.store[:subforem_id]}",
      expires_in: 20.hours,
    ) do
      Tag.from_subforem.followed_tags_for(follower: object).map { |tag| tag.slice(*CACHED_TAGGED_BY_USER_ATTRIBUTES) }
    end

    cached_tag_attributes.map do |cached_tag|
      CachedTagByUser.new(cached_tag)
    end
  end

  def darker_color(adjustment = 0.88)
    Color::CompareHex.new([enriched_colors[:bg], enriched_colors[:text]]).brightness(adjustment)
  end

  def enriched_colors
    if setting.brand_color1.blank?
      {
        bg: assigned_color[:bg],
        text: assigned_color[:text]
      }
    else
      {
        bg: setting.brand_color1,
        text: "#ffffff"
      }
    end
  end

  def ordered_subforems
    activity_store = user_activity
    return [] unless activity_store
    recent_subforem_ids = activity_store.recent_subforems.compact
    followed_subforem_ids = activity_store.alltime_subforems.compact
    all_subforem_ids = Subforem.cached_discoverable_ids

    scores = Hash.new(0)

    # Iterate through recent subforems and increment score by 1 for each appearance.
    recent_subforem_ids.each do |id|
      scores[id] += 1
    end
  
    # Iterate through followed subforems and increment score by 10 for each appearance.
    followed_subforem_ids.each do |id|
      scores[id] += 10
    end
  
    # Sort the subforems from user activity in descending order based on their score.
    user_activity_ids = scores.keys.sort { |a, b| scores[b] <=> scores[a] }
  
    # Get the remaining discoverable subforems that were not in the user's activity,
    # preserving their original order from all_subforem_ids.
    remaining_ids = all_subforem_ids - user_activity_ids
  
    # Concatenate the sorted user activity list with the remaining discoverable subforems.
    user_activity_ids + remaining_ids
  end


  def config_body_class
    body_class = [
      setting.config_theme.tr("_", "-"),
      "#{setting.resolved_font_name.tr('_', '-')}-article-body",
      "mod-status-#{any_admin? || !moderator_for_tags.empty?}",
      "trusted-status-#{trusted?}",
      "#{setting.config_navbar.tr('_', '-')}-header",
    ]

    cached_role_names.each do |name|
      body_class << "user-role--#{name}"
    end

    # Backfill ten-x-hacker-theme because the ios app looks for it to render native dark shell.
    body_class << "ten-x-hacker-theme" if setting.config_theme == "dark_theme"
    body_class.join(" ")
  end

  def assigned_color
    colors = [
      {
        bg: "#19063A",
        text: "#dce9f3"
      },
      {
        bg: "#0D4D4B",
        text: "#fdf9f3"
      },
      {
        bg: "#010C1F",
        text: "#aebcd5"
      },
      {
        bg: "#d7dee2",
        text: "#022235"
      },
      {
        bg: "#161616",
        text: "#66e2d5"
      },
      {
        bg: "#1c0bba",
        text: "#c9d2dd"
      },
    ]
    colors |= WHITE_TEXT_COLORS
    colors[(id || rand(100)) % 10]
  end

  # returns true if the user has been suspended and has no content
  def fully_banished?
    articles_count.zero? && comments_count.zero? && spam_or_suspended?
  end

  def considered_new?
    Settings::RateLimit.user_considered_new?(user: self)
  end

  # Returns the user's public email if it is set and the display_email_on_profile
  # settings is set to true.
  def profile_email
    return unless setting.display_email_on_profile?

    email
  end

  # Returns the users profile summary or a placeholder text
  def profile_summary
    profile&.summary.presence || DEFAULT_PROFILE_SUMMARY.call
  end

  delegate :display_sponsors, to: :setting

  delegate :display_announcements, to: :setting
end
