module Model.Tokens.Session
  ( SessionToken(..)
  , _sessionIdentifier, _sessionId, _sessionIssuedBy, _sessionIssuedAt, _sessionExpiresAt, _sessionStartsAt
  ) where

import ClassyPrelude.Yesod

import Model.Tokens.Lens
import Model
import Utils.Lens

import Web.ServerSession.Core

import Jose.Jwt (IntDate(..))
import qualified Jose.Jwt as Jose

import Data.Time.Clock.POSIX

import Control.Monad.Fail


data SessionToken sess = SessionToken
  { sessionIdentifier :: TokenId
  , sessionId         :: SessionId sess
  , sessionIssuedAt   :: UTCTime
  , sessionIssuedBy   :: InstanceId
  , sessionIssuedFor  :: ClusterId
  , sessionExpiresAt
  , sessionStartsAt   :: Maybe UTCTime
  } deriving (Eq, Ord, Read, Show, Generic, Typeable)

makeLenses_ ''SessionToken
instance HasTokenIdentifier (SessionToken sess) TokenId where
  _tokenIdentifier = _sessionIdentifier
instance HasTokenIssuedBy (SessionToken sess) InstanceId where
  _tokenIssuedBy = _sessionIssuedBy
instance HasTokenIssuedFor (SessionToken sess) ClusterId where
  _tokenIssuedFor = _sessionIssuedFor
instance HasTokenIssuedAt (SessionToken sess) UTCTime where
  _tokenIssuedAt = _sessionIssuedAt
instance HasTokenExpiresAt (SessionToken sess) (Maybe UTCTime) where
  _tokenExpiresAt = _sessionExpiresAt
instance HasTokenStartsAt (SessionToken sess) (Maybe UTCTime) where
  _tokenStartsAt = _sessionStartsAt

instance ToJSON (SessionToken sess) where
  toJSON SessionToken{..} = toJSON Jose.JwtClaims{..}
    where jwtIss = Just $ toPathPiece sessionIssuedBy
          jwtSub = Just $ toPathPiece sessionId
          jwtAud = Just . pure $ toPathPiece sessionIssuedFor
          jwtExp = IntDate . utcTimeToPOSIXSeconds <$> sessionExpiresAt
          jwtNbf = IntDate . utcTimeToPOSIXSeconds <$> sessionStartsAt
          jwtIat = Just . IntDate $ utcTimeToPOSIXSeconds sessionIssuedAt
          jwtJti = Just $ toPathPiece sessionIdentifier

instance FromJSON (SessionToken sess) where
  parseJSON val = do
    Jose.JwtClaims{..} <- parseJSON val

    sessionIdentifier <- parseMaybe "sessionIdentfier" $
      fromPathPiece =<< jwtIss
    sessionId <- parseMaybe "sessionId" $
      fromPathPiece =<< jwtSub
    sessionIssuedAt <- parseMaybe "sessionIssuedAt" $
      unIntDate <$> jwtIat
    sessionIssuedBy <- parseMaybe "sessionIssuedBy" $
      fromPathPiece =<< jwtIss
    sessionIssuedFor <- parseMaybe "sessionIssuedFor" $ do
      [aud] <- jwtAud
      fromPathPiece aud
    let sessionExpiresAt = unIntDate <$> jwtExp
        sessionStartsAt = unIntDate <$> jwtNbf

    return SessionToken{..}
    where
      parseMaybe errId = maybe (fail $ "Could not parse " <> errId) return
      unIntDate (IntDate posix) = posixSecondsToUTCTime posix
