package com.ustadmobile.core.db.dao

import com.ustadmobile.door.DoorDbType
import com.ustadmobile.door.EntityInsertionAdapter
import com.ustadmobile.door.PreparedStatementConfig
import com.ustadmobile.door.ext.prepareAndUseStatement
import com.ustadmobile.door.ext.prepareAndUseStatementAsync
import com.ustadmobile.door.jdbc.PreparedStatement
import com.ustadmobile.door.jdbc.ext.executeQueryAsyncKmp
import com.ustadmobile.door.jdbc.ext.executeUpdateAsyncKmp
import com.ustadmobile.door.jdbc.ext.mapNextRow
import com.ustadmobile.door.jdbc.ext.useResults
import com.ustadmobile.door.room.RoomDatabase
import com.ustadmobile.lib.db.entities.Person
import com.ustadmobile.lib.db.entities.PersonAuth
import kotlin.Boolean
import kotlin.IllegalArgumentException
import kotlin.Int
import kotlin.Long
import kotlin.String
import kotlin.collections.List

public class PersonAuthDao_JdbcImpl(
  public val _db: RoomDatabase,
) : PersonAuthDao() {
  public val _insertAdapterPersonAuth_abort: EntityInsertionAdapter<PersonAuth> = object :
      EntityInsertionAdapter<PersonAuth>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT INTO PersonAuth (personAuthUid, passwordHash, personAuthStatus) VALUES(?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO PersonAuth (personAuthUid, passwordHash, personAuthStatus) VALUES(COALESCE(?,nextval('PersonAuth_personAuthUid_seq')), ?, ?)" + if(returnsId) { " RETURNING personAuthUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: PersonAuth) {
      if(entity.personAuthUid == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.personAuthUid)
      }
      stmt.setString(2, entity.passwordHash)
      stmt.setInt(3, entity.personAuthStatus)
    }
  }

  public override fun insert(entity: PersonAuth): Long {
    val _retVal = _insertAdapterPersonAuth_abort.insertAndReturnId(entity)
    return _retVal
  }

  public override suspend fun insertAsync(entity: PersonAuth): Long {
    val _retVal = _insertAdapterPersonAuth_abort.insertAndReturnIdAsync(entity)
    return _retVal
  }

  public override fun insertList(entityList: List<PersonAuth>) {
    _insertAdapterPersonAuth_abort.insertList(entityList)
  }

  override suspend fun updateAsync(entity: PersonAuth): Int {
    var _result = 0
    val _sql =
        "UPDATE PersonAuth SET passwordHash = ?, personAuthStatus = ? WHERE personAuthUid = ?"
    _db.prepareAndUseStatementAsync(_sql) {
       _stmt ->
      _stmt.setString(1, entity.passwordHash)
      _stmt.setInt(2, entity.personAuthStatus)
      _stmt.setLong(3, entity.personAuthUid)
      _result += _stmt.executeUpdateAsyncKmp()
    }
    return _result
  }

  public override fun update(entity: PersonAuth) {
    val _sql =
        "UPDATE PersonAuth SET passwordHash = ?, personAuthStatus = ? WHERE personAuthUid = ?"
    _db.prepareAndUseStatement(_sql) {
       _stmt ->
      _stmt.setString(1, entity.passwordHash)
      _stmt.setInt(2, entity.personAuthStatus)
      _stmt.setLong(3, entity.personAuthUid)
      _stmt.executeUpdate()
    }
  }

  override suspend fun findByUidAsync(uid: Long): PersonAuth? =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = "SELECT * FROM PersonAuth WHERE personAuthUid = ?",
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,uid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_personAuthUid = _result.getLong("personAuthUid")
        val _tmp_passwordHash = _result.getString("passwordHash")
        val _tmp_personAuthStatus = _result.getInt("personAuthStatus")
        PersonAuth().apply {
          this.personAuthUid = _tmp_personAuthUid
          this.passwordHash = _tmp_passwordHash
          this.personAuthStatus = _tmp_personAuthStatus
        }
      }
    }
  }

  override fun findByUid(uid: Long): PersonAuth? =
      _db.prepareAndUseStatement(PreparedStatementConfig(
    sql = "SELECT * FROM PersonAuth WHERE personAuthUid = ?",
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,uid)
    _stmt.executeQuery().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_personAuthUid = _result.getLong("personAuthUid")
        val _tmp_passwordHash = _result.getString("passwordHash")
        val _tmp_personAuthStatus = _result.getInt("personAuthStatus")
        PersonAuth().apply {
          this.personAuthUid = _tmp_personAuthUid
          this.passwordHash = _tmp_passwordHash
          this.personAuthStatus = _tmp_personAuthStatus
        }
      }
    }
  }

  override fun findPersonByUsername(username: String): Person? =
      _db.prepareAndUseStatement(PreparedStatementConfig(
    sql = "SELECT * FROM Person WHERE username = ?",
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setString(1,username)
    _stmt.executeQuery().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_personUid = _result.getLong("personUid")
        val _tmp_username = _result.getString("username")
        val _tmp_firstNames = _result.getString("firstNames")
        val _tmp_lastName = _result.getString("lastName")
        val _tmp_emailAddr = _result.getString("emailAddr")
        val _tmp_phoneNum = _result.getString("phoneNum")
        val _tmp_gender = _result.getInt("gender")
        val _tmp_active = _result.getBoolean("active")
        val _tmp_isPersonalAccount = _result.getBoolean("isPersonalAccount")
        val _tmp_dateOfBirth = _result.getLong("dateOfBirth")
        val _tmp_personAddress = _result.getString("personAddress")
        val _tmp_personOrgId = _result.getString("personOrgId")
        val _tmp_personGroupUid = _result.getLong("personGroupUid")
        val _tmp_personLct = _result.getLong("personLct")
        val _tmp_personCountry = _result.getString("personCountry")
        val _tmp_personType = _result.getInt("personType")
        val _tmp_personMasterChangeSeqNum = _result.getLong("personMasterChangeSeqNum")
        val _tmp_personLocalChangeSeqNum = _result.getLong("personLocalChangeSeqNum")
        val _tmp_personLastChangedBy = _result.getInt("personLastChangedBy")
        val _tmp_admin = _result.getBoolean("admin")
        val _tmp_personNotes = _result.getString("personNotes")
        val _tmp_fatherName = _result.getString("fatherName")
        val _tmp_fatherNumber = _result.getString("fatherNumber")
        val _tmp_motherName = _result.getString("motherName")
        val _tmp_motherNum = _result.getString("motherNum")
        Person().apply {
          this.personUid = _tmp_personUid
          this.username = _tmp_username
          this.firstNames = _tmp_firstNames
          this.lastName = _tmp_lastName
          this.emailAddr = _tmp_emailAddr
          this.phoneNum = _tmp_phoneNum
          this.gender = _tmp_gender
          this.active = _tmp_active
          this.isPersonalAccount = _tmp_isPersonalAccount
          this.dateOfBirth = _tmp_dateOfBirth
          this.personAddress = _tmp_personAddress
          this.personOrgId = _tmp_personOrgId
          this.personGroupUid = _tmp_personGroupUid
          this.personLct = _tmp_personLct
          this.personCountry = _tmp_personCountry
          this.personType = _tmp_personType
          this.personMasterChangeSeqNum = _tmp_personMasterChangeSeqNum
          this.personLocalChangeSeqNum = _tmp_personLocalChangeSeqNum
          this.personLastChangedBy = _tmp_personLastChangedBy
          this.admin = _tmp_admin
          this.personNotes = _tmp_personNotes
          this.fatherName = _tmp_fatherName
          this.fatherNumber = _tmp_fatherNumber
          this.motherName = _tmp_motherName
          this.motherNum = _tmp_motherNum
        }
      }
    }
  }

  override suspend fun updatePasswordForPersonUid(personUid: Long, passwordHash: String): Int =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = "UPDATE PersonAuth set passwordHash = ?  WHERE personAuthUid = ?",
    readOnly = false,)
  ) { _stmt -> 
    _stmt.setString(1,passwordHash)
    _stmt.setLong(2,personUid)
    _stmt.executeUpdateAsyncKmp()
  }
}
