class Node < ApplicationRecord
  MENUS = %i[]
  COOKIE_POLICY = :cookie_policy
  SETTINGS = MENUS << COOKIE_POLICY

  include AncestryWithSortedUrl
  include HasAttachments
  include HasTags

  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 }

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

  enum :template, {
    start: 0,
    facts: 1,
    intro: 2,
    stage: 3,
    good_answer: 4,
    bad_answer: 5,
    chance: 6,
    last_save: 7,
    results: 8
  }

  def available_templates
    return [ :start ] if root?

    case depth
    when 1
      [ :facts, :intro, :stage, :last_save, :results ]
    when 2
      [ :good_answer, :bad_answer, :chance ]
    when 3
      [ :good_answer, :bad_answer ]
    else
      []
    end
  end

  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
  before_validation :normalize_translations

  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}' DESC, id DESC": "title ->>'#{I18n.locale}' 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


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


  def normalize_translations
    %w[title slug].each do |attr|
      value = read_attribute(attr)
      if value.is_a?(Hash)
        write_attribute(attr, value.transform_values { |v| v.is_a?(String) ? v.strip : v })
      end
    end
  end
end
