module Locomotive::Steam
  module Middlewares

    # Set the locale from the path if possible or use the default one
    #
    # Examples:
    #
    #   /fr/index   => locale = :fr
    #   /fr/        => locale = :fr
    #   /index      => locale = :en (default one)
    #
    #   /en/index?locale=fr => locale = :fr
    #   /index      => redirection to /en if the locale in cookie is :en
    #
    class Locale < ThreadSafe

      include Concerns::Helpers

      def _call
        env['steam.path']   = request.path_info

        env['steam.locale'] = services.locale = extract_locale

        set_locale_cookie

        log "Locale used: #{locale.upcase}"

        I18n.with_locale(locale) do
          self.next
        end
      end

      protected

      def extract_locale
        # Regarding the index page (basically, "/"), we've to see if we could
        # guess the locale from the headers the browser sends to us.
        locale = if is_index_page? && !site.bypass_browser_locale
          locale_from_params || locale_from_cookie || locale_from_header
        else
          locale_from_path || locale_from_params
        end

        # make sure, the locale is among the ones defined in the site,
        # otherwise take the default one.
        locale && locales.include?(locale) ? locale : default_locale
      end

      def locale_from_params
        params[:locale]&.to_sym.tap do |locale|
          log 'Locale extracted from the params' unless locale.blank?
        end
      end

      def locale_from_path
        path = request.path_info

        if path =~ /^\/(#{site.locales.join('|')})+(\/|$)/
          locale = $1

          # no need to keep the locale in the path used to fetch the page
          env['steam.path'] = path.gsub(/^\/#{$1 + $2}/, '/')
          env['steam.locale_in_path'] = true

          log 'Locale extracted from the path'

          locale.to_sym
        end
      end

      def locale_from_header
        request.accept_language.lazy
        .sort { |a, b| b[1] <=> a[1] }
        .map  { |lang, _| lang[0..1].to_sym }
        .find { |lang| locales.include?(lang) }.tap do |locale|
          log 'Locale extracted from the header' unless locale.blank?
        end
      end

      def locale_from_cookie
        if locale = services.cookie.get(cookie_key_name)

          log 'Locale extracted from the cookie'

          locale.to_sym
        end
      end

      def set_locale_cookie
        services.cookie.set(cookie_key_name, { 'value': locale, 'path': '/', 'max_age': 1.year })
      end

      # The preview urls for all the sites share the same domain, so cookie[:locale]
      # would be the same for all the preview urls and this is not good.
      # This is why we need to use a different key.
      def cookie_key_name
        live_editing? ? "steam-locale-#{site.handle}" : 'steam-locale'
      end

      def is_index_page?
        ['/', ''].include?(request.path_info)
      end

    end
  end
end
