/*
 * Copyright (C) 2020  Prasoon Joshi
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package graphql

import repository
  .{
  DataRepository,
  UserRepository,
  RepositoryComponent}
import sangria.execution.ExceptionHandler
import configuration.AuthenticationException
import sangria.execution.HandledException
import configuration.AuthorizationException
import models.{
  NiosxUser, AuthPayload
}

/*
Data and utils needed to fulfil the GraphQL queries.
 */

object ABagOfGoodies {
  val errorHandler = ExceptionHandler {
    case (m, AuthenticationException(message)) => HandledException(message)
    case (m, AuthorizationException(message)) => HandledException(message)
  }
}

case class GraphqlContext(dataRepository: DataRepository)

case class SecureGraphqlContext(
  token: Option[String],
  userRepository: UserRepository,
  dataRepository: DataRepository) {

    /**
     * Returns a bearer token that can be used to perform actions
     * that the user is allowed.
     *
     * @param username Usernames are unique
     * @param password Passwords are not stored in plaintext
     * @return         A String bearer token wrapped inside `AuthPayload` 
     */
    def login(username: String, password: String): Option[AuthPayload] = {
    
      userRepository
        .authenticate(username, password)
//        .getOrElse(
//          throw new AuthenticationException("Username or password is incorrect.")
//        )
    }

    def authorized[T](permissions: String*)(fn: NiosxUser => T) =
      token.flatMap(userRepository.authorise).fold(
        throw AuthorizationException("Invalid token")){ user => 
          if(permissions.forall(user.permissions.contains)) fn(user)
          else throw AuthorizationException("Operation not permitted.")
        }

    def ensurePermissions(permissions: List[String]): Unit =
      token.flatMap(userRepository.authorise).fold(
        throw AuthorizationException("Invalid token.")){ user =>
          if(!permissions.forall(user.permissions.contains))
            throw AuthorizationException("Operation not permitted.")
        }

    def user =
      token
        .flatMap(userRepository.authorise)
        .fold(throw AuthorizationException("Invalid token."))(identity)
  }

/*
trait ContextComponent {
  this: RepositoryComponent =>

  val context: GraphQLContext

}
*/
