# frozen_string_literal: true

module Underpass
  # Namespace for Overpass Query Language related classes.
  module QL
    # High-level entry point for querying the Overpass API.
    #
    # Glues together {Request}, {Client}, {Response}, {QueryAnalyzer}, and
    # {Matcher} to provide a single-call interface.
    #
    # @example Query with a bounding box
    #   features = Underpass::QL::Query.perform(bbox, 'way["building"="yes"];')
    #
    # @example Query with a named area
    #   features = Underpass::QL::Query.perform_in_area('Romania', 'node["place"="city"];')
    class Query
      # Queries the Overpass API within a bounding box.
      #
      # @param bounding_box [RGeo::Feature::Geometry] an RGeo polygon defining the search area
      # @param query [String, Builder] an Overpass QL query string or a {Builder} instance
      # @return [Array<Feature>] the matched features
      # @raise [RateLimitError] when rate limited after exhausting retries
      # @raise [TimeoutError] when the API times out after exhausting retries
      # @raise [ApiError] when the API returns an unexpected error
      def self.perform(bounding_box, query)
        query_string     = resolve_query(query)
        op_bbox          = Underpass::QL::BoundingBox.from_geometry(bounding_box)
        request          = Underpass::QL::Request.new(query_string, op_bbox)
        execute(request, query_string)
      end

      # Queries the Overpass API within a named area (e.g. "Romania").
      #
      # @param area_name [String] an OSM area name
      # @param query [String, Builder] an Overpass QL query string or a {Builder} instance
      # @return [Array<Feature>] the matched features
      # @raise [RateLimitError] when rate limited after exhausting retries
      # @raise [TimeoutError] when the API times out after exhausting retries
      # @raise [ApiError] when the API returns an unexpected error
      def self.perform_in_area(area_name, query)
        query_string = resolve_query(query)
        request      = Underpass::QL::Request.new(query_string, nil, area_name: area_name)
        execute(request, query_string)
      end

      def self.resolve_query(query)
        query.respond_to?(:to_ql) ? query.to_ql : query
      end
      private_class_method :resolve_query

      def self.execute(request, query_string)
        api_response     = Underpass::Client.perform(request)
        response         = Underpass::QL::Response.new(api_response)
        query_analyzer   = Underpass::QL::QueryAnalyzer.new(query_string)
        requested_types  = query_analyzer.requested_types
        matcher          = Underpass::Matcher.new(response, requested_types)

        matcher.matches
      end
      private_class_method :execute
    end
  end
end