# frozen_string_literal: true

module Nanoc::Helpers
  module Generic
    #
    # Check if NANOC_ENV is set to production
    #
    def production?
      ENV['NANOC_ENV'] == 'production'
    end

    #
    # Check if NANOC_ENV is set to production and the branch is the default one.
    # For things that should only be built in production of the default branch.
    # Sometimes we don't want things to be deployed into the stable branches,
    # which they are considered production.
    #
    def production_and_default_branch?
      ENV['NANOC_ENV'] == 'production' && ENV['CI_DEFAULT_BRANCH'] == ENV['CI_COMMIT_REF_NAME']
    end

    #
    # Find the current branch. If CI_COMMIT_BRANCH is not defined, that means
    # we're working locally, and Git is used to find the branch.
    #
    def current_branch
      if ENV['CI_COMMIT_REF_NAME'].nil?
        `git branch --show-current`.tr("\n", '')
      else
        ENV['CI_COMMIT_REF_NAME']
      end
    end

    #
    # Check if CI_PROJECT_NAME is 'gitlab-docs', or nil which implies
    # local development. This can be used to skip portions that we
    # don't want to render in one of the upstream products.
    #
    def gitlab_docs_or_local?
      ENV['CI_PROJECT_NAME'] == 'gitlab-docs' || ENV['CI_PROJECT_NAME'].nil?
    end

    #
    # Control display of survey banner. See README.md#survey-banner
    #
    def show_banner?
      @items['/_data/banner.yaml'][:show_banner]
    end

    #
    # Returns global nav sections.
    #
    def get_nav_sections
      @items['/_data/navigation.yaml'][:sections]
    end

    #
    # Get the top-level section for a page, based on title and menu placement.
    #
    # This function temporarily serves a dual purpose in the migration from
    # Google Programmable Search to Elasticsearch:
    #
    # 1. It returns a human-readable string for use in the "gitlab-docs-section" metatag,
    #    which is used for Google search results.
    #
    # 2. When the 'machine' parameter is set to true, it returns a machine-readable string
    #    for use in the "gitlab_docs_section" metatag, which is used for Elasticsearch indexing.
    #
    # @param title [String] The title of the page
    # @param path [String] The path of the page
    # @param machine [Boolean] Whether to return a machine-readable string (default: false)
    #
    # @return [String] The section name in either human-readable or machine-readable format,
    #                  or "none" if no section is found
    #
    def docs_section(title, path, machine_readable = false)
      section = find_section(title, path) || "none"
      machine_readable ? machine_friendly(section) : section
    end

    def find_section(title, path)
      return "Tutorials" if title.start_with?("Tutorial:")

      path = path[1..] # remove the leading slash

      get_nav_sections.each do |section|
        section_title = section[:section_title]
        return section_title if section[:section_url] == path

        section.fetch(:section_categories, []).each do |category|
          return section_title if category[:category_url] == path
          next unless category[:docs]
          return section_title if section_exists?(category[:docs], path)
        end
      end
      "none"
    end

    def machine_friendly(section)
      return "none" if section == "none"

      section.downcase.gsub(%r{[^a-z0-9]+}, '_').gsub(%r{^_|_$}, '')
    end

    def section_exists?(docs, path)
      docs.each do |doc|
        return true if doc[:doc_url] == path

        sub_docs = doc[:docs]
        next unless sub_docs

        return true if section_exists?(sub_docs, path)
      end

      false
    end

    #
    # Generate a breadcrumb trail for a page, based on the global nav.
    #
    # Returns an array structured to fit the schema.org breadcrumbList spec.
    # See https://schema.org/BreadcrumbList
    #
    def build_breadcrumb_list(path)
      breadcrumb_list = []

      data = get_nav_sections
      crumbs = breadcrumb_trail(data, path[1..]) # remove the leading slash

      return nil if crumbs.empty?

      crumbs.each_with_index do |crumb, index|
        structured_crumb = {
          :@type => "ListItem",
          :position => index + 1,
          :name => crumb[:name]
        }

        structured_crumb[:item] = "https://docs.gitlab.com/#{crumb[:item]}" if crumb[:item] && index < crumbs.length - 1
        breadcrumb_list << structured_crumb
      end

      return nil if breadcrumb_list.empty?

      {
        '@context': "https://schema.org",
        '@type': "BreadcrumbList",
        itemListElement: breadcrumb_list
      }
    end

    #
    # Traverse the menu and return an array of breadcrumbs for a given item.
    #
    # This is used to fill in the itemListElement property in the
    # BreadcrumbList JSON-LD object, which is included in the head of each page.
    #
    def breadcrumb_trail(data, path)
      return [] if data.empty?

      data.each do |item|
        # 1st level items
        if item[:section_url] == path
          return [{ name: item[:section_title], item: item[:section_url] }]

        # 2nd level items
        elsif item.key?(:section_categories)
          result = breadcrumb_trail(item[:section_categories], path)
          next if result.empty?

          return [{ name: item[:section_title], item: item[:section_url] }] + result

        # 3rd level items
        elsif item.key?(:category_url) && item[:category_url] == path
          return [{ name: item[:category_title], item: item[:category_url] }]

        # 4th-6th level items
        elsif item.key?(:docs)
          result = breadcrumb_trail_docs(item[:docs], path)
          next if result.empty?

          return [{ name: item[:category_title], item: item[:category_url] }] + result
        end
      end

      []
    end

    #
    # Builds a breadcrumb trail for 4th-6th level menu items.
    #
    # We can use a recursive method for these since they use the same
    # property names at each level.
    #
    def breadcrumb_trail_docs(docs, path)
      return [] if docs.empty?

      docs.each do |doc|
        if doc[:doc_url] == path
          return [{ name: doc[:doc_title], item: doc[:doc_url] }]
        elsif doc.key?(:docs)
          result = breadcrumb_trail_docs(doc[:docs], path)
          next if result.empty?

          return [{ name: doc[:doc_title], item: doc[:doc_url] }] + result
        end
      end

      []
    end

    #
    # Return the breadcrumb trail in string format.
    #
    # This is set in a metatag and then used for
    # search results on the site.
    #
    # The Google Programmable Search JSON API does not
    # include the JSON-LD breadcrumbList schema in the
    # response, but it does include metatag content.
    #
    def docs_breadcrumb_list(path)
      data = get_nav_sections
      list = breadcrumb_trail(data, path[1..])
      list.map { |item| item[:name] }.join(" &rsaquo; ")
    end

    #
    # Fetch information about GitLab releases.
    #
    def get_release_dates
      uri = URI('https://gitlab.com/gitlab-com/www-gitlab-com/-/raw/master/data/releases.yml')
      response = Net::HTTP.get_response(uri)
      return "[]" unless response.is_a?(Net::HTTPSuccess)

      parsed_yaml = YAML.safe_load(response.body) || []
      JSON.generate(parsed_yaml)
    rescue StandardError => e
      warn("Error getting release dates - #{e}")
      "[]"
    end

    # This method will check whether gitlab product analytics is
    # enabled or not based on env variables
    #
    def gitlab_analytics_enabled?
      !ENV.fetch('GITLAB_ANALYTICS_HOST', '').empty? &&
        !ENV.fetch('GITLAB_ANALYTICS_ID', '').empty?
    end

    #
    # This method will return configuration object
    # when gitlab product analytics is enabled.
    #
    def gitlab_analytics_json
      return unless gitlab_analytics_enabled?

      {
        'appId' => ENV.fetch('GITLAB_ANALYTICS_ID', nil),
        'host' => ENV.fetch('GITLAB_ANALYTICS_HOST', nil),
        'hasCookieConsent' => true
      }.to_json
    end
  end
end
