require_relative '../inferno/terminology/tasks/check_built_terminology'

module ONCCertificationG10TestKit
  class ConfigurationChecker
    EXPECTED_VALIDATOR_VERSION = '2.3.2'.freeze
    INFERNO_VALIDATOR_VERSION_KEY = 'inferno-framework/fhir-validator-wrapper'.freeze
    EXPECTED_HL7_VALIDATOR_VERSION = '1.0.60'.freeze
    HL7_VALIDATOR_VERSION_KEY = 'validatorWrapperVersion'.freeze

    def configuration_messages
      validator_version_message + terminology_messages + version_message
    end

    def terminology_checker
      @terminology_checker ||= Inferno::Terminology::Tasks::CheckBuiltTerminology.new
    end

    def mismatched_value_sets
      terminology_checker.mismatched_value_sets
    end

    def validator_url
      @validator_url ||= G10CertificationSuite.find_validator(:default).url
    end

    def validator_version_message
      if Feature.use_hl7_resource_validator?
        expected_validator_version = EXPECTED_HL7_VALIDATOR_VERSION
        validator_version_key = HL7_VALIDATOR_VERSION_KEY
        validator_version_url = "#{validator_url}/validator/version"
      else
        expected_validator_version = EXPECTED_VALIDATOR_VERSION
        validator_version_key = INFERNO_VALIDATOR_VERSION_KEY
        validator_version_url = "#{validator_url}/version"
      end

      response = Faraday.get validator_version_url
      if response.body.starts_with? '{'
        version_json = JSON.parse(response.body)
        version = version_json[validator_version_key]
      else
        version = response.body
      end

      if version == expected_validator_version
        [{
          type: 'info',
          message: "FHIR validator is the expected version `#{expected_validator_version}`"
        }]
      else
        [{
          type: 'error',
          message: "Expected FHIR validator version `#{expected_validator_version}`, but found `#{version}`"
        }]
      end
    rescue JSON::ParserError => e
      [{
        type: 'error',
        message: "Unable to parse Validator version '`#{response.body}`'. Parser error: `#{e.message}`"
      }]
    rescue StandardError => e
      [{
        type: 'error',
        message: "Unable to connect to Validator: `#{e.message}`"
      }]
    end

    def code_system_version_messages
      path = File.join('resources', 'terminology', 'validators', 'bloom', 'metadata.yml')
      return '' unless File.exist? path

      cs_metadata = YAML.load_file(path)
      message = "Terminology was generated based on the following code system versions:\n"
      cs_metadata.each do |_url, metadata|
        message += "* #{metadata[:name]}: version #{metadata[:versions].join(', ')}\n"
      end

      message
    end

    def terminology_messages # rubocop:disable Metrics/CyclomaticComplexity
      success_messages = []
      warning_messages = []
      error_messages = []
      messages = []
      terminology_checker.expected_manifest.each do |expected_value_set|
        url = expected_value_set[:url]
        actual_value_set = terminology_checker.new_value_set(url)

        if actual_value_set == expected_value_set
          success_messages << "* `#{url}`: #{actual_value_set[:count]} codes"
        elsif actual_value_set.nil?
          error_messages << "* `#{url}`: Not loaded"
        elsif terminology_checker.class::NON_UMLS_SYSTEMS.include? url
          warning_messages <<
            "* `#{url}`: Expected codes: #{expected_value_set[:count]} Actual codes: #{actual_value_set[:count]}"
        else
          error_messages <<
            "* `#{url}`: Expected codes: #{expected_value_set[:count]} Actual codes: #{actual_value_set[:count]}"
        end
      end

      code_system_messages = code_system_version_messages

      if code_system_version_messages.present?
        messages << {
          type: 'info',
          message: code_system_messages
        }
      end

      if success_messages.present?
        messages << {
          type: 'info',
          message:
            "The following value sets and code systems have been properly loaded:\n#{success_messages.join("\n")}"
        }
      end

      if warning_messages.present?
        warning_message = <<~WARNING
          Some terminology not based on UMLS did not match, but this can be a
          result of these terminologies having a different update schedule than
          UMLS. As long as the actual number of codes is close to the expected
          number, this does not does not reflect a problem with the terminology
          build.
        WARNING
        messages << {
          type: 'warning',
          message: warning_message + warning_messages.join("\n")
        }
      end

      if error_messages.present?
        error_message = <<~ERROR
          There is a problem with the terminology resources. See the README for
          the [G10 Certification Test Kit
          README](https://github.com/inferno-framework/g10-certification-test-kit#terminology-support)
          for instructions on building the required terminology resources:\n
        ERROR
        messages << {
          type: 'error',
          message: error_message + error_messages.join("\n")
        }
      end

      messages
    end

    def version_message
      return [] if VERSION.match?(/\A\d+\.\d+\.\d+\z/)

      [{
        type: 'error',
        message: <<~MESSAGE
          This is a development version (`#{VERSION}`) of the ONC Certification
          (g)(10) Standardized API Test Kit and is not suitable for
          certification. Please [download an official
          release](https://github.com/onc-healthit/onc-certification-g10-test-kit/releases)
          if you did not intend to use the development version.
        MESSAGE
      }]
    end
  end
end