# frozen_string_literal: true

require 'active_support/deprecation'
require 'uri'
require 'etc'

module QA
  module Runtime
    module Env
      extend self

      attr_writer :gitlab_url

      ENV_VARIABLES = Gitlab::QA::Runtime::Env::ENV_VARIABLES

      # The environment variables used to indicate if the environment under test
      # supports the given feature
      SUPPORTED_FEATURES = {
        git_protocol_v2: 'QA_CAN_TEST_GIT_PROTOCOL_V2',
        admin: 'QA_CAN_TEST_ADMIN_FEATURES',
        praefect: 'QA_CAN_TEST_PRAEFECT'
      }.freeze

      def supported_features
        SUPPORTED_FEATURES
      end

      def gitlab_url
        @gitlab_url ||= ENV["QA_GITLAB_URL"] || "http://127.0.0.1:3000" # default to GDK
      end

      # Retrieves the value of the gitlab_canary cookie if set or returns an empty hash.
      #
      # @return [Hash]
      def canary_cookie
        canary = ENV['QA_COOKIES']&.scan(/gitlab_canary=(true|false)/)&.dig(0, 0)

        canary ? { gitlab_canary: canary } : {}
      end

      def additional_repository_storage
        ENV['QA_ADDITIONAL_REPOSITORY_STORAGE'] || 'secondary'
      end

      def non_cluster_repository_storage
        ENV['QA_GITALY_NON_CLUSTER_STORAGE'] || 'gitaly'
      end

      def praefect_repository_storage
        ENV['QA_PRAEFECT_REPOSITORY_STORAGE'] || 'default'
      end

      def interception_enabled?
        enabled?(ENV['QA_INTERCEPT_REQUESTS'], default: false)
      end

      def can_intercept?
        browser == :chrome && interception_enabled?
      end

      def release
        ENV['RELEASE']
      end

      def release_registry_url
        ENV['RELEASE_REGISTRY_URL']
      end

      def release_registry_username
        ENV['RELEASE_REGISTRY_USERNAME']
      end

      def release_registry_password
        ENV['RELEASE_REGISTRY_PASSWORD']
      end

      def ci_job_url
        ENV['CI_JOB_URL']
      end

      def ci_job_name
        ENV['CI_JOB_NAME']
      end

      def ci_project_name
        ENV['CI_PROJECT_NAME']
      end

      def ci_project_path
        ENV['CI_PROJECT_PATH']
      end

      def ci_project_dir
        ENV.fetch('CI_PROJECT_DIR')
      end

      def coverband_enabled?
        enabled?(ENV['COVERBAND_ENABLED'], default: false)
      end

      def istanbul_coverage_enabled?
        ENV['BABEL_ENV'] == 'istanbul'
      end

      def selective_execution_improved_enabled?
        enabled?(ENV['SELECTIVE_EXECUTION_IMPROVED'], default: false)
      end

      def frontend_selective_execution_enabled?
        enabled?(ENV['FRONTEND_SELECTIVE_EXECUTION'], default: false)
      end

      def mr_targeting_stable_branch?
        /^[\d-]+-stable(-ee|-jh)?$/.match?(ENV['CI_MERGE_REQUEST_TARGET_BRANCH_NAME'] || "")
      end

      def schedule_type
        ENV['SCHEDULE_TYPE']
      end

      def generate_allure_report?
        enabled?(ENV['QA_GENERATE_ALLURE_REPORT'], default: false)
      end

      def default_branch
        'main'
      end

      def colorized_logs?
        enabled?(ENV['COLORIZED_LOGS'], default: false)
      end

      # set to 'false' to have the browser run visibly instead of headless
      def webdriver_headless?
        if ENV.key?('CHROME_HEADLESS')
          Rails.application.deprecators[:qa].warn("CHROME_HEADLESS is deprecated. Use WEBDRIVER_HEADLESS instead.")
        end

        return enabled?(ENV['WEBDRIVER_HEADLESS']) unless ENV['WEBDRIVER_HEADLESS'].nil?

        enabled?(ENV['CHROME_HEADLESS'])
      end

      # set to 'true' to have Chrome use a fixed profile directory
      def reuse_chrome_profile?
        enabled?(ENV['CHROME_REUSE_PROFILE'], default: false)
      end

      # Disable /dev/shm use in CI. See https://gitlab.com/gitlab-org/gitlab/issues/4252
      def disable_dev_shm?
        running_in_ci? || enabled?(ENV['CHROME_DISABLE_DEV_SHM'], default: false)
      end

      def accept_insecure_certs?
        enabled?(ENV['ACCEPT_INSECURE_CERTS'])
      end

      def running_on_live_env?
        running_on_dot_com? || running_on_release?
      end

      def running_on_dot_com?
        gitlab_host.include?('.com')
      end

      # This method can be removed after decommissioning `release` instance
      # https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/1571
      def running_on_release?
        gitlab_host == 'release.gitlab.net'
      end

      def running_on_dev?
        uri = URI.parse(Runtime::Scenario.gitlab_address)
        uri.port != 80 && uri.port != 443
      end

      def running_on_dev_or_dot_com?
        running_on_dev? || running_on_dot_com?
      end

      def running_in_ci?
        ENV['CI'] || ENV['CI_SERVER']
      end

      def qa_cookies
        ENV['QA_COOKIES'] && ENV['QA_COOKIES'].split(';')
      end

      def running_against_cell?
        return false unless qa_cookies

        qa_cookies.any? do |cookie|
          cookie.start_with?("_gitlab_session=cell-")
        end
      end

      def signup_disabled?
        enabled?(ENV['SIGNUP_DISABLED'], default: false)
      end

      # PATs are disabled for FedRamp
      def personal_access_tokens_disabled?
        enabled?(ENV['PERSONAL_ACCESS_TOKENS_DISABLED'], default: false)
      end

      def remote_grid
        # if username specified, password/auth token is required
        # can be
        # - "http://user:pass@somehost.com/wd/hub"
        # - "https://user:pass@somehost.com:443/wd/hub"
        # - "http://localhost:4444/wd/hub"

        return if (ENV['QA_REMOTE_GRID'] || '').empty?

        "#{remote_grid_protocol}://#{remote_grid_credentials}#{ENV['QA_REMOTE_GRID']}/wd/hub"
      end

      def remote_grid_username
        ENV['QA_REMOTE_GRID_USERNAME']
      end

      def remote_grid_access_key
        ENV['QA_REMOTE_GRID_ACCESS_KEY']
      end

      def remote_grid_protocol
        ENV['QA_REMOTE_GRID_PROTOCOL'] || 'http'
      end

      def remote_tunnel_id
        ENV['QA_REMOTE_TUNNEL_ID'] || 'gitlab-sl_tunnel_id'
      end

      def browser
        ENV['QA_BROWSER'].nil? ? :chrome : ENV['QA_BROWSER'].to_sym
      end

      def selenoid_browser_version
        ENV['QA_SELENOID_BROWSER_VERSION']
      end

      def selenoid_browser_image
        ENV['QA_SELENOID_BROWSER_IMAGE']
      end

      def video_recorder_image
        ENV['QA_VIDEO_RECORDER_IMAGE']
      end

      def remote_mobile_device_name
        ENV['QA_REMOTE_MOBILE_DEVICE_NAME']&.downcase
      end

      def layout
        ENV['QA_LAYOUT']&.downcase || ''
      end

      def tablet_layout?
        return true if remote_mobile_device_name && !phone_layout?

        layout.include?('tablet')
      end

      def phone_layout?
        return true if layout.include?('phone')

        return false unless remote_mobile_device_name

        !(remote_mobile_device_name.include?('ipad') || remote_mobile_device_name.include?('tablet'))
      end

      def mobile_layout?
        phone_layout? || tablet_layout? || remote_mobile_device_name
      end

      def record_video?
        enabled?(ENV['QA_RECORD_VIDEO'], default: false)
      end

      def use_selenoid?
        enabled?(ENV['USE_SELENOID'], default: false)
      end

      def use_sha256_repository_object_storage
        enabled?(ENV['QA_USE_SHA256_REPOSITORY_OBJECT_STORAGE'], default: false)
      end

      def save_all_videos?
        enabled?(ENV['QA_SAVE_ALL_VIDEOS'], default: false)
      end

      def require_video_variables!
        docs_link = 'https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/docs/running_against_remote_grid.md#testing-with-selenoid'
        use_selenoid? || (raise ArgumentError, "USE_SELENOID is required! See docs: #{docs_link}")
        remote_grid || (raise ArgumentError, "QA_REMOTE_GRID is required! See docs: #{docs_link}")
        video_recorder_image || (raise ArgumentError, "QA_VIDEO_RECORDER_IMAGE is required! See docs: #{docs_link}")
        selenoid_browser_image || (raise ArgumentError, "QA_SELENOID_BROWSER_IMAGE is required! See docs: #{docs_link}")
        selenoid_browser_version || (raise ArgumentError,
          "QA_SELENOID_BROWSER_VERSION is required! See docs: #{docs_link}")
      end

      def github_username
        ENV['QA_GITHUB_USERNAME']
      end

      def github_password
        ENV['QA_GITHUB_PASSWORD']
      end

      def jira_admin_username
        ENV['QA_JIRA_ADMIN_USERNAME']
      end

      def jira_admin_password
        ENV['QA_JIRA_ADMIN_PASSWORD']
      end

      def jira_hostname
        ENV['JIRA_HOSTNAME']
      end

      def slack_workspace
        ENV['QA_SLACK_WORKSPACE']
      end

      def slack_email
        ENV['QA_SLACK_EMAIL']
      end

      def slack_password
        ENV['QA_SLACK_PASSWORD']
      end

      def jenkins_admin_username
        ENV.fetch('QA_JENKINS_USER', 'administrator')
      end

      def jenkins_admin_password
        ENV.fetch('QA_JENKINS_PASS', 'password')
      end

      # this is set by the integrations job
      # which will allow bidirectional communication
      # between the app and the specs container
      # should the specs container spin up a server
      def qa_hostname
        ENV['QA_HOSTNAME']
      end

      def knapsack?
        ENV['CI_NODE_TOTAL'].to_i > 1 && ENV['NO_KNAPSACK'] != "true"
      end

      def sandbox_name
        ENV['GITLAB_SANDBOX_NAME']
      end

      def namespace_name
        ENV['GITLAB_NAMESPACE_NAME']
      end

      def auto_devops_project_name
        ENV['GITLAB_AUTO_DEVOPS_PROJECT_NAME']
      end

      def gcloud_account_key
        ENV.fetch("GCLOUD_ACCOUNT_KEY")
      end

      def gcloud_account_email
        ENV.fetch("GCLOUD_ACCOUNT_EMAIL")
      end

      def gcloud_region
        ENV['GCLOUD_REGION']
      end

      def gcloud_num_nodes
        ENV.fetch('GCLOUD_NUM_NODES', 1)
      end

      def has_gcloud_credentials?
        %w[GCLOUD_ACCOUNT_KEY GCLOUD_ACCOUNT_EMAIL].none? { |var| ENV[var].to_s.empty? }
      end

      # ENV variables for workspaces to run against existing cluster or creating new cluster
      def workspaces_cluster_available?
        enabled?(ENV['WORKSPACES_CLUSTER_AVAILABLE'], default: false)
      end

      def workspaces_cluster_name
        ENV.fetch("WORKSPACES_CLUSTER_NAME")
      end

      def workspaces_cluster_region
        ENV.fetch("WORKSPACES_CLUSTER_REGION")
      end

      # ENV variables for workspaces OAuth App and the domain
      def workspaces_oauth_app_id
        ENV.fetch("WORKSPACES_OAUTH_APP_ID")
      end

      def workspaces_oauth_app_secret
        ENV.fetch("WORKSPACES_OAUTH_APP_SECRET")
      end

      def workspaces_oauth_signing_key
        ENV.fetch("WORKSPACES_OAUTH_SIGNING_KEY")
      end

      def workspaces_proxy_version
        ENV.fetch("WORKSPACES_PROXY_VERSION", '0.1.12')
      end

      def workspaces_proxy_domain
        ENV.fetch("WORKSPACES_PROXY_DOMAIN")
      end

      def workspaces_domain_cert
        ENV.fetch("WORKSPACES_DOMAIN_CERT")
      end

      def workspaces_domain_key
        ENV.fetch("WORKSPACES_DOMAIN_KEY")
      end

      def workspaces_wildcard_cert
        ENV.fetch("WORKSPACES_WILDCARD_CERT")
      end

      def workspaces_wildcard_key
        ENV.fetch("WORKSPACES_WILDCARD_KEY")
      end

      # Specifies the token that can be used for the GitHub API
      def github_access_token
        ENV['QA_GITHUB_ACCESS_TOKEN'].to_s.strip
      end

      def require_github_access_token!
        return unless github_access_token.empty?

        raise ArgumentError, "Please provide QA_GITHUB_ACCESS_TOKEN"
      end

      # Returns true if there is an environment variable that indicates that
      # the feature is supported in the environment under test.
      # All features are supported by default.
      def can_test?(feature)
        raise ArgumentError, %(Unknown feature "#{feature}") unless SUPPORTED_FEATURES.include? feature

        enabled?(ENV[SUPPORTED_FEATURES[feature]], default: true)
      end

      def runtime_scenario_attributes
        ENV['QA_RUNTIME_SCENARIO_ATTRIBUTES']
      end

      def simulate_slow_connection?
        enabled?(ENV['QA_SIMULATE_SLOW_CONNECTION'], default: false)
      end

      def slow_connection_latency
        ENV.fetch('QA_SLOW_CONNECTION_LATENCY_MS', 2000).to_i
      end

      def slow_connection_throughput
        ENV.fetch('QA_SLOW_CONNECTION_THROUGHPUT_KBPS', 32).to_i
      end

      def gitlab_qa_loop_runner_minutes
        ENV.fetch('GITLAB_QA_LOOP_RUNNER_MINUTES', 1).to_i
      end

      def mailhog_hostname
        ENV['MAILHOG_HOSTNAME']
      end

      # Get the version of GitLab currently being tested against
      # @return String Version
      # @example
      #   > Env.deploy_version
      #   #=> 13.3.4-ee.0
      def deploy_version
        ENV['DEPLOY_VERSION']
      end

      def user_agent
        ENV['GITLAB_QA_USER_AGENT']
      end

      def geo_environment?
        QA::Runtime::Scenario.attributes.include?(:geo_secondary_address)
      end

      def gitlab_tls_certificate
        ENV['GITLAB_TLS_CERTIFICATE']
      end

      def export_metrics?
        enabled?(ENV['QA_EXPORT_TEST_METRICS'], default: false)
      end

      def save_metrics_json?
        enabled?(ENV['QA_SAVE_TEST_METRICS'], default: false)
      end

      def ee_license
        return ENV["QA_EE_LICENSE"] if ENV["QA_EE_LICENSE"]

        ENV["EE_LICENSE"].tap do |license|
          next unless license

          Runtime::Logger.warn("EE_LICENSE environment variable is deprecated, please use QA_EE_LICENSE instead!")
        end
      end

      def ee_activation_code
        ENV['QA_EE_ACTIVATION_CODE']
      end

      def quarantine_disabled?
        enabled?(ENV['DISABLE_QUARANTINE'], default: false)
      end

      def validate_resource_reuse?
        enabled?(ENV['QA_VALIDATE_RESOURCE_REUSE'], default: false)
      end

      def fips?
        enabled?(ENV['FIPS'], default: false)
      end

      def container_registry_host
        ENV.fetch('QA_CONTAINER_REGISTRY_HOST', 'registry.gitlab.com')
      end

      def runner_container_image
        ENV.fetch('QA_RUNNER_CONTAINER_IMAGE', 'gitlab-runner:alpine')
      end

      def runner_container_namespace
        ENV['QA_RUNNER_CONTAINER_NAMESPACE'] || 'gitlab-org'
      end

      def gitlab_qa_build_image
        ENV['QA_GITLAB_QA_BUILD_IMAGE'] || 'gitlab-build-images:gitlab-qa-alpine-ruby-2.7'
      end

      # ENV variables for authenticating against a private container registry
      # These need to be set if using the
      # Service::DockerRun::Mixins::ThirdPartyDocker module
      def third_party_docker_registry
        ENV['QA_THIRD_PARTY_DOCKER_REGISTRY']
      end

      def third_party_docker_repository
        ENV['QA_THIRD_PARTY_DOCKER_REPOSITORY']
      end

      def third_party_docker_user
        ENV['QA_THIRD_PARTY_DOCKER_USER']
      end

      def third_party_docker_password
        ENV['QA_THIRD_PARTY_DOCKER_PASSWORD']
      end

      def max_capybara_wait_time
        ENV.fetch('MAX_CAPYBARA_WAIT_TIME', 10).to_i
      end

      def use_public_ip_api?
        enabled?(ENV['QA_USE_PUBLIC_IP_API'], default: false)
      end

      def allow_local_requests?
        enabled?(ENV['QA_ALLOW_LOCAL_REQUESTS'], default: false)
      end

      def chrome_default_download_path
        ENV['DEFAULT_CHROME_DOWNLOAD_PATH'] || Dir.tmpdir
      end

      def require_slack_env!
        missing_env = %i[slack_workspace slack_email slack_password].select do |method|
          ::QA::Runtime::Env.public_send(method).nil?
        end
        return unless missing_env.any?

        raise "Missing Slack env: #{missing_env.map(&:upcase).join(', ')}"
      end

      def one_p_email
        ENV['QA_1P_EMAIL']
      end

      def one_p_password
        ENV['QA_1P_PASSWORD']
      end

      def one_p_secret
        ENV['QA_1P_SECRET']
      end

      def one_p_github_uuid
        ENV['QA_1P_GITHUB_UUID']
      end

      # Docker network to use when starting sidecar containers
      #
      # @return [String]
      def docker_network
        ENV["QA_DOCKER_NETWORK"]
      end

      # Product analytics configurator string (e.g. https://usr:pass@gl-configurator.gitlab.com)
      #
      # @return [String]
      def pa_configurator_url
        ENV['PA_CONFIGURATOR_URL']
      end

      # Product analytics collector url (e.g. https://collector.gitlab.com)
      #
      # @return [String]
      def pa_collector_host
        ENV['PA_COLLECTOR_HOST']
      end

      # Product analytics cube api url (e.g. https://cube.gitlab.com)
      #
      # @return [String]
      def pa_cube_api_url
        ENV['PA_CUBE_API_URL']
      end

      # Product analytics cube api key
      #
      # @return [String]
      def pa_cube_api_key
        ENV['PA_CUBE_API_KEY']
      end

      # Test run is in rspec retried process
      #
      # @return [Boolean]
      def rspec_retried?
        enabled?(ENV['QA_RSPEC_RETRIED'], default: false)
      end

      def parallel_processes
        ENV.fetch('QA_PARALLEL_PROCESSES') do
          [Etc.nprocessors / 2, 1].max
        end.to_i
      end

      # Execution was started by parallel runner
      #
      # @return [Boolean]
      def parallel_run?
        ENV["TEST_ENV_NUMBER"].present?
      end

      # Execute tests in multiple parallel processes
      #
      # @return [Boolean]
      def run_in_parallel?
        enabled?(ENV["QA_RUN_IN_PARALLEL"], default: false)
      end

      # Environment has no support for admin operations
      #
      # @return [Boolean]
      def no_admin_environment?
        enabled?(ENV["QA_NO_ADMIN_ENV"], default: false) || gitlab_host == "gitlab.com"
      end

      # Test run type
      #
      # @return [String]
      def run_type
        ENV["QA_RUN_TYPE"].presence
      end

      # Execution performed with --dry-run flag
      #
      # @return [Boolean]
      def dry_run
        enabled?(ENV["QA_RSPEC_DRY_RUN"], default: false)
      end

      # Ignore runtime data when generating knapsack reports
      #
      # @return [Boolean]
      def ignore_runtime_data?
        enabled?(ENV["QA_IGNORE_RUNTIME_DATA"], default: false)
      end

      # Create uniq test users for each test
      #
      # @return [Boolean]
      def create_unique_test_users?
        enabled?(ENV["QA_CREATE_UNIQUE_TEST_USERS"], default: true)
      end

      # Run tests against Project Studio UI
      #
      # @return [Boolean]
      def project_studio_enabled?
        enabled?(ENV["QA_PROJECT_STUDIO"], default: false)
      end

      private

      # Gitlab host tests are running against
      #
      # @return [String]
      def gitlab_host
        # gitlab address should be immutable so it's ok to memoize as global
        @gitlab_host ||= URI.parse(Runtime::Scenario.gitlab_address).host
      end

      def remote_grid_credentials
        if remote_grid_username
          unless remote_grid_access_key
            raise ArgumentError, %(Please provide an access key for user "#{remote_grid_username}")
          end

          return "#{remote_grid_username}:#{remote_grid_access_key}@"
        end

        ''
      end

      def enabled?(value, default: true)
        return default if value.nil?

        value.to_s.match?(/^(true|yes|1)$/i)
      end
    end
  end
end

QA::Runtime::Env.extend_mod_with('Runtime::Env', namespace: QA)
