#
# Copyright:: Copyright (c) 2016 GitLab Inc.
# License:: Apache License, Version 2.0
#
# 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_relative './redis_uri'
require_relative '../../package/libraries/helpers/redis_helper'

module GitlabWorkhorse
  class << self
    def parse_variables
      Gitlab['gitlab_workhorse']['auth_socket'] = nil if !auth_socket_specified? && auth_backend_specified?

      user_listen_addr = Gitlab['gitlab_workhorse']['listen_addr']
      Gitlab['gitlab_workhorse']['sockets_directory'] ||= '/var/opt/gitlab/gitlab-workhorse/sockets' if user_listen_addr.nil?

      sockets_dir = Gitlab['gitlab_workhorse']['sockets_directory']

      default_network = Gitlab['node']['gitlab']['gitlab_workhorse']['listen_network']
      user_network = Gitlab['gitlab_workhorse']['listen_network']
      network = user_network || default_network

      Gitlab['gitlab_workhorse']['listen_addr'] ||= File.join(sockets_dir, 'socket') if network == "unix"

      parse_redis_settings
    end

    def parse_secrets
      # gitlab-workhorse expects exactly 32 bytes, encoded with base64
      Gitlab['gitlab_workhorse']['secret_token'] ||= SecureRandom.base64(32)
    end

    def parse_redis_settings
      gitlab_workhorse_redis_configured = Gitlab['gitlab_workhorse'].key?('redis_socket') ||
        Gitlab['gitlab_workhorse'].key?('redis_host')

      rails_workhorse_redis_configured =
        Gitlab['gitlab_rails']['redis_workhorse_instance'] ||
        (Gitlab['gitlab_rails']['redis_workhorse_sentinels'] &&
         !Gitlab['gitlab_rails']['redis_workhorse_sentinels'].empty?)

      if gitlab_workhorse_redis_configured
        # Parse settings from `redis['master_*']` first.
        parse_redis_master_settings
        # If gitlab_workhorse settings are specified, populate
        # gitlab_rails['redis_workhorse_*'] settings from it.
        update_separate_redis_instance_settings
      elsif rails_workhorse_redis_configured
        # If user has specified a separate Redis host for Workhorse via
        # `gitlab_rails['redis_workhorse_*']` settings, copy them to
        # `gitlab_workhorse['redis_*']`.
        parse_separate_redis_instance_settings
        parse_redis_master_settings
      else
        # If user hasn't specified any separate Redis settings for Workhorse,
        # copy the global settings from GitLab Rails
        parse_global_rails_redis_settings
        parse_redis_master_settings
      end
    end

    # rubocop:disable Metrics/CyclomaticComplexity
    # rubocop:disable Metrics/AbcSize
    # rubocop:disable Metrics/PerceivedComplexity
    def update_separate_redis_instance_settings
      if Gitlab['gitlab_workhorse']['redis_host']
        uri_from_workhorse = RedisHelper.build_redis_url(
          ssl: Gitlab['gitlab_workhorse']['redis_ssl'] || Gitlab['node']['gitlab']['gitlab_workhorse']['redis_ssl'],
          host: Gitlab['gitlab_workhorse']['redis_host'] || Gitlab['node']['gitlab']['gitlab_workhorse']['redis_host'],
          port: Gitlab['gitlab_workhorse']['redis_port'] || Gitlab['node']['gitlab']['gitlab_workhorse']['redis_port'],
          password: Gitlab['gitlab_workhorse']['redis_password'] || Gitlab['node']['gitlab']['gitlab_workhorse']['redis_password'],
          path: Gitlab['gitlab_workhorse']['redis_database'] || Gitlab['node']['gitlab']['gitlab_workhorse']['redis_database']
        ).to_s

        uri_from_rails = RedisHelper.build_redis_url(
          ssl: Gitlab['gitlab_rails']['redis_ssl'] || Gitlab['node']['gitlab']['gitlab_rails']['redis_ssl'],
          host: Gitlab['gitlab_rails']['redis_host'] || Gitlab['node']['gitlab']['gitlab_rails']['redis_host'],
          port: Gitlab['gitlab_rails']['redis_port'] || Gitlab['node']['gitlab']['gitlab_rails']['redis_port'],
          password: Gitlab['gitlab_rails']['redis_password'] || Gitlab['node']['gitlab']['gitlab_rails']['redis_password'],
          path: Gitlab['gitlab_rails']['redis_database'] || Gitlab['node']['gitlab']['gitlab_rails']['redis_database']
        ).to_s
        Gitlab['gitlab_rails']['redis_workhorse_instance'] = uri_from_workhorse if uri_from_workhorse != uri_from_rails

      else
        workhorse_redis_socket = Gitlab['gitlab_workhorse']['redis_socket'] || Gitlab['node']['gitlab']['gitlab_workhorse']['redis_socket']
        rails_redis_socket = Gitlab['gitlab_rails']['redis_socket'] || Gitlab['node']['gitlab']['gitlab_rails']['redis_socket']
        Gitlab['gitlab_rails']['redis_workhorse_instance'] = "unix://#{workhorse_redis_socket}" if workhorse_redis_socket != rails_redis_socket
      end

      %w[username password cluster_nodes sentinels sentinel_master sentinels_password].each do |setting|
        Gitlab['gitlab_rails']["redis_workhorse_#{setting}"] ||= Gitlab['gitlab_workhorse']["redis_#{setting}"]
      end
    end
    # rubocop:enable Metrics/CyclomaticComplexity
    # rubocop:enable Metrics/AbcSize
    # rubocop:enable Metrics/PerceivedComplexity

    def parse_global_rails_redis_settings
      %w[ssl host socket port password database sentinels sentinels_password].each do |setting|
        Gitlab['gitlab_workhorse']["redis_#{setting}"] ||= Gitlab['gitlab_rails']["redis_#{setting}"]
      end
    end

    def parse_separate_redis_instance_settings
      # If an individual Redis instance is specified for Workhorse, figure out
      # host, port, password, etc. from it
      if Gitlab['gitlab_rails']['redis_workhorse_instance']
        uri = URI(Gitlab['gitlab_rails']['redis_workhorse_instance'])

        Gitlab['gitlab_workhorse']['redis_ssl'] = uri.scheme == 'rediss' unless Gitlab['gitlab_workhorse'].key?('redis_ssl')
        if uri.scheme == 'unix'
          Gitlab['gitlab_workhorse']['redis_socket'] = uri.path
        else
          Gitlab['gitlab_workhorse']['redis_host'] ||= if uri.path.start_with?('/')
                                                         uri.host
                                                       else
                                                         uri.path
                                                       end
          Gitlab['gitlab_workhorse']['redis_port'] ||= uri.port
          Gitlab['gitlab_workhorse']['redis_password'] ||= uri.password
          Gitlab['gitlab_workhorse']['redis_database'] ||= uri.path.delete_prefix('/') if uri.path.start_with?('/')
        end
      end

      %w[username password cluster_nodes sentinels sentinel_master sentinels_password].each do |setting|
        Gitlab['gitlab_workhorse']["redis_#{setting}"] ||= Gitlab['gitlab_rails']["redis_workhorse_#{setting}"]
      end
    end

    def parse_redis_master_settings
      # TODO: When GitLab rails gets it's own set if `redis_sentinel_master_*`
      # settings, update the following to use them instead of
      # `Gitlab['redis'][*]` settings. It can be then merged with
      # `parse_rails_redis_settings` method
      Gitlab['gitlab_workhorse']['redis_sentinel_master'] ||= Gitlab['redis']['master_name'] || Gitlab[:node]['redis']['master_name']
      Gitlab['gitlab_workhorse']['redis_sentinel_master_ip'] ||= Gitlab['redis']['master_ip'] || Gitlab[:node]['redis']['master_ip']
      Gitlab['gitlab_workhorse']['redis_sentinel_master_port'] ||= Gitlab['redis']['master_port'] || Gitlab[:node]['redis']['master_port']
      Gitlab['gitlab_workhorse']['redis_password'] ||= Gitlab['redis']['master_password'] || Gitlab[:node]['redis']['master_password']
    end

    private

    def auth_socket_specified?
      auth_socket = Gitlab['gitlab_workhorse']['auth_socket']

      !auth_socket&.empty?
    end

    def auth_backend_specified?
      auth_backend = Gitlab['gitlab_workhorse']['auth_backend']

      !auth_backend&.empty?
    end
  end
end
