class Node < ApplicationRecord

  MENUS = %i""
  COOKIE_POLICY = :cookie_policy
  SETTINGS = MENUS << COOKIE_POLICY

  include AncestryWithSortedUrl
  include HasAttachments
  include HasTags

  has_many :answers, dependent: :destroy

  extend Mobility
  translates :slug,
             :tags,
             locale_accessors: I18n.available_locales,
             fallbacks: {zh: :en, hr: :en, cs: :en, da: :en, nl: :en, fi: :en, fr: :en, fr_ca: :en, de: :en, hu: :en, it: :en, ja: :en, ko: :en, nb: :en, pl: :en, pt: :en, ro: :en, sr: :en, sk: :en, sl: :en, es: :en, sv: :en, uk: :en}

  translates :title,
             :url,
             :href,
             :page_title,
             :page_description,
             locale_accessors: I18n.available_locales,
             fallbacks: {zh: :en, hr: :en, cs: :en, da: :en, nl: :en, fi: :en, fr: :en, fr_ca: :en, de: :en, hu: :en, it: :en, ja: :en, ko: :en, nb: :en, pl: :en, pt: :en, ro: :en, sr: :en, sk: :en, sl: :en, es: :en, sv: :en, uk: :en}


  NODE_TEMPLATES = %w"tmpl_article tmpl_index tmpl_list"

  enum :status, { status_published: 0, status_draft: 1, status_archived: 2 }

  enum :template, { tmpl_index: 0,
                   tmpl_article: 1,
                   tmpl_list: 2
                  }

  include PgSearch::Model
  pg_search_scope :pg_search,
                  against: {title: 'A', url: 'B', page_title: 'A', page_description: 'B', href: 'B', slug: 'B' },
                  associated_against: {
                    attachments: [:body, :url]
                  }

  before_validation :remove_empty_tags

  validates_presence_of :title

  validates :expires_at,
            date: { after: :published_at },
            allow_nil: true

  scope :ordered, -> { order(position: :asc) }
  scope :by_title, ->(rev) { order(Arel.sql(rev ? "title->>'#{I18n.locale.to_s}' DESC, id DESC": "title ->>'#{I18n.locale.to_s}' ASC, id ASC")) }
  scope :by_status, ->(rev) { order(status: rev ? :desc : :asc, id: rev ? :desc : :asc) }
  scope :by_slug, ->(rev) { order(ancestry: rev ? :desc : :asc, position: rev ? :desc : :asc, id: rev ? :desc : :asc) }
  scope :by_last_modified, ->(rev) { order(updated_at: rev ? :asc : :desc, id: rev ? :desc : :asc) }

  # scope :not_excluded, -> { where "NOT(? = ANY (excluded_locales))", I18n.locale.to_s }

  scope :for_current_locale, -> {
    where("(cardinality(excluded_locales) = 0 AND is_allowlist = false) OR
    (is_allowlist = true AND ? = ANY(excluded_locales)) OR
    (is_allowlist = false AND NOT(? = ANY(excluded_locales)))",
    I18n.locale.to_s, I18n.locale.to_s )
  }

  scope :viewable, -> { status_published.for_current_locale.where('published_at <= ? AND (expires_at IS NULL OR expires_at > ?)', Time.current, Time.current) }
  scope :of_template, ->(tmpl) { where(template: Node.templates[tmpl.to_s]) }

  scope :with_setting, ->(setting) { setting.kind_of?(Array) ? where("settings && ?", "{#{setting.join(',')}}") : where("settings @> ?", "{#{setting}}") }

  scope :simple_search, ->(q) { pg_search(q) unless q.blank? }

  scope :tiles, -> { where("ancestry LIKE '/?/%'", Node.where(position: 2, ancestry_depth: 0).first) }

  def self.tags
    result = {}
    I18n.available_locales.each do |locale|
      key = locale.to_s.downcase.sub('-','_')
      result[key] = pluck(Arel.sql("tags->'#{locale}'")).flatten.compact.uniq.sort.reject{|v| v.blank?}
    end
    result
  end


  def self.categories
    ['document']
  end


  def category
    self.children.any? ? 'folder' : 'document'
  end


  def document?
    self.category == 'document'
  end


  def href_or_url
    href.present?? href : url
  end


  def viewable?
    Node.viewable.where(id: self.path_ids).count == self.path_ids.length
  end


  def index?
    self.attachments.blank?
  end


  def site?
    (self.root || self).position == 1
  end


  def tile?
    (self.root || self).position == 2
  end


  def node_type
    return "site" if self.site?
    'tile'
  end


  def answer_percentages
    # Get all valid answers (non-nil values)
    valid_answers = answers.where.not(value: nil)

    # Count answers by value
    value_counts = valid_answers.group(:value).count

    # Calculate total number of answers
    total_answers = valid_answers.count

    # Initialize with 0 for both expected values
    percentages = {0 => 0, 1 => 0}

    # Return initial percentages if no answers
    return percentages if total_answers.zero?

    # Calculate exact percentages first (not rounded)
    exact_percentages = {}
    value_counts.each do |value, count|
      # Only consider values 0 and 1
      next unless [0, 1].include?(value)
      exact_percentages[value] = count.to_f / total_answers * 100
    end

    # First round down all percentages
    [0, 1].each do |value|
      percentages[value] = (exact_percentages[value] || 0).floor
    end

    # Calculate how much we're off by due to rounding
    remaining = 100 - percentages.values.sum

    # Distribute the remaining percentage points to values with the largest fractional parts
    if remaining > 0
      # Filter exact percentages to only include 0 and 1
      sorted_by_fraction = exact_percentages
        .select { |k, _| [0, 1].include?(k) }
        .sort_by { |_, p| p - p.floor }
        .reverse

      # Add 1 to the values with the largest fractional parts
      sorted_by_fraction.take(remaining).each do |value, _|
        percentages[value] += 1
      end
    end

    # Return the percentages hash with keys 0 and 1
    percentages
   end

private

  def remove_empty_tags
    %w"settings excluded_locales tags_en tags_zh tags_hr tags_cs tags_da tags_nl tags_fi tags_fr tags_fr_ca tags_de tags_hu tags_it tags_ja tags_ko tags_nb tags_pl tags_pt tags_ro tags_sr tags_sk tags_sl tags_es tags_sv tags_uk ".map do |k|
      self.send "#{k}=", self.send(k).reject { |v| v.blank? } unless self.send(k).blank?
    end
  end

end
