-- | Newtype wrappers for route DSL
module Mig.Core.Types.Route (
  -- * inputs
  Body (..),
  Query (..),
  QueryFlag (..),
  Optional (..),
  Capture (..),
  Header (..),
  OptionalHeader (..),
  Cookie (..),
  PathInfo (..),
  FullPathInfo (..),
  RawRequest (..),
  IsSecure (..),

  -- * outputs
  Send (..),
  Get,
  Post,
  Put,
  Delete,
  Options,
  Head,
  Patch,
  Trace,

  -- ** Method tags
  IsMethod (..),
  GET,
  POST,
  PUT,
  DELETE,
  OPTIONS,
  HEAD,
  PATCH,
  TRACE,
) where

import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import Data.Text (Text)
import GHC.TypeLits
import Network.HTTP.Types.Method

import Mig.Core.Types.Http (Request)

-------------------------------------------------------------------------------------
-- inputs

-- | Generic case for request body. The type encodes a media type and value of the request body.
newtype Body media a = Body a

{-| Required URL parameter query.

> "api/route?foo=bar" ==> (Query bar) :: Query "foo" a
-}
newtype Query (sym :: Symbol) a = Query a

{-| Optional URL parameter query.

> "api/route?foo=bar" ==> (Optional maybeBar) :: Query "foo" a
-}
newtype Optional (sym :: Symbol) a = Optional (Maybe a)

{-| Query flag. It is a boolean value in the URL-query. If it is missing
it is @False@ if it is in the query but does not have any value it is @True@.
Also it can have values @true/false@ in the query.
-}
newtype QueryFlag (sym :: Symbol) = QueryFlag Bool

{-| Argument of capture from the query.

> "api/route/{foo} if api/route/bar passed"  ==> (Capture bar) :: Capture "Foo" barType
-}
newtype Capture (sym :: Symbol) a = Capture a

{-| Reads value from the required header by name. For example if the request has header:

> "foo": "bar"

It reads the value:

> (Header bar) :: Header "foo" barType
-}
newtype Header (sym :: Symbol) a = Header a

{-| Reads value from the optional header by name. For example if the request has header:

> "foo": "bar"

It reads the value:

> (OptionalHeader (Just bar)) :: OptionalHeader "foo" barType
-}
newtype OptionalHeader (sym :: Symbol) a = OptionalHeader (Maybe a)

{-| Reads a cookie. It's an optional header with name "Cookie".
The cookie is URL-encoded and read with instnace of FromForm class.

> data MyCookie = MyCookie
>   { secret :: Text
>   , count :: Int
>   }
>   deriving (Generic, FromForm)
>
> > "secret=lolkek&count=101"
>
> (Cookie (Just (MyCookie { secret = "lolkek", count = 101 }))) :: Cookie MyCookie
-}
newtype Cookie a = Cookie (Maybe a)

{-| Reads current path info.

> "api/foo/bar" ==> PathInfo ["foo", "bar"]
-}
newtype PathInfo = PathInfo [Text]

{-| Reads current full-path info with queries.

> "api/foo/bar?param=value" ==> FullPathInfo "api/foo/bar?param=value"
-}
newtype FullPathInfo = FullPathInfo Text

-- | Read low-level request. Note that it does not affect the API schema
newtype RawRequest = RawRequest Request

-- | Reads info on weather the connection is secure (made over SSL).
newtype IsSecure = IsSecure Bool

-------------------------------------------------------------------------------------
-- outputs

{-| Route response type. It encodes the route method in the type
and which monad is used and which type the response has.

The repsonse value is usually one of two cases:

* @Resp media a@ -- for routes which always produce a value

* @RespOr media err a@ - for routes that can also produce an error or value.

See the class @IsResp@ for more details on response types.
-}
newtype Send method m a = Send {forall {k} {k} (method :: k) (m :: k -> *) (a :: k).
Send method m a -> m a
unSend :: m a}
  deriving newtype (forall k (method :: k) (m :: * -> *) a b.
Functor m =>
a -> Send method m b -> Send method m a
forall k (method :: k) (m :: * -> *) a b.
Functor m =>
(a -> b) -> Send method m a -> Send method m b
forall a b. a -> Send method m b -> Send method m a
forall a b. (a -> b) -> Send method m a -> Send method m b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> Send method m b -> Send method m a
$c<$ :: forall k (method :: k) (m :: * -> *) a b.
Functor m =>
a -> Send method m b -> Send method m a
fmap :: forall a b. (a -> b) -> Send method m a -> Send method m b
$cfmap :: forall k (method :: k) (m :: * -> *) a b.
Functor m =>
(a -> b) -> Send method m a -> Send method m b
Functor, forall a. a -> Send method m a
forall {k} {method :: k} {m :: * -> *}.
Applicative m =>
Functor (Send method m)
forall k (method :: k) (m :: * -> *) a.
Applicative m =>
a -> Send method m a
forall k (method :: k) (m :: * -> *) a b.
Applicative m =>
Send method m a -> Send method m b -> Send method m a
forall k (method :: k) (m :: * -> *) a b.
Applicative m =>
Send method m a -> Send method m b -> Send method m b
forall k (method :: k) (m :: * -> *) a b.
Applicative m =>
Send method m (a -> b) -> Send method m a -> Send method m b
forall k (method :: k) (m :: * -> *) a b c.
Applicative m =>
(a -> b -> c)
-> Send method m a -> Send method m b -> Send method m c
forall a b. Send method m a -> Send method m b -> Send method m a
forall a b. Send method m a -> Send method m b -> Send method m b
forall a b.
Send method m (a -> b) -> Send method m a -> Send method m b
forall a b c.
(a -> b -> c)
-> Send method m a -> Send method m b -> Send method m c
forall (f :: * -> *).
Functor f
-> (forall a. a -> f a)
-> (forall a b. f (a -> b) -> f a -> f b)
-> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
-> (forall a b. f a -> f b -> f b)
-> (forall a b. f a -> f b -> f a)
-> Applicative f
<* :: forall a b. Send method m a -> Send method m b -> Send method m a
$c<* :: forall k (method :: k) (m :: * -> *) a b.
Applicative m =>
Send method m a -> Send method m b -> Send method m a
*> :: forall a b. Send method m a -> Send method m b -> Send method m b
$c*> :: forall k (method :: k) (m :: * -> *) a b.
Applicative m =>
Send method m a -> Send method m b -> Send method m b
liftA2 :: forall a b c.
(a -> b -> c)
-> Send method m a -> Send method m b -> Send method m c
$cliftA2 :: forall k (method :: k) (m :: * -> *) a b c.
Applicative m =>
(a -> b -> c)
-> Send method m a -> Send method m b -> Send method m c
<*> :: forall a b.
Send method m (a -> b) -> Send method m a -> Send method m b
$c<*> :: forall k (method :: k) (m :: * -> *) a b.
Applicative m =>
Send method m (a -> b) -> Send method m a -> Send method m b
pure :: forall a. a -> Send method m a
$cpure :: forall k (method :: k) (m :: * -> *) a.
Applicative m =>
a -> Send method m a
Applicative, forall a. a -> Send method m a
forall {k} {method :: k} {m :: * -> *}.
Monad m =>
Applicative (Send method m)
forall k (method :: k) (m :: * -> *) a.
Monad m =>
a -> Send method m a
forall k (method :: k) (m :: * -> *) a b.
Monad m =>
Send method m a -> Send method m b -> Send method m b
forall k (method :: k) (m :: * -> *) a b.
Monad m =>
Send method m a -> (a -> Send method m b) -> Send method m b
forall a b. Send method m a -> Send method m b -> Send method m b
forall a b.
Send method m a -> (a -> Send method m b) -> Send method m b
forall (m :: * -> *).
Applicative m
-> (forall a b. m a -> (a -> m b) -> m b)
-> (forall a b. m a -> m b -> m b)
-> (forall a. a -> m a)
-> Monad m
return :: forall a. a -> Send method m a
$creturn :: forall k (method :: k) (m :: * -> *) a.
Monad m =>
a -> Send method m a
>> :: forall a b. Send method m a -> Send method m b -> Send method m b
$c>> :: forall k (method :: k) (m :: * -> *) a b.
Monad m =>
Send method m a -> Send method m b -> Send method m b
>>= :: forall a b.
Send method m a -> (a -> Send method m b) -> Send method m b
$c>>= :: forall k (method :: k) (m :: * -> *) a b.
Monad m =>
Send method m a -> (a -> Send method m b) -> Send method m b
Monad, forall a. IO a -> Send method m a
forall {k} {method :: k} {m :: * -> *}.
MonadIO m =>
Monad (Send method m)
forall k (method :: k) (m :: * -> *) a.
MonadIO m =>
IO a -> Send method m a
forall (m :: * -> *).
Monad m -> (forall a. IO a -> m a) -> MonadIO m
liftIO :: forall a. IO a -> Send method m a
$cliftIO :: forall k (method :: k) (m :: * -> *) a.
MonadIO m =>
IO a -> Send method m a
MonadIO)

instance MonadTrans (Send method) where
  lift :: forall (m :: * -> *) a. Monad m => m a -> Send method m a
lift = forall {k} {k} (method :: k) (m :: k -> *) (a :: k).
m a -> Send method m a
Send

-- | type-level GET-method tag
data GET

-- | type-level POST-method tag
data POST

-- | type-level PUT-method tag
data PUT

-- | type-level DELETE-method tag
data DELETE

-- | type-level OPTIONS-method tag
data OPTIONS

-- | type-level HEAD-method tag
data HEAD

-- | type-level PATCH-method tag
data PATCH

-- | type-level TRACE-method tag
data TRACE

-- | Get request
type Get m a = Send GET m a

-- | Post request
type Post m a = Send POST m a

-- | Put request
type Put m a = Send PUT m a

-- | Delete request
type Delete m a = Send DELETE m a

-- | Options request
type Options m a = Send OPTIONS m a

-- | Head request
type Head m a = Send HEAD m a

-- | Path request
type Patch m a = Send PATCH m a

-- | trace request
type Trace m a = Send TRACE m a

-- | Converts type-level tag for methods to value
class IsMethod a where
  toMethod :: Method

instance IsMethod GET where
  toMethod :: Method
toMethod = Method
methodGet

instance IsMethod POST where
  toMethod :: Method
toMethod = Method
methodPost

instance IsMethod PUT where
  toMethod :: Method
toMethod = Method
methodPut

instance IsMethod DELETE where
  toMethod :: Method
toMethod = Method
methodDelete

instance IsMethod OPTIONS where
  toMethod :: Method
toMethod = Method
methodOptions

instance IsMethod HEAD where
  toMethod :: Method
toMethod = Method
methodHead

instance IsMethod PATCH where
  toMethod :: Method
toMethod = Method
methodPatch

instance IsMethod TRACE where
  toMethod :: Method
toMethod = Method
methodTrace