# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require 'htmlentities'
require 'algoliasearch'
require 'slugize'
require 'dotenv'
require 'securerandom'

module Jekyll
  class GeneratorAlgolia < Generator
    # Do this last so everything else has been processed already.
    priority :lowest

    def initialize(_config)
      Dotenv.load
    end

    def generate(site)
      @site = site
      return unless check_config?
      @prefix = site.config['algolia_prefix'] || ''
      @random_code = random_code
      Algolia.init(application_id: site.config['algolia_app_id'],
                   api_key: site.config['algolia_api_key'])
      @indexes = setup_indexes
      generate_all
    end

    private

    def check_config?
      if @site.config['algolia_app_id'].nil? || @site.config['algolia_app_id'].empty?
        Jekyll.logger.warn(
          'Config Warning:',
          'You did not provide a ALGOLIA_APP_ID environment variable.'
        )
        return false
      end
      if @site.config['algolia_api_key'].nil? || @site.config['algolia_api_key'].empty?
        Jekyll.logger.warn(
          'Config Warning:',
          'You did not provide a ALGOLIA_API_KEY environment variable.'
        )
        return false
      end
      true
    end

    def generate_all
      generate_blog_posts
      generate_guides
      generate_documentation
      generate_none_guide_guides
      generate_other
    end

    def random_code
      SecureRandom.hex
    end

    def setup_indexes
      indexes = {}
      @site.data['search_indexes'].each do |name, properties|
        index = Algolia::Index.new(@prefix + name)
        unless properties['settings'].nil?
          index.set_settings(properties['settings'])
        end
        indexes[name] = index
      end
      indexes
    end

    def generate_documentation
      return if @site.data['docs'].nil?

      documents = @site.data['docs'][:symbols].map do |item|
        next if item[:language] == 'c_preview'

        if item[:summary].nil? || item[:summary].strip.length == 0
          Jekyll.logger.warn(
            'Search Warning:',
            "There was no summary for the symbol '#{item[:name]}' in #{item[:language]}."
          )
        end
        {
          'objectID'   => item[:url],
          'title'      => item[:name],
          'splitTitle' => item[:name].split(/(?=[A-Z])/).join(' '),
          'url'        => item[:url],
          'summary'    => item[:summary],
          'kind'       => item[:kind],
          'language'   => item[:language],
          'type'       => 'documentation',
          'ranking'    => doc_language_rank[item[:language]] * 1000,
          'randomCode' => @random_code
        }
      end.compact
      @indexes['documentation'].save_objects(documents)
    end

    def generate_blog_posts
      documents = []
      @site.posts.docs.each do | post |
        # Calculate the age of the post so we can prioritise newer posts
        # over older ones.
        # NOTE: post.date is actually a Time object, despite its name
        age = (Time.now - post.date).round
        author = post.data['author']

        post.get_sections.each do | section |
          # Ignore sections without any contents.
          if section[:contents].strip.size == 0
            next
          end

          if section[:title].nil?
            url = post.url
          else
            url = post.url + '#' + section[:title].slugize
          end

          document = {
            'objectID'     => url,
            'title'        => post.data['title'],
            'sectionTitle' => section[:title],
            'url'          => url,
            'urlDisplay'   => post.url,
            'author'       => author,
            'content'      => HTMLEntities.new.decode(section[:contents]),
            'posted'       => post.date,
            'age'          => age,
            'type'         => 'blog post',
            'randomCode'   => @random_code
          }
          documents << document
        end
      end
      @indexes['blog-posts'].save_objects(documents)
    end

    def generate_guides
      documents = []
      return if @site.collections['guides'].nil?

      @site.collections['guides'].docs.each do | guide |
        group = @site.data['guides'][guide.data['guide_group']]
        unless group.nil? || group['subgroups'].nil? || guide.data['guide_subgroup'].nil?
          subgroup = group.nil? ? '' : group['subgroups'][guide.data['guide_subgroup']]
        end

        guide.get_sections.each do | section |
          url = guide.url
          unless section[:title].nil?
            url = url + '#' + section[:title].slugize
          end

          document = {
            'objectID'     => url,
            'title'        => guide.data['title'],
            'sectionTitle' => section[:title],
            'url'          => url,
            'urlDisplay'   => guide.url,
            'content'      => HTMLEntities.new.decode(section[:contents]),
            'group'        => group.nil? ? '' : group['title'],
            'subgroup'     => subgroup.nil? ? '' : subgroup['title'],
            'type'         => 'guide',
            'randomCode'   => @random_code
          }
          documents << document
        end
      end

      @indexes['guides'].save_objects(documents)
    end

    def generate_none_guide_guides
      documents = []
      gs_pages = @site.pages.select { |page| page.data['search_index'] }
      gs_pages.each do |page|
        page.get_sections.each do |section|
          url = page.url
          url = url + '#' + section[:title].slugize unless section[:title].nil?
          document = {
            'objectID'     => url,
            'title'        => page.data['title'],
            'sectionTitle' => section[:title],
            'url'          => url,
            'urlDisplay'   => page.url,
            'content'      => HTMLEntities.new.decode(section[:contents]),
            'group'        => page.data['search_group'],
            'subgroup'     => page.data['sub_group'],
            'type'         => 'not-guide',
            'randomCode'   => @random_code
          }
          documents << document
        end
      end
      @indexes['guides'].save_objects(documents)
    end

    def generate_other
      documents = @site.data['search-other'].map do |other|
        {
            'objectID' => other['id'],
            'title' => other['title'],
            'url' => other['url'],
            'content' => other['description'],
            'randomCode' => @random_code
        }
      end
      @indexes['other'].save_objects(documents)
    end

    def doc_language_rank
      {
          'c' => 10,
          'rockyjs' => 9,
          'pebblekit_js' => 8,
          'pebblekit_android' => 6,
          'pebblekit_ios' => 4
      }
    end
  end
end
