# frozen_string_literal: true

module LocationSearch
  class ResultAggregator
    include ActionView::Helpers::TextHelper

    # Time threshold for grouping consecutive points into visits (minutes)
    VISIT_TIME_THRESHOLD = 30

    def group_points_into_visits(points)
      return [] if points.empty?

      # Sort points by timestamp to handle unordered input
      sorted_points = points.sort_by { |p| p[:timestamp] }

      visits = []
      current_visit_points = []

      sorted_points.each do |point|
        if current_visit_points.empty? || within_visit_threshold?(current_visit_points.last, point)
          current_visit_points << point
        else
          # Finalize current visit and start a new one
          visits << create_visit_from_points(current_visit_points) if current_visit_points.any?
          current_visit_points = [point]
        end
      end

      # Don't forget the last visit
      visits << create_visit_from_points(current_visit_points) if current_visit_points.any?

      visits.sort_by { |visit| -visit[:timestamp] } # Most recent first
    end

    private

    def within_visit_threshold?(previous_point, current_point)
      time_diff = (current_point[:timestamp] - previous_point[:timestamp]).abs / 60.0 # minutes
      time_diff <= VISIT_TIME_THRESHOLD
    end

    def create_visit_from_points(points)
      return nil if points.empty?

      # Sort points by timestamp to get chronological order
      sorted_points = points.sort_by { |p| p[:timestamp] }
      first_point = sorted_points.first
      last_point = sorted_points.last

      # Calculate visit duration
      duration_minutes =
        if sorted_points.length > 1
          ((last_point[:timestamp] - first_point[:timestamp]) / 60.0).round
        else
          # Single point visit - estimate based on typical stay time
          15 # minutes
        end

      # Find the most accurate point (lowest accuracy value means higher precision)
      most_accurate_point = points.min_by { |p| p[:accuracy] || 999_999 }

      # Calculate average distance from search center
      average_distance = (points.sum { |p| p[:distance_meters] } / points.length).round(2)

      {
        timestamp: first_point[:timestamp],
        date: first_point[:date],
        coordinates: most_accurate_point[:coordinates],
        distance_meters: average_distance,
        duration_estimate: format_duration(duration_minutes),
        points_count: points.length,
        accuracy_meters: most_accurate_point[:accuracy],
        visit_details: {
          start_time: first_point[:date],
          end_time: last_point[:date],
          duration_minutes: duration_minutes,
          city: most_accurate_point[:city],
          country: most_accurate_point[:country],
          altitude_range: calculate_altitude_range(points)
        }
      }
    end

    def format_duration(minutes)
      return "~#{pluralize(minutes, 'minute')}" if minutes < 60

      hours = minutes / 60
      remaining_minutes = minutes % 60

      if remaining_minutes.zero?
        "~#{pluralize(hours, 'hour')}"
      else
        "~#{pluralize(hours, 'hour')} #{pluralize(remaining_minutes, 'minute')}"
      end
    end

    def calculate_altitude_range(points)
      altitudes = points.map { |p| p[:altitude] }.compact
      return nil if altitudes.empty?

      min_altitude = altitudes.min
      max_altitude = altitudes.max

      if min_altitude == max_altitude
        "#{min_altitude}m"
      else
        "#{min_altitude}m - #{max_altitude}m"
      end
    end
  end
end
