# frozen_string_literal: true

require 'resolv'

class InstanceConfiguration
  SSH_ALGORITHMS = %w(DSA ECDSA ED25519 RSA).freeze
  SSH_ALGORITHMS_PATH = '/etc/ssh/'
  CACHE_KEY = 'instance_configuration'
  EXPIRATION_TIME = 24.hours

  def settings
    @configuration ||= Rails.cache.fetch(CACHE_KEY, expires_in: EXPIRATION_TIME) do
      { ssh_algorithms_hashes: ssh_algorithms_hashes,
        host: host,
        gitlab_pages: gitlab_pages,
        ci_cd_limits: ci_cd_limits,
        size_limits: size_limits,
        package_file_size_limits: package_file_size_limits,
        rate_limits: rate_limits }.deep_symbolize_keys
    end
  end

  private

  def ssh_algorithms_hashes
    SSH_ALGORITHMS.select { |algo| ssh_algorithm_enabled?(algo) }.map { |algo| ssh_algorithm_hashes(algo) }.compact
  end

  def ssh_algorithm_enabled?(algorithm)
    algorithm_key_restriction = application_settings["#{algorithm.downcase}_key_restriction"]
    algorithm_key_restriction.nil? || algorithm_key_restriction != ApplicationSetting::FORBIDDEN_KEY_VALUE
  end

  def host
    Settings.gitlab.host
  end

  def gitlab_pages
    Settings.pages.to_h.merge(ip_address: resolv_dns(Settings.pages.host))
  end

  def resolv_dns(dns)
    Resolv.getaddress(dns)
  rescue Resolv::ResolvError
  end

  def size_limits
    {
      max_attachment_size: application_settings[:max_attachment_size].megabytes,
      receive_max_input_size: application_settings[:receive_max_input_size]&.megabytes,
      max_export_size: application_settings[:max_export_size] > 0 ? application_settings[:max_export_size].megabytes : nil,
      max_import_size: application_settings[:max_import_size] > 0 ? application_settings[:max_import_size].megabytes : nil,
      diff_max_patch_bytes: application_settings[:diff_max_patch_bytes].bytes,
      max_artifacts_size: application_settings[:max_artifacts_size].megabytes,
      max_pages_size: application_settings[:max_pages_size] > 0 ? application_settings[:max_pages_size].megabytes : nil,
      snippet_size_limit: application_settings[:snippet_size_limit]&.bytes
    }
  end

  def package_file_size_limits
    Plan.all.to_h { |plan| [plan.name.capitalize, plan_file_size_limits(plan)] }
  end

  def plan_file_size_limits(plan)
    {
      conan: plan.actual_limits[:conan_max_file_size],
      helm: plan.actual_limits[:helm_max_file_size],
      maven: plan.actual_limits[:maven_max_file_size],
      npm: plan.actual_limits[:npm_max_file_size],
      nuget: plan.actual_limits[:nuget_max_file_size],
      pypi: plan.actual_limits[:pypi_max_file_size],
      terraform_module: plan.actual_limits[:terraform_module_max_file_size],
      generic: plan.actual_limits[:generic_packages_max_file_size]
    }
  end

  def rate_limits
    {
      unauthenticated: {
        enabled: application_settings[:throttle_unauthenticated_enabled],
        requests_per_period: application_settings[:throttle_unauthenticated_requests_per_period],
        period_in_seconds: application_settings[:throttle_unauthenticated_period_in_seconds]
      },
      authenticated_api: {
        enabled: application_settings[:throttle_authenticated_api_enabled],
        requests_per_period: application_settings[:throttle_authenticated_api_requests_per_period],
        period_in_seconds: application_settings[:throttle_authenticated_api_period_in_seconds]
      },
      authenticated_web: {
        enabled: application_settings[:throttle_authenticated_web_enabled],
        requests_per_period: application_settings[:throttle_authenticated_web_requests_per_period],
        period_in_seconds: application_settings[:throttle_authenticated_web_period_in_seconds]
      },
      protected_paths: {
        enabled: application_settings[:throttle_protected_paths_enabled],
        requests_per_period: application_settings[:throttle_protected_paths_requests_per_period],
        period_in_seconds: application_settings[:throttle_protected_paths_period_in_seconds]
      },
      unauthenticated_packages_api: {
        enabled: application_settings[:throttle_unauthenticated_packages_api_enabled],
        requests_per_period: application_settings[:throttle_unauthenticated_packages_api_requests_per_period],
        period_in_seconds: application_settings[:throttle_unauthenticated_packages_api_period_in_seconds]
      },
      authenticated_packages_api: {
        enabled: application_settings[:throttle_authenticated_packages_api_enabled],
        requests_per_period: application_settings[:throttle_authenticated_packages_api_requests_per_period],
        period_in_seconds: application_settings[:throttle_authenticated_packages_api_period_in_seconds]
      },
      authenticated_git_lfs_api: {
        enabled: application_settings[:throttle_authenticated_git_lfs_enabled],
        requests_per_period: application_settings[:throttle_authenticated_git_lfs_requests_per_period],
        period_in_seconds: application_settings[:throttle_authenticated_git_lfs_period_in_seconds]
      },
      issue_creation: application_setting_limit_per_minute(:issues_create_limit),
      note_creation: application_setting_limit_per_minute(:notes_create_limit),
      project_export: application_setting_limit_per_minute(:project_export_limit),
      project_export_download: application_setting_limit_per_minute(:project_download_export_limit),
      project_import: application_setting_limit_per_minute(:project_import_limit),
      group_export: application_setting_limit_per_minute(:group_export_limit),
      group_export_download: application_setting_limit_per_minute(:group_download_export_limit),
      group_import: application_setting_limit_per_minute(:group_import_limit),
      raw_blob: application_setting_limit_per_minute(:raw_blob_request_limit),
      search_rate_limit: application_setting_limit_per_minute(:search_rate_limit),
      search_rate_limit_unauthenticated: application_setting_limit_per_minute(:search_rate_limit_unauthenticated),
      users_get_by_id: {
        enabled: application_settings[:users_get_by_id_limit] > 0,
        requests_per_period: application_settings[:users_get_by_id_limit],
        period_in_seconds: 10.minutes
      }
    }
  end

  def ci_cd_limits
    Plan.all.to_h { |plan| [plan.name.capitalize, plan_ci_cd_limits(plan)] }
  end

  def plan_ci_cd_limits(plan)
    plan.actual_limits.slice(
      :ci_pipeline_size,
      :ci_active_jobs,
      :ci_active_pipelines,
      :ci_project_subscriptions,
      :ci_pipeline_schedules,
      :ci_needs_size_limit,
      :ci_registered_group_runners,
      :ci_registered_project_runners
    )
  end

  def ssh_algorithm_file(algorithm)
    File.join(SSH_ALGORITHMS_PATH, "ssh_host_#{algorithm.downcase}_key.pub")
  end

  def ssh_algorithm_hashes(algorithm)
    content = ssh_algorithm_file_content(algorithm)
    return unless content.present?

    { name: algorithm,
      md5: ssh_algorithm_md5(content),
      sha256: ssh_algorithm_sha256(content) }
  end

  def ssh_algorithm_file_content(algorithm)
    file = ssh_algorithm_file(algorithm)
    return unless File.exist?(file)

    File.read(file)
  end

  def ssh_algorithm_md5(ssh_file_content)
    Gitlab::SSHPublicKey.new(ssh_file_content).fingerprint
  end

  def ssh_algorithm_sha256(ssh_file_content)
    Gitlab::SSHPublicKey.new(ssh_file_content).fingerprint_sha256
  end

  def application_settings
    Gitlab::CurrentSettings.current_application_settings
  end

  def application_setting_limit_per_minute(setting)
    {
      enabled: application_settings[setting] > 0,
      requests_per_period: application_settings[setting],
      period_in_seconds: 1.minute
    }
  end
end