defmodule Plausible.Stats.Imported.SQL.WhereBuilder do
  @moduledoc """
  A module for building an ecto where clause of a query out of a query for Imported tables.
  """

  import Ecto.Query

  alias Plausible.Stats.Query

  @db_field_mappings %{
    referrer_source: :source,
    screen_size: :device,
    screen: :device,
    os: :operating_system,
    os_version: :operating_system_version,
    country_code: :country,
    subdivision1_code: :region,
    city_geoname_id: :city,
    entry_page_hostname: :hostname,
    pathname: :page,
    url: :link_url
  }

  def build(%Query{filters: []}), do: true

  def build(query) do
    query.filters
    |> Enum.map(&add_filter(query, &1))
    |> Enum.reduce(fn condition, acc -> dynamic([], ^acc and ^condition) end)
  end

  defp add_filter(query, [:ignore_in_totals_query, filter]) do
    add_filter(query, filter)
  end

  defp add_filter(query, [:not, filter]) do
    dynamic([i], not (^add_filter(query, filter)))
  end

  defp add_filter(query, [:and, filters]) do
    filters
    |> Enum.map(&add_filter(query, &1))
    |> Enum.reduce(fn condition, acc -> dynamic([], ^acc and ^condition) end)
  end

  defp add_filter(query, [:or, filters]) do
    filters
    |> Enum.map(&add_filter(query, &1))
    |> Enum.reduce(fn condition, acc -> dynamic([], ^acc or ^condition) end)
  end

  defp add_filter(query, [_operation, dimension, _clauses | _rest] = filter) do
    db_field = Plausible.Stats.Filters.without_prefix(dimension)

    if db_field == :goal do
      Plausible.Stats.Goals.add_filter(query, filter, imported?: true)
    else
      mapped_db_field = Map.get(@db_field_mappings, db_field, db_field)

      Plausible.Stats.SQL.WhereBuilder.build_condition(mapped_db_field, filter)
    end
  end
end
