# frozen_string_literal: true

module Infoboxer
  class MediaWiki
    # DSL for defining "traits" for some site.
    #
    # More docs (and possible refactoring) to follow.
    #
    # You can look at current
    # [English Wikipedia traits](https://github.com/molybdenum-99/infoboxer/blob/master/lib/infoboxer/definitions/en.wikipedia.org.rb)
    # definitions in Infoboxer's repo.
    class Traits
      class << self
        # Define set of templates for current site's traits.
        #
        # See {Templates::Set} for longer (yet insufficient) explanation.
        #
        # Expected to be used inside Traits definition block.
        def templates(&definition)
          @templates ||= Templates::Set.new

          return @templates unless definition

          @templates.define(&definition)
        end

        # @private
        def domain(d)
          # NB: explicitly store all domains in base Traits class
          Traits.domains.key?(d) and
            fail(ArgumentError, "Domain binding redefinition: #{Traits.domains[d]}")

          Traits.domains[d] = self
        end

        # @private
        def get(domain, site_info = {})
          (Traits.domains[domain] || Traits).new(site_info)
        end

        # @private
        def domains
          @domains ||= {}
        end

        # Define traits for some  domain. Use it like:
        #
        # ```ruby
        # MediaWiki::Traits.for 'ru.wikipedia.org' do
        #   templates do
        #     template '...' do
        #       # some template definition
        #     end
        #   end
        # end
        # ```
        #
        # Again, you can look at current
        # [English Wikipedia traits](https://github.com/molybdenum-99/infoboxer/blob/master/lib/infoboxer/definitions/en.wikipedia.org.rb)
        # for example implementation.
        def for(domain, &block)
          Traits.domains[domain]&.instance_eval(&block) ||
            Class.new(self, &block).domain(domain)
        end

        # @private
        alias_method :default, :new
      end

      def initialize(site_info = {})
        @site_info = site_info
      end

      def namespace?(prefix)
        known_namespaces.include?(prefix)
      end

      def interwiki?(prefix)
        known_interwikis.key?(prefix)
      end

      # @private
      def file_namespace
        @file_namespace ||= ns_aliases('File')
      end

      # @private
      def category_namespace
        @category_namespace ||= ns_aliases('Category')
      end

      # @private
      def templates
        self.class.templates
      end

      private

      def known_namespaces
        @known_namespaces ||=
          if @site_info.empty?
            STANDARD_NAMESPACES
          else
            (@site_info['namespaces'].values + @site_info['namespacealiases']).map { |n| n['*'] }
          end
      end

      def known_interwikis
        @known_interwikis ||=
          if @site_info.empty?
            {}
          else
            @site_info['interwikimap'].map { |iw| [iw['prefix'], iw] }.to_h
          end
      end

      def ns_aliases(base)
        return [base] if @site_info.empty?

        main = @site_info['namespaces'].values.detect { |n| n['canonical'] == base }
        [base, main['*']] +
          @site_info['namespacealiases']
          .select { |a| a['id'] == main['id'] }.flat_map { |n| n['*'] }
          .compact.uniq
      end

      # See https://www.mediawiki.org/wiki/Help:Namespaces#Standard_namespaces
      STANDARD_NAMESPACES = [
        'Media',            # Direct linking to media files.
        'Special',          # Special (non-editable) pages.
        '',                 # (Main)
        'Talk',             # Article discussion.
        'User',             #
        'User talk',        #
        'Project',          # Meta-discussions related to the operation and development of the wiki.
        'Project talk',     #
        'File',             # Metadata for images, videos, sound files and other media.
        'File talk',        #
        'MediaWiki',        # System messages and other important content.
        'MediaWiki talk',   #
        'Template',         # Templates: blocks of text or wikicode that are intended to be transcluded.
        'Template talk',    #
        'Help',             # Help files, instructions and "how-to" guides.
        'Help talk',        #
        'Category',         # Categories: dynamic lists of other pages.
        'Category talk',    #
      ].freeze
    end
  end
end
