-- | Specification for valid Request component types used at the Kind and Type level for
-- creating an API structure.
--
module Hreq.Core.API.Request where

import Data.Kind (Type)
import Data.Singletons (Sing, SingI(..))
import GHC.TypeLits (Symbol)

-- * Request Type
data ReqContent a =
      Path a Symbol
    | Params [(Symbol, a)]
    | QueryFlags a [Symbol]
    | Captures [a]
    | BasicAuth a Symbol
    | CaptureAll a
    | ReqBody a a
    | StreamBody a a
    | ReqHeaders [(Symbol, a)]

-- * Request Type Synonyms
type Captures = 'Captures
type QueryFlags = 'QueryFlags ()
type Params = 'Params
type ReqBody = 'ReqBody
type StreamBody = 'StreamBody
type ReqHeaders = 'ReqHeaders
type CaptureAll = 'CaptureAll
type BasicAuth = 'BasicAuth ()
type Path = 'Path ()
type QueryFlag (s :: Symbol) = QueryFlags '[ s ]
type Param s t = Params '[ '( s,  t ) ]
type Capture t = Captures '[ t ]

-- * Request as a Singleton GADT
data SReqContent (a :: ReqContent Type)
  = forall t s . a ~ 'Path t s => SPath (Sing t) (Sing s)
  | forall ts (b :: Type) . a ~ 'QueryFlags b ts => SQueryFlags (Sing b) (Sing ts)
  | forall b . a ~ 'CaptureAll b => SCaptureAll (Sing b)
  | forall ts . a ~ 'Captures ts => SCaptures (Sing ts)
  | forall ctyp b . a ~ 'ReqBody ctyp b => SReqBody (Sing ctyp) (Sing b)
  | forall ctyp b . a ~ 'StreamBody ctyp b => SStreamBody (Sing ctyp) (Sing b)
  | forall ts . a ~ ReqHeaders ts => SReqHeaders (Sing ts)
  | forall ts . a ~ Params ts => SParams (Sing ts)
  | forall b s . a ~ 'BasicAuth b s => SBasicAuth (Sing b) (Sing s)

type instance Sing = SReqContent

instance (SingI a, SingI s) => SingI ('BasicAuth a s :: ReqContent Type) where
  sing = SBasicAuth sing sing

instance (SingI a, SingI s) => SingI ('Path a s :: ReqContent Type) where
  sing = SPath sing sing

instance SingI ts => SingI ('Params ts :: ReqContent Type) where
  sing = SParams sing

instance (SingI a, SingI ts) => SingI ('QueryFlags a ts :: ReqContent Type) where
  sing = SQueryFlags sing sing

instance SingI ts => SingI ('Captures ts :: ReqContent Type) where
  sing = SCaptures sing

instance (SingI a) => SingI ('CaptureAll a :: ReqContent Type) where
  sing = SCaptureAll sing

instance (SingI a, SingI ctyp) => SingI ('ReqBody ctyp a :: ReqContent Type) where
  sing = SReqBody sing sing

instance (SingI a, SingI ctyp) => SingI ('StreamBody ctyp a :: ReqContent Type) where
  sing = SStreamBody sing sing

instance SingI a => SingI ('ReqHeaders a :: ReqContent Type) where
  sing = SReqHeaders sing