{-|
Module      : Gargantext.API.Admin.Auth.Types
Description : Types for Server API Auth Module
Copyright   : (c) CNRS, 2017-Present
License     : AGPL + CECILL v3
Maintainer  : team@gargantext.org
Stability   : experimental
Portability : POSIX
-}

{-# LANGUAGE TemplateHaskell #-}

module Gargantext.API.Admin.Auth.Types
  ( -- * Types
    AuthRequest(..)
  , AuthResponse(..)
  , Token
  , TreeId
  , CheckAuth(..)
  , AuthenticatedUser(..)
  , AuthContext
  , AuthenticationError(..)
  , PathId(..)
  , Email
  , Password
  , ForgotPasswordRequest(..)
  , ForgotPasswordResponse(..)
  , ForgotPasswordAsyncParams(..)
  , ForgotPasswordGet(..)

  -- * Lenses
  , auth_node_id
  , auth_user_id
  , authRes_token
  , authRes_tree_id
  , authRes_user_id

  -- * Combinators
  ) where

import Crypto.JWT qualified as Jose
import Data.Aeson.TH qualified as JSON
import Data.Aeson.Types (genericParseJSON, defaultOptions, genericToJSON)
import Data.List (tail)
import Data.Swagger ( ToSchema(..), genericDeclareNamedSchema )
import Gargantext.Core.Types.Individu (Username, GargPassword(..), arbitraryUsername, arbitraryPassword)
import Gargantext.Core.Utils.Prefix (unPrefix, unPrefixSwagger)
import Gargantext.Database.Admin.Types.Node (NodeId(..), ListId, DocId, UserId (..))
import Gargantext.Prelude hiding (reverse)
import Servant.Auth.Server (CookieSettings, JWTSettings, ToJWT, FromJWT)
import Test.QuickCheck (elements)
import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)

---------------------------------------------------

-- | Main types for AUTH API
data AuthRequest = AuthRequest { _authReq_username :: Username
                               , _authReq_password :: GargPassword
                               }
  deriving (Generic)

data AuthResponse = AuthResponse { _authRes_token   :: Token
                                 , _authRes_tree_id :: TreeId
                                 , _authRes_user_id :: UserId
                                 }
  deriving (Generic, Eq, Show)

instance NFData AuthResponse where

type Token  = Text
type TreeId = NodeId

data CheckAuth = InvalidUser | InvalidPassword | Valid Token TreeId UserId
  deriving (Eq)

data AuthenticatedUser = AuthenticatedUser
  { _auth_node_id :: NodeId
  , _auth_user_id :: UserId
  } deriving (Generic, Show, Eq)

makeLenses ''AuthenticatedUser

instance ToSchema AuthenticatedUser where
  declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_authUser_")

data AuthenticationError
  = LoginFailed NodeId UserId Jose.Error
  | InvalidUsernameOrPassword
  | UserNotAuthorized UserId Text
  deriving (Show, Eq)

-- TODO-SECURITY why is the CookieSettings necessary?
type AuthContext = '[JWTSettings, CookieSettings] -- , BasicAuthCfg

-- | Instances
instance ToSchema AuthRequest where
  declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_authReq_")

instance Arbitrary AuthRequest where
  arbitrary = elements [ AuthRequest u p
                       | u <- arbitraryUsername
                       , p <- arbitraryPassword
                       ]

instance ToSchema AuthResponse where
  declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_authRes_")
instance Arbitrary AuthResponse where
  arbitrary = elements [ AuthResponse to' tr u
                       | to' <- ["token0", "token1"]
                       , tr <- map UnsafeMkNodeId [1..3]
                       , u <-  map UnsafeMkUserId [1..3]
                       ]

data PathId = PathNode NodeId | PathNodeNode ListId DocId


---------------------------

type Email = Text
type Password = Text

data ForgotPasswordRequest = ForgotPasswordRequest { _fpReq_email :: Email }
  deriving (Generic)
instance ToSchema ForgotPasswordRequest where
  declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_fpReq_")

data ForgotPasswordResponse = ForgotPasswordResponse { _fpRes_status :: Text }
  deriving (Generic)
instance ToSchema ForgotPasswordResponse where
  declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_fpRes_")

data ForgotPasswordGet = ForgotPasswordGet {_fpGet_password :: Password}
  deriving (Generic)
instance ToSchema ForgotPasswordGet where
  declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_fpGet_")

newtype ForgotPasswordAsyncParams =
  ForgotPasswordAsyncParams { email :: Text }
  deriving (Generic, Show, Eq)
instance FromJSON ForgotPasswordAsyncParams where
  parseJSON = genericParseJSON defaultOptions
instance ToJSON ForgotPasswordAsyncParams where
  toJSON = genericToJSON defaultOptions
instance ToSchema ForgotPasswordAsyncParams

--
-- Lenses
--

makeLenses ''AuthResponse

--
-- JSON instances
--

$(deriveJSON (JSON.defaultOptions { JSON.fieldLabelModifier = tail . dropWhile ((/=) '_') . tail }) ''AuthenticatedUser)
$(deriveJSON (unPrefix "_authReq_") ''AuthRequest)
$(deriveJSON (unPrefix "_authRes_") ''AuthResponse)
$(deriveJSON (unPrefix "_fpReq_") ''ForgotPasswordRequest)
$(deriveJSON (unPrefix "_fpRes_") ''ForgotPasswordResponse)
$(deriveJSON (unPrefix "_fpGet_") ''ForgotPasswordGet)

--
-- JWT instances
--

instance ToJWT AuthenticatedUser
instance FromJWT AuthenticatedUser
