content: {-# LANGUAGE CPP #-}
  {-# LANGUAGE TemplateHaskell #-}
  {-# LANGUAGE ExistentialQuantification #-}
  {-# LANGUAGE FlexibleInstances #-}
  {-# LANGUAGE PolyKinds #-}
  
  module Data.Aeson.TypeScript.Lookup where
  
  import Control.Monad
  import Data.Aeson.TypeScript.Instances ()
  import Data.Aeson.TypeScript.Types
  import Data.Function
  import qualified Data.List as L
  import Data.Proxy
  import Data.String.Interpolate
  import Language.Haskell.TH hiding (stringE)
  import qualified Language.Haskell.TH.Lib as TH
  
  
  -- | Generates a 'TypeScript' declaration for a closed type family as a lookup type.
  deriveTypeScriptLookupType :: Name
                             -- ^ Name of a type family.
                             -> String
                             -- ^ Name of the declaration to derive.
                             -> Q [Dec]
  deriveTypeScriptLookupType name declNameStr = do
    info <- reify name
    case info of
      FamilyI (ClosedTypeFamilyD (TypeFamilyHead _name _vars _sig _maybeInject) eqns) _decs -> do
        interfaceDecl <- getClosedTypeFamilyInterfaceDecl name eqns
        return [FunD (mkName declNameStr) [Clause [] (NormalB (ListE [interfaceDecl])) []]]
  
      _ -> fail [i|Expected a closed type family; got #{info}|]
  
  getClosedTypeFamilyInterfaceDecl :: Name -> [TySynEqn] -> Q Exp
  getClosedTypeFamilyInterfaceDecl name eqns = do
    fields <- forM eqns $ \case
  #if MIN_VERSION_template_haskell(2,15,0)
      TySynEqn Nothing (AppT (ConT _) (ConT arg)) result -> do
        [| TSField False (getTypeScriptType (Proxy :: Proxy $(conT arg))) (getTypeScriptType (Proxy :: Proxy $(return result))) Nothing |]
      TySynEqn Nothing (AppT (ConT _) (PromotedT arg)) result -> do
        [| TSField False (getTypeScriptType (Proxy :: Proxy $(promotedT arg))) (getTypeScriptType (Proxy :: Proxy $(return result))) Nothing |]
  #else
      TySynEqn [ConT arg] result -> do
        [| TSField False (getTypeScriptType (Proxy :: Proxy $(conT arg))) (getTypeScriptType (Proxy :: Proxy $(return result))) Nothing |]
  #endif
      x -> fail [i|aeson-typescript doesn't know yet how to handle this type family equation when generating interface declaration: '#{x}'|]
  
    [| TSInterfaceDeclaration $(TH.stringE $ nameBase name) [] (L.sortBy (compare `on` fieldName) $(listE $ fmap return fields)) Nothing |]
  
  getClosedTypeFamilyImage :: [TySynEqn] -> Q [Type]
  getClosedTypeFamilyImage eqns = do
    forM eqns $ \case
  #if MIN_VERSION_template_haskell(2,15,0)
      TySynEqn Nothing (AppT (ConT _) _) result -> return result
  #else
      TySynEqn [ConT _] result -> return result
  #endif
      x -> fail [i|aeson-typescript doesn't know yet how to handle this type family equation when calculating closed type family image: '#{x}'|]
  