-- | This module defines the top-level translation functions pertaining to
-- queries that use aggregation (i.e., /not/ so-called "simple" selects) into
-- Postgres AST.
module Hasura.Backends.Postgres.Translate.Select.Aggregate
  ( mkAggregateSelect,
    selectAggregateQuerySQL,
  )
where

import Control.Monad.Writer.Strict (runWriter)
import Database.PG.Query (Query, fromBuilder)
import Hasura.Backends.Postgres.SQL.DML (BoolExp (BELit), Select)
import Hasura.Backends.Postgres.SQL.IdentifierUniqueness
  ( prefixNumToAliases,
  )
import Hasura.Backends.Postgres.SQL.Types
  ( IsIdentifier (toIdentifier),
  )
import Hasura.Backends.Postgres.Translate.Select.AnnotatedFieldJSON
import Hasura.Backends.Postgres.Translate.Select.GenerateSelect (generateSQLSelectFromArrayNode)
import Hasura.Backends.Postgres.Translate.Select.Process (processAnnAggregateSelect)
import Hasura.Backends.Postgres.Translate.Types
  ( MultiRowSelectNode (MultiRowSelectNode),
    SelectNode (SelectNode),
    SourcePrefixes (SourcePrefixes),
  )
import Hasura.Prelude
import Hasura.RQL.IR.Select
  ( AnnAggregateSelect,
    AnnSelectG (_asnStrfyNum),
  )
import Hasura.RQL.Types.Backend (Backend)
import Hasura.RQL.Types.Common (FieldName (FieldName))
import Hasura.SQL.Backend (BackendType (Postgres))
import Hasura.SQL.Types (ToSQL (toSQL))

-- | Translates IR to Postgres queries for aggregated SELECTs.
--
-- See 'mkAggregateSelect' for the Postgres AST.
selectAggregateQuerySQL ::
  forall pgKind.
  (Backend ('Postgres pgKind), PostgresAnnotatedFieldJSON pgKind) =>
  AnnAggregateSelect ('Postgres pgKind) ->
  Query
selectAggregateQuerySQL =
  fromBuilder . toSQL . mkAggregateSelect

mkAggregateSelect ::
  forall pgKind.
  ( Backend ('Postgres pgKind),
    PostgresAnnotatedFieldJSON pgKind
  ) =>
  AnnAggregateSelect ('Postgres pgKind) ->
  Select
mkAggregateSelect annAggSel =
  let ((selectSource, nodeExtractors, topExtractor), joinTree) =
        runWriter $
          flip runReaderT strfyNum $
            processAnnAggregateSelect sourcePrefixes rootFieldName annAggSel
      selectNode = SelectNode nodeExtractors joinTree
      arrayNode = MultiRowSelectNode [topExtractor] selectNode
   in prefixNumToAliases $
        generateSQLSelectFromArrayNode selectSource arrayNode $ BELit True
  where
    strfyNum = _asnStrfyNum annAggSel
    rootFieldName = FieldName "root"
    rootIdentifier = toIdentifier rootFieldName
    sourcePrefixes = SourcePrefixes rootIdentifier rootIdentifier