import org.codehaus.groovy.runtime.StackTraceUtils
import groovy.xml.XmlSlurper

def getUserAGOVLoiRoles() {
  // set attibutes from DTO: -> AGOVaq
  def list = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
  return list.'**'.findAll { node -> node.name() == 'roles' && node.applicationName.text() == 'AGOV-Loi' }.collect({ node -> node.name.text() })
}

def getUserAGOVRecoveryRoles() {
  // set attibutes from DTO: -> AGOV
  def list = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
  return list.'**'.findAll { node -> node.name() == 'roles' && node.applicationName.text() == 'AGOV-AccountStatus' }.collect({ node -> node.name.text() })
}

def getUserAGOVLoiIdVerification() {
  // set attibutes from DTO: -> idVerification
  def list = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
  return list.'**'.findAll {node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text().contains('AGOV-Loi,')}.collect({ node -> node.value.text()})
}

def getUserAGOVLoiIdVerification(level) {
  // set attibutes from DTO: -> idVerification
  def list = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
  return list.'**'.findAll {node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text() == 'AGOV-Loi,level' + level}.collect({ node -> node.value.text()})
} 

def getUserAGOVLoiValidFrom(level) {
  // set attibutes from DTO: -> validFrom
  def payload = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
  return payload.'**'.find {node -> node.name() == 'authorizations' && node.role.name.text() == level}?.validFrom?.text()
}

def getUserAGOVLoiValidTo(level) {
  // set attibutes from DTO: -> validTo
  def payload = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
  return payload.'**'.find {node -> node.name() == 'authorizations' && node.role.name.text() == level}?.validTo?.text()
}

def getUserIdVerificationForRecovery() {
  // application is AGOV-AccountStatus
  def list = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
  def result = list.'**'.find {node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text() == 'AGOV-AccountStatus,mustRecover'}?.value?.text()

  if (!result) {
    // fallback if not explicitly set
    def currentLoaRole = getUserAGOVLoiRoles()?.sort()?.last() ?: 'level100'
    def chDomicile = list.country.text() == 'ch'
    def lastIdVerification = list.'**'.find {node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text() == 'AGOV-Loi,' + currentLoaRole}?.value?.text()
    switch (currentLoaRole) {
      case 'level100':
        result = chDomicile ? 'SimpleLetter' : 'Video'
        break
      case 'level200':
        result = chDomicile ? 'Bmid' : 'Video'
        break
      case 'level300':
      case 'level400':
        result = chDomicile ? lastIdVerification : 'Video'
        break
      default:
        LOG.warn("unexpected loa on account: ${currentLoaRole}")
        // safest default, should work in any case
        result = 'Video'
    }
    LOG.warn("Recovery method not set, choosing ${result} (based on currentLoad: ${currentLoaRole}, CH-domicile: ${chDomicile}, last verification method: ${lastIdVerification})")
  }
  return result
}

def getAqLevelBasedOnIdVerificationForRecovery(idVerification, highestRoleLevelNumber) {
  def result = 'urn:qa.agov.ch:names:tc:ac:classes:'

  switch (idVerification) {
    case 'None':
      result = result.concat('100')
      break
    case 'SimpleLetter':
      result = result.concat('200')
      break
    case 'Video':
    case 'VideoSelfPaid':
    case 'Bmid':
    case 'BmidSelfPaid':
    case 'Counter':
      result = result.concat((highestRoleLevelNumber == 400) ? '400' : '300')
      break
    default:
      LOG.warn("unexpected idVerification for recovery on account: ${idVerification}")
      // safest default, should work in any case
      result = result.concat('' + highestRoleLevelNumber)
  }

  return result
}

def getUserMustRecoverValidFrom() {
  // set attibutes from DTO: -> validFrom
  def payload = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
  def authzNode = payload.'**'.find {node -> node.name() == 'authorizations' && node.role.name.text() == 'mustRecover'}
  return (authzNode) ? ((authzNode.validFrom && !authzNode.validFrom.text().isEmpty()) ? authzNode.validFrom?.text() : authzNode.ctlCreDat?.text()) : ''
}

// Accounting
def requester = session['ch.nevis.auth.saml.request.scoping.requesterId'] ?: 'unknown'
def requestId = session['ch.nevis.auth.saml.request.id'] ?: 'unknown'
def requestedAq = session['agov.requestedRoleLevel'] ?: 'unknown'
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
def credentialType = session['authenticatedWith'] ?: 'unknown'
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'

try {
  // beef
  def session = request.getAuthSession(true)
  def highestRoleLevelNumber = 0
  def requestedRoleLevelNumber = session.get('agov.requestedRoleLevel').toInteger()
  def adressVerificationList = getUserAGOVLoiIdVerification('200')
  def adressVerification = 'None'
  if (adressVerificationList && !adressVerificationList.isEmpty()) {
    adressVerification = adressVerificationList[0]
  }

  LOG.debug('CheckLoa: Requested role level '+ requestedRoleLevelNumber)
  LOG.debug('CheckLoa: idVerification: ' + getUserAGOVLoiIdVerification())
  LOG.debug('CheckLoa: adressVerification : ' + adressVerification)

  def idVerificationMethodList = getUserAGOVLoiIdVerification()

  session.setAttribute('idVerification', idVerificationMethodList.isEmpty() ? 'None' : idVerificationMethodList.last())
  session.setAttribute('agov.adressVerification', '' + adressVerification)


  if (requestedRoleLevelNumber == 0) {
    // AuthnFailed_Zero_RoleLvl
    response.setResult('error');
    return
  }

  if (session.get('ch.adnovum.nevisidm.profileExtId') == '') {
    LOG.error("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', errorMessage='Account without Profile', SourceIp=${sourceIp}, UserAgent='${userAgent}'")

    session.setAttribute('contextClassRefToSet', 'urn:qa.agov.ch:names:tc:ac:classes:100')
    response.setResult('ok')
    return
  }

  // Transform sex to number
  if(session.get('ch.nevis.idm.User.gender') == 'MALE'){
          session.setAttribute('ch.nevis.idm.User.gender', '1')
  }
  if(session.get('ch.nevis.idm.User.gender') == 'FEMALE'){
          session.setAttribute('ch.nevis.idm.User.gender', '2')
  }
  if(session.get('ch.nevis.idm.User.gender') == 'OTHER'){
          session.setAttribute('ch.nevis.idm.User.gender', '3')
  }


  for (String role : getUserAGOVLoiRoles()) {
  if (role.startsWith('level')) {
    def roleLevel = role.substring(5)
    int roleLevelNumber = Integer.parseInt(roleLevel)
    if (highestRoleLevelNumber == 0) {
      highestRoleLevelNumber = roleLevelNumber
    }
    if (highestRoleLevelNumber< roleLevelNumber) { 
      highestRoleLevelNumber=roleLevelNumber 
    } 
  } 
  } 
  LOG.debug('CheckLoa: Highest role Level' + highestRoleLevelNumber.toString() +' contextclassref' + requestedRoleLevelNumber.toString()) 
  LOG.debug('CheckLoa: Compare' + (highestRoleLevelNumber>=requestedRoleLevelNumber)) 

  //set attribute Actual Role Level
  session.setAttribute('agov.actualRoleLevel', '' + highestRoleLevelNumber)
  LOG.debug('CheckLoa: actual role level (agov) '+ highestRoleLevelNumber)

  if (highestRoleLevelNumber > 0) {
    // set attribute contextClassRefToSet
    session.setAttribute('contextClassRefToSet','urn:qa.agov.ch:names:tc:ac:classes:' .concat(highestRoleLevelNumber.toString()))
  } else {
    // by default 100
    session.setAttribute('contextClassRefToSet','urn:qa.agov.ch:names:tc:ac:classes:100' ) 
  }

  // no login for users with a recovery role
  for (String role : getUserAGOVRecoveryRoles()) {
    if (role == 'mustRecover') {
      session.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:mustRecover')
      session.setAttribute('agov.recovery.authenticatedWith', session.getAttribute('authenticatedWith') ?: 'unknown' ) 

      def origIdVerification = getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString()) ?: 'None'

      def idVerification = getUserIdVerificationForRecovery() ?: origIdVerification
      session.setAttribute('agov.recovery.currentIdVerification', '' + idVerification )

      // align currentAgovAq with the method selected for idVerification
      def currentAgovAqForRecovery = getAqLevelBasedOnIdVerificationForRecovery(idVerification, highestRoleLevelNumber)
      session.setAttribute('agov.recovery.currentAgovAq', '' + currentAgovAqForRecovery)

      def validFrom = getUserMustRecoverValidFrom() ?: ''
      session.setAttribute('agov.recovery.currentAgovAqRoleValidFrom', '' + validFrom ) 

      LOG.debug("CheckLoa: mustRecover: origIdVerification=${origIdVerification}, idVerification=${idVerification}, currentAgovAqForRecovery=${currentAgovAqForRecovery}")

      response.setResult('exit.2')
      return

    } else if (role == 'recovery') {
      session.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:recovery')
      session.setAttribute('agov.recovery.authenticatedWith', session.getAttribute('authenticatedWith') ?: 'unknown') 
      session.setAttribute('agov.recovery.currentAgovAq', session.getAttribute('contextClassRefToSet') ?: 'urn:qa.agov.ch:names:tc:ac:classes:100' )
      LOG.debug('CheckLoa: idVerification2= '+ getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString()))
      def idVerification = getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString())
      session.setAttribute('agov.recovery.currentIdVerification', (idVerification.isEmpty() ? 'None' : idVerification.first()))
      def validFrom = getUserAGOVLoiValidFrom('level'.concat(highestRoleLevelNumber.toString())) ?: ''
      session.setAttribute('agov.recovery.currentAgovAqRoleValidFrom', validFrom) 

      response.setResult('exit.2')
      return
    } 
  }

  if (highestRoleLevelNumber>=requestedRoleLevelNumber) { 

    // set attribute ValidFrom and ValidTo (only for higher than 100)
    if (highestRoleLevelNumber > 100) {
      def validFrom = getUserAGOVLoiValidFrom('level'.concat(highestRoleLevelNumber.toString()))
      def validTo = getUserAGOVLoiValidTo('level'.concat(highestRoleLevelNumber.toString()))

      LOG.debug('CheckLoa: ValidFrom :' + validFrom)
      LOG.debug('CheckLoa: ValidTo :' + validTo)

      if(validFrom != '') {
        session.setAttribute('ValidFrom', '' + validFrom) 
      }
      if(validTo != '') {
        session.setAttribute('ValidTo', '' + validTo) 
      }
    }
    response.setResult('ok')
    return; 
  } else { 
    // Insufficient_LoaInfo
    response.setResult('exit.1'); 
    return; 
  }
} catch (Exception ex) {
  LOG.error("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', errorMessage='exception occured: ${ex}', SourceIp=${sourceIp}, UserAgent='${userAgent}'")
  ex = StackTraceUtils.sanitize(ex)
  def affectedLines = ex.stackTrace.findAll { it.className.startsWith('Script') }.collect { "${it.methodName}:${it.lineNumber}" }
  LOG.error("FATAL: Script failure (at lines: ${affectedLines})", ex)
  // AuthnFailed_Zero_RoleLvl
  response.setResult('error');
  return;
}