<commit_before>/*
 * Copyright 2015 HM Revenue & Customs
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package controllers

import auth.RouterAuthenticationProvider
import config.FrontendAuditConnector
import connector.FrontendAuthConnector
import engine.{Condition, Rule, RuleEngine}
import model.Location._
import model._
import org.joda.time.{DateTime, Seconds}
import play.api.Play.current
import play.api.mvc._
import play.api.{Logger, Play}
import services._
import uk.gov.hmrc.play.audit.http.connector.AuditConnector
import uk.gov.hmrc.play.frontend.auth._
import uk.gov.hmrc.play.frontend.auth.connectors.AuthConnector
import uk.gov.hmrc.play.frontend.controller.FrontendController
import uk.gov.hmrc.play.http.HeaderCarrier

import scala.concurrent.Future

object RouterController extends RouterController {
  override protected def authConnector: AuthConnector = FrontendAuthConnector

  override val defaultLocation: LocationType = BusinessTaxAccount

  override val metricsMonitoringService: MetricsMonitoringService = MetricsMonitoringService

  override val ruleEngine: RuleEngine = TarRules

  override def throttlingService: ThrottlingService = ThrottlingService

  override def auditConnector: AuditConnector = FrontendAuditConnector

  override def createAuditContext(): TAuditContext = AuditContext()
}

trait RouterController extends FrontendController with Actions {

  val longLiveCookieExpirationTime = Play.configuration.getString("sticky-routing.long-live-cookie-expiration-time")
  val shortLiveCookieDuration = Play.configuration.getInt("sticky-routing.short-live-cookie-duration")
  val stickyRoutingEnabled = Play.configuration.getBoolean("sticky-routing.enabled").getOrElse(false)

  val cookieMaxAge = Map(
    (PersonalTaxAccount, PersonalTaxAccount) -> longLiveCookieExpirationTime.map(t => Instant(DateTime.parse(t))),
    (BusinessTaxAccount, BusinessTaxAccount) -> shortLiveCookieDuration.map(t => Duration(t)),
    (PersonalTaxAccount, BusinessTaxAccount) -> shortLiveCookieDuration.map(t => Duration(t))
  )

  val cookieValues = Map(
    PersonalTaxAccount -> PersonalTaxAccount,
    BusinessTaxAccount -> BusinessTaxAccount,
    WelcomeBTA -> BusinessTaxAccount,
    WelcomePTA -> PersonalTaxAccount
  )

  val metricsMonitoringService: MetricsMonitoringService

  def defaultLocation: LocationType

  def ruleEngine: RuleEngine

  def throttlingService: ThrottlingService

  def auditConnector: AuditConnector

  def createAuditContext(): TAuditContext

  val account = AuthenticatedBy(authenticationProvider = RouterAuthenticationProvider, pageVisibility = AllowAll).async { implicit user => request => route(user, request) }

  def route(implicit authContext: AuthContext, request: Request[AnyContent]): Future[Result] = {

    val ruleContext = RuleContext(authContext)

    val auditContext: TAuditContext = createAuditContext()

    val nextLocation = ruleEngine.getLocation(authContext, ruleContext, auditContext)

    nextLocation.map(locationCandidate => {
      val location = locationCandidate.getOrElse(defaultLocation)

      val routingCookie: Option[Cookie] = request.cookies.get(CookieNames.mdtpRouting)
      val routingCookieValues: Option[RoutingCookieValues] = routingCookie.map(cookie => RoutingCookieValues(cookie.value))
      val finalDestination = routingCookieValues match {
        case Some(cookie) if Location.locations.get(cookie.routedDestination).contains(location) && stickyRoutingEnabled =>
          Location.locations.get(cookie.throttledDestination).get
        case _ =>
          throttlingService.throttle(location, auditContext)
      }

      Logger.debug(s"routing to: ${finalDestination.name}")
      sendAuditEvent(auditContext, finalDestination)
      metricsMonitoringService.sendMonitoringEvents(auditContext, finalDestination)

      if (stickyRoutingEnabled) {
        val maxAge = for {
          routedDestination <- cookieValues.get(location)
          throttledDestination <- cookieValues.get(finalDestination)
          maxAge <- cookieMaxAge.get((routedDestination, throttledDestination)).flatMap(v => v)
        } yield maxAge.getMaxAge

        Redirect(finalDestination.url).withCookies(
          Cookie(CookieNames.mdtpRouting, RoutingCookieValues(location.name, finalDestination.name).toString, maxAge = maxAge.fold(Some(0))(v => Some(v)))
        )
      } else {
        Redirect(finalDestination.url).discardingCookies(DiscardingCookie(CookieNames.mdtpRouting))
      }
    })
  }

  def sendAuditEvent(auditContext: TAuditContext, throttledLocation: LocationType)(implicit authContext: AuthContext, request: Request[AnyContent], hc: HeaderCarrier): Unit = {
    auditContext.toAuditEvent(throttledLocation).foreach { auditEvent =>
      auditConnector.sendEvent(auditEvent)
      Logger.debug(s"Routing decision summary: ${auditEvent.detail \ "reasons"}")
    }
  }
}

object TarRules extends RuleEngine {

  import Condition._

  private val shouldShowWelcomePage: Condition = LoggedInForTheFirstTime and HasNeverSeenWelcomeBefore

  override val rules: List[Rule] = List(
    when(LoggedInViaVerify) thenGoTo PersonalTaxAccount withName "pta-home-page-for-verify-user",
    when(shouldShowWelcomePage and (LoggedInViaGovernmentGateway and HasAnyBusinessEnrolment)) thenGoTo WelcomeBTA withName "bta-welcome-page-for-user-with-business-enrolments",
    when(LoggedInViaGovernmentGateway and HasAnyBusinessEnrolment) thenGoTo BusinessTaxAccount withName "bta-home-page-for-user-with-business-enrolments",

    when(shouldShowWelcomePage and (LoggedInViaGovernmentGateway and HasSelfAssessmentEnrolments and not(HasPreviousReturns))) thenGoTo WelcomeBTA withName "bta-welcome-page-for-user-with-no-previous-return",
    when(LoggedInViaGovernmentGateway and HasSelfAssessmentEnrolments and not(HasPreviousReturns)) thenGoTo BusinessTaxAccount withName "bta-home-page-for-user-with-no-previous-return",
<commit_msg>[CG|PB] TAR-25 Refactored RouterController by extracting separate methods to determine the final destination and to return the redirect.<commit_after>/*
 * Copyright 2015 HM Revenue & Customs
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package controllers

import auth.RouterAuthenticationProvider
import config.FrontendAuditConnector
import connector.FrontendAuthConnector
import engine.{Condition, Rule, RuleEngine}
import model.Location._
import model._
import org.joda.time.{DateTime, Seconds}
import play.api.Play.current
import play.api.mvc._
import play.api.{Logger, Play}
import services._
import uk.gov.hmrc.play.audit.http.connector.AuditConnector
import uk.gov.hmrc.play.frontend.auth._
import uk.gov.hmrc.play.frontend.auth.connectors.AuthConnector
import uk.gov.hmrc.play.frontend.controller.FrontendController
import uk.gov.hmrc.play.http.HeaderCarrier

import scala.concurrent.Future

object RouterController extends RouterController {
  override protected def authConnector: AuthConnector = FrontendAuthConnector

  override val defaultLocation: LocationType = BusinessTaxAccount

  override val metricsMonitoringService: MetricsMonitoringService = MetricsMonitoringService

  override val ruleEngine: RuleEngine = TarRules

  override def throttlingService: ThrottlingService = ThrottlingService

  override def auditConnector: AuditConnector = FrontendAuditConnector

  override def createAuditContext(): TAuditContext = AuditContext()
}

trait RouterController extends FrontendController with Actions {

  val longLiveCookieExpirationTime = Play.configuration.getString("sticky-routing.long-live-cookie-expiration-time")
  val shortLiveCookieDuration = Play.configuration.getInt("sticky-routing.short-live-cookie-duration")
  val stickyRoutingEnabled = Play.configuration.getBoolean("sticky-routing.enabled").getOrElse(false)

  val cookieMaxAge = Map(
    (PersonalTaxAccount, PersonalTaxAccount) -> longLiveCookieExpirationTime.map(t => Instant(DateTime.parse(t))),
    (BusinessTaxAccount, BusinessTaxAccount) -> shortLiveCookieDuration.map(t => Duration(t)),
    (PersonalTaxAccount, BusinessTaxAccount) -> shortLiveCookieDuration.map(t => Duration(t))
  )

  val cookieValues = Map(
    PersonalTaxAccount -> PersonalTaxAccount,
    BusinessTaxAccount -> BusinessTaxAccount,
    WelcomeBTA -> BusinessTaxAccount,
    WelcomePTA -> PersonalTaxAccount
  )

  val metricsMonitoringService: MetricsMonitoringService

  def defaultLocation: LocationType

  def ruleEngine: RuleEngine

  def throttlingService: ThrottlingService

  def auditConnector: AuditConnector

  def createAuditContext(): TAuditContext

  val account = AuthenticatedBy(authenticationProvider = RouterAuthenticationProvider, pageVisibility = AllowAll).async { implicit user => request => route(user, request) }

  def route(implicit authContext: AuthContext, request: Request[AnyContent]): Future[Result] = {

    val ruleContext = RuleContext(authContext)

    val auditContext: TAuditContext = createAuditContext()

    val nextLocation = ruleEngine.getLocation(authContext, ruleContext, auditContext)

    nextLocation.map(locationCandidate => {

      val location = locationCandidate.getOrElse(defaultLocation)
      val finalDestination = getFinalDestination(auditContext, location)

      Logger.debug(s"routing to: ${finalDestination.name}")
      sendAuditEvent(auditContext, finalDestination)
      metricsMonitoringService.sendMonitoringEvents(auditContext, finalDestination)

      returnRedirect(location, finalDestination)
    })
  }

  def returnRedirect(location: LocationType, finalDestination: LocationType): Result = {
    if (stickyRoutingEnabled) {
      val maxAge = for {
        routedDestination <- cookieValues.get(location)
        throttledDestination <- cookieValues.get(finalDestination)
        maxAge <- cookieMaxAge.get((routedDestination, throttledDestination)).flatMap(identity)
      } yield maxAge.getMaxAge

      Redirect(finalDestination.url).withCookies(
        Cookie(CookieNames.mdtpRouting, RoutingCookieValues(location.name, finalDestination.name).toString, maxAge = maxAge.fold(Some(0))(v => Some(v)))
      )
    } else {
      Redirect(finalDestination.url).discardingCookies(DiscardingCookie(CookieNames.mdtpRouting))
    }
  }

  def getFinalDestination(auditContext: TAuditContext, location: LocationType)(implicit request: Request[AnyContent]): LocationType = {
    val routingCookie = request.cookies.get(CookieNames.mdtpRouting)
    val routingCookieValues = routingCookie.map(cookie => RoutingCookieValues(cookie.value))
    val finalDestination = routingCookieValues match {
      case Some(cookie) if Location.locations.get(cookie.routedDestination).contains(location) && stickyRoutingEnabled =>
        Location.locations.get(cookie.throttledDestination).get
      case _ =>
        throttlingService.throttle(location, auditContext)
    }
    finalDestination
  }

  def sendAuditEvent(auditContext: TAuditContext, throttledLocation: LocationType)(implicit authContext: AuthContext, request: Request[AnyContent], hc: HeaderCarrier): Unit = {
    auditContext.toAuditEvent(throttledLocation).foreach { auditEvent =>
      auditConnector.sendEvent(auditEvent)
      Logger.debug(s"Routing decision summary: ${auditEvent.detail \ "reasons"}")
    }
  }
}

object TarRules extends RuleEngine {

  import Condition._

  private val shouldShowWelcomePage: Condition = LoggedInForTheFirstTime and HasNeverSeenWelcomeBefore

  override val rules: List[Rule] = List(
    when(LoggedInViaVerify) thenGoTo PersonalTaxAccount withName "pta-home-page-for-verify-user",
    when(shouldShowWelcomePage and (LoggedInViaGovernmentGateway and HasAnyBusinessEnrolment)) thenGoTo WelcomeBTA withName "bta-welcome-page-for-user-with-business-enrolments",
    when(LoggedInViaGovernmentGateway and HasAnyBusinessEnrolment) thenGoTo BusinessTaxAccount withName "bta-home-page-for-user-with-business-enrolments",

    when(shouldShowWelcomePage and (LoggedInViaGovernmentGateway and HasSelfAssessmentEnrolments and not(HasPreviousReturns))) thenGoTo WelcomeBTA withName "bta-welcome-page-for-user-with-no-previous-return",
    when(LoggedInViaGovernmentGateway and HasSelfAssessmentEnrolments and not(HasPreviousReturns)) thenGoTo BusinessTaxAccount withName "bta-home-page-for-user-with-no-previous-return",

{'fraction_non_alphanumeric': 0.08358411074330424, 'fraction_numerical': 0.001504664459825459, 'mean_word_length': 5.294034090909091, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 40, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '656580', 'n_tokens_mistral': 3710, 'n_tokens_neox': 3618, 'n_words': 813}