{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards   #-}

module Micro.Gateway.Types
  ( App (..)
  , AppKey (..)
  , AppSecret (..)
  , Domain (..)
  , newApp

  , Provider (..)
  , newProvider
  ) where

import           Data.Aeson           (FromJSON (..), ToJSON (..), withText)
import           Data.ByteString      (ByteString)
import qualified Data.ByteString.Lazy as LB (ByteString)
import           Data.Int             (Int64)
import           Data.String          (IsString (..))
import qualified Data.Text.Lazy       as LT (Text, fromStrict, null, unpack)
import qualified Network.HTTP.Client  as HTTP
import           Network.Wai          (Request (..))

newtype AppKey = AppKey {unAppKey :: LT.Text}
  deriving (Eq)

instance Show AppKey where
  show = LT.unpack . unAppKey

instance IsString AppKey where
  fromString = AppKey . fromString

instance FromJSON AppKey where
  parseJSON = withText "AppKey" $ pure . AppKey . LT.fromStrict

instance ToJSON AppKey where
  toJSON (AppKey k) = toJSON k

newtype AppSecret = AppSecret {unAppSecret :: LT.Text}

instance Show AppSecret where
  show = LT.unpack . unAppSecret

instance IsString AppSecret where
  fromString = AppSecret . fromString

instance FromJSON AppSecret where
  parseJSON = withText "AppSecret" $ pure . AppSecret . LT.fromStrict

instance ToJSON AppSecret where
  toJSON (AppSecret s) = toJSON s

newtype Domain = Domain {unDomain :: LT.Text}

instance Show Domain where
  show = LT.unpack . unDomain

instance IsString Domain where
  fromString = Domain . fromString

instance FromJSON Domain where
  parseJSON = withText "Domain" $ pure . Domain . LT.fromStrict

instance ToJSON Domain where
  toJSON (Domain d) = toJSON d

data App = App
  { appKey         :: AppKey
  , appSecret      :: AppSecret
  , isKeyOnPath    :: Bool
  , isSecure       :: Bool
  , onlyProxy      :: Bool
  , doRequest      :: (HTTP.Request -> HTTP.Manager -> IO (HTTP.Response LB.ByteString))
                   -> String -> IO (HTTP.Response LB.ByteString)
  , beforeRequest  :: Maybe String -> Request -> IO (Either String ())
  -- beforeRequest retryError req
  , afterRequest   :: Int64 -> Int -> IO ()
  -- afterRequest contentLength statusCode
  , onErrorRequest :: IO ()
  , maxRetry       :: Int
  -- set the max retry on bad gateway error
  , retryError     :: Maybe String
  , prepareWsRequest :: (String -> Int -> IO ()) -> IO ()
  --
  , replaceKeyPages :: [LT.Text]
  , replaceKeyName :: ByteString

  -- allow page prefix
  , allowPages :: [LT.Text]
  }


newApp :: AppKey -> AppSecret -> Bool -> Bool -> App
newApp appKey appSecret isSecure onlyProxy = App
  { isKeyOnPath = False
  , doRequest = error "no implement"
  , beforeRequest = \_ _ -> pure $ Right ()
  , afterRequest = \_ _ -> pure ()
  , onErrorRequest = pure ()
  , maxRetry = 3
  , retryError = Nothing
  , prepareWsRequest = error "no implement"
  , replaceKeyPages = []
  , replaceKeyName = "__KEY__"
  , allowPages = []
  , ..
  }


data Provider = Provider
  { getAppByKey    :: AppKey -> IO (Maybe App)
  , getAppByDomain :: Domain -> IO (Maybe App)
  , isValidDomain  :: Domain -> IO Bool
  , isValidKey     :: AppKey -> IO Bool
  }

notNull :: AppKey -> Bool
notNull (AppKey k) = not $ LT.null k

newProvider :: Provider
newProvider = Provider
  { getAppByKey    = const $ pure Nothing
  , getAppByDomain = const $ pure Nothing
  , isValidDomain  = return . const False
  , isValidKey     = return . notNull
  }