{-# LANGUAGE OverloadedStrings #-}
module Clckwrks.Authenticate.Route where

import Control.Applicative         ((<$>))
import Clckwrks.Monad              (ClckT(..), plugins)
import Clckwrks.Authenticate.URL   (AuthURL(..))
import Clckwrks.Authenticate.Page.AuthModes (authModesPage)
import Clckwrks.Authenticate.Page.Login (loginPage)
import Clckwrks.Authenticate.Page.ChangePassword (changePasswordPanel)
import Clckwrks.Authenticate.Page.ResetPassword  (resetPasswordPage)
import Clckwrks.Authenticate.Page.OpenIdRealm    (openIdRealmPanel)
import Clckwrks.ProfileData.API    (Role(..), requiresRole_)
import Clckwrks.URL                (ClckURL)
-- import Clckwrks.Plugin             (clckPlugin)
import Control.Monad.State         (get)
import Control.Monad.Trans         (lift)
import qualified Data.Set          as Set
import Happstack.Authenticate.Core (AuthenticateURL)
import Happstack.Server            (Happstack, Response, ServerPartT)
import Web.Routes                  (RouteT(..), askRouteFn, runRouteT)
import Web.Plugins.Core            (getPluginRouteFn, pluginName)

-- | routeAuth
-- there is much craziness here. This should be more like clckwrks-plugin-page or something
routeAuth :: (AuthenticateURL -> RouteT AuthenticateURL (ServerPartT IO) Response)
          -> AuthURL
          -> ClckT AuthURL (ServerPartT IO) Response
routeAuth routeAuthenticate u' =
  do u <- checkAuth u'
     p <- plugins <$> get
     ~(Just clckShowFn) <- getPluginRouteFn p "clck" -- (pluginName clckPlugin) -- a mildly dangerous hack to avoid circular depends
     let withClckURL m =  ClckT $ RouteT $ \_ -> unRouteT (unClckT m) clckShowFn
     case u of
       (Auth authenticateURL) ->
         do p <- plugins <$> get
            ~(Just authShowFn) <- getPluginRouteFn p "authenticate"
            lift $ runRouteT routeAuthenticate (authShowFn . Auth) authenticateURL
       Login          -> withClckURL loginPage
       ResetPassword  -> withClckURL resetPasswordPage
       ChangePassword -> withClckURL changePasswordPanel
       OpenIdRealm    -> withClckURL openIdRealmPanel
       AuthModes      -> authModesPage u

checkAuth :: (Happstack m, Monad m) =>
             AuthURL
          -> ClckT AuthURL m AuthURL
checkAuth url =
  do p <- plugins <$> get
     ~(Just clckShowFn) <- getPluginRouteFn p "clck" -- (pluginName clckPlugin) -- a mildly dangerous hack to avoid circular depends
     let requiresRole = requiresRole_ clckShowFn
     case url of
       (Auth {})      -> pure url
       Login          -> pure url
       ResetPassword  -> pure url
       AuthModes      -> requiresRole (Set.fromList [Administrator]) url
       ChangePassword -> requiresRole (Set.fromList [Visitor]) url
       OpenIdRealm    -> requiresRole (Set.fromList [Administrator]) url