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


// AGOVaq conversion
def maxLoiRoleToCtxClssConvertorMap = [
    "level100": "urn:qa.agov.ch:names:tc:ac:classes:100",
    "level200": "urn:qa.agov.ch:names:tc:ac:classes:200",
    "level300": "urn:qa.agov.ch:names:tc:ac:classes:300",
    "level400": "urn:qa.agov.ch:names:tc:ac:classes:400",
    "level500": "urn:qa.agov.ch:names:tc:ac:classes:500"
]

def getUserIdVerificationForRecovery(currentLoaRole) {
  // 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 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() ?: 'missing'
    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, highestRoleLevel) {
  def result = 'level'

  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((highestRoleLevel == 'level400') ? '400' : '300')
      break
    default:
      LOG.warn("unexpected idVerification for recovery on account: ${idVerification}")
      // safest default, should work in any case
      result = highestRoleLevel
  }

  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()) : ''
}


// for autditing
def user = session['ch.adnovum.nevisidm.user.extId'] ?: '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'
def maxLoi = null


// new
if (session['ch.adnovum.nevisidm.userDto'] != null && notes['lasterror'] == null) {
  try {
    def userDto = new XmlSlurper().parseText(session['ch.adnovum.nevisidm.userDto'])
    def userState = userDto.state
    LOG.debug("Recovery: Dto is '${userDto}")
    LOG.debug("Recovery: state is '${userState}")
    def session = request.getAuthSession(true)

    if (userState == 'ACTIVE') {

      session.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:recovery')

      def maxLoiList =   userDto.'**'.findAll { node -> node.name() == 'roles' && node.applicationName.text() == 'AGOV-Loi' }.collect({ node -> node.name.text() })
      maxLoi = (maxLoiList == null || maxLoiList.isEmpty()) ? null : maxLoiList.sort().last()

      def idVerification = null
      def agovAqValidFrom = null
      if (maxLoi) {
        idVerification = userDto.'**'.find { node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text() == 'AGOV-Loi,' + maxLoi}?.value?.text()
        idVerification = idVerification ?: 'None'
        agovAqValidFrom = userDto.'**'.find { node -> node.name() == 'authorizations' && node.role.name.text() == maxLoi}?.validFrom?.text()
        agovAqValidFrom = agovAqValidFrom?: userDto.'**'.find { node -> node.name() == 'authorizations' && node.role.name.text() == maxLoi}?.ctlCreDat?.text()
      }

      def mustRecover =   userDto.'**'.find { node -> node.name() == 'roles' && node.applicationName.text() == 'AGOV-AccountStatus' && node.name.text() == 'mustRecover' }

      def hasRecoveryRole = userDto.'**'.find { node -> node.name() == 'roles' && node.applicationName.text() == 'AGOV-AccountStatus' && node.name.text() == 'recovery' }


      if (mustRecover) {
        // attributes are defined over the mustRecover authorization
        session.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:mustRecover')

        idVerification = getUserIdVerificationForRecovery(maxLoi ?: 'level100') ?: idVerification

        agovAqValidFrom = getUserMustRecoverValidFrom()

        maxLoi = getAqLevelBasedOnIdVerificationForRecovery(idVerification, maxLoi)
      }

      LOG.debug("Recovery: MaxLoi is '${maxLoi}'")
      LOG.debug("Recovery: IdVerification is  ${idVerification}")
      LOG.debug("Recovery: agovAqValidFrom is ${agovAqValidFrom}")
      LOG.debug("Recovery: mustRecover is '${mustRecover}'")
      LOG.debug("Recovery: hasRecoveryRole is '${hasRecoveryRole}'")

      if (maxLoi != null) {       
        if (maxLoiRoleToCtxClssConvertorMap.containsKey(maxLoi)) {
          LOG.debug("Recovery: MaxLoiMapping is " + maxLoiRoleToCtxClssConvertorMap[maxLoi])
          response.setSessionAttribute('agov.recovery.currentAgovAq', '' + maxLoiRoleToCtxClssConvertorMap[maxLoi])
          response.setSessionAttribute('agov.recovery.currentIdVerification', '' + idVerification)
          response.setSessionAttribute('agov.recovery.currentAgovAqRoleValidFrom', '' + agovAqValidFrom)

          if ((maxLoi == 'level100') && (mustRecover == null)) {
            // mustRecover role not set, so code needs to be checked
            LOG.debug("Recovery: emailAndCode")
            response.setSessionAttribute('agov.recovery.authenticatedWith', 'urn:qa.agov.ch:names:tc:authfactor:emailAndCode')
            response.setResult('needCode')
            return
          } else {
            LOG.debug("Recovery: email")
            response.setSessionAttribute('agov.recovery.authenticatedWith', 'urn:qa.agov.ch:names:tc:authfactor:email')
            response.setResult('ok')
            return
          }

        } else {
          LOG.error("Recovery: Failed to convert '${maxLoi}' to AGOVaq")
          response.setResult('error')
          return
        }
      } else {
        // maxLoi is null
        LOG.debug("Recovery: no 'AGOV-Loi'-role assigned to user ${user}")
        if ((hasRecoveryRole != null) && (mustRecover == null)) {
          response.setResult('notFullyRegistered')
          return
        } else {
          LOG.error("Recovery: no 'AGOV-Loi'-role assigned to user ${user} and no recovery role ")
          response.setResult('error')
          return
        }
      }
    } else {
      // state != ACTIVE and no lasterror should not happen
      LOG.error("Recovery: state='${userState}' but not lasterror set")
      response.setNote('lasterror', '9909')
      response.setNote('lasterrorinfo', 'internal error')
      response.setResult('error')
      return
    }
  } catch (Exception e) {
    e = StackTraceUtils.sanitize(e)
    def affectedLines = e.stackTrace.findAll { it.className.startsWith('Script') }.collect { "${it.methodName}:${it.lineNumber}" }
    LOG.error("FATAL: Recovery processing failed (at lines: ${affectedLines})", e)
    response.setNote('lasterror', '9909')
    response.setNote('lasterrorinfo', 'internal error')
    response.setResult('error')
    return
  }
}

LOG.error("Recovery: userDto missing or failure before (lasterror='${notes.getProperty('lasterror', '-')}')")
response.setNote('lasterror', '9909')
response.setNote('lasterrorinfo', 'internal error')
response.setResult('error')
return