# frozen_string_literal: true

# Performs checks on whether resources from Grafana can be handled
# We have certain restrictions on which formats we accept.
# Some are technical requirements, others are simplifications.
module Grafana
  class Validator
    Error = Class.new(StandardError)

    attr_reader :grafana_dashboard, :datasource, :panel, :query_params

    UNSUPPORTED_GRAFANA_GLOBAL_VARS = %w(
      $__interval_ms
      $__timeFilter
      $__name
      $timeFilter
      $interval
    ).freeze

    def initialize(grafana_dashboard, datasource, panel, query_params)
      @grafana_dashboard = grafana_dashboard
      @datasource = datasource
      @panel = panel
      @query_params = query_params
    end

    def validate!
      validate_query_params!
      validate_panel_type!
      validate_variable_definitions!
      validate_global_variables!
      validate_datasource! if datasource
    end

    def valid?
      validate!

      true
    rescue ::Grafana::Validator::Error
      false
    end

    private

    # See defaults in Banzai::Filter::InlineGrafanaMetricsFilter.
    def validate_query_params!
      return if [:from, :to].all? { |param| query_params.include?(param) }

      raise_error 'Grafana query parameters must include from and to.'
    end

    # We may choose to support other panel types in future.
    def validate_panel_type!
      return if panel && panel[:type] == 'graph' && panel[:lines]

      raise_error 'Panel type must be a line graph.'
    end

    # We must require variable definitions to create valid prometheus queries.
    def validate_variable_definitions!
      return unless grafana_dashboard[:dashboard][:templating]

      return if grafana_dashboard[:dashboard][:templating][:list].all? do |variable|
        query_params[:"var-#{variable[:name]}"].present?
      end

      raise_error 'All Grafana variables must be defined in the query parameters.'
    end

    # We may choose to support further Grafana variables in future.
    def validate_global_variables!
      return unless panel_contains_unsupported_vars?

      raise_error "Prometheus must not include #{UNSUPPORTED_GRAFANA_GLOBAL_VARS}"
    end

    # We may choose to support additional datasources in future.
    def validate_datasource!
      return if datasource[:access] == 'proxy' && datasource[:type] == 'prometheus'

      raise_error 'Only Prometheus datasources with proxy access in Grafana are supported.'
    end

    def panel_contains_unsupported_vars?
      panel[:targets].any? do |target|
        UNSUPPORTED_GRAFANA_GLOBAL_VARS.any? do |variable|
          target[:expr].include?(variable)
        end
      end
    end

    def raise_error(message)
      raise Validator::Error, message
    end
  end
end