package com.ustadmobile.core.db.dao

import androidx.paging.PagingSource
import com.ustadmobile.door.DoorDbType
import com.ustadmobile.door.EntityInsertionAdapter
import com.ustadmobile.door.PreparedStatementConfig
import com.ustadmobile.door.ext.prepareAndUseStatementAsync
import com.ustadmobile.door.flow.doorFlow
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.getStringNonNull
import com.ustadmobile.door.jdbc.ext.mapNextRow
import com.ustadmobile.door.jdbc.ext.mapRows
import com.ustadmobile.door.jdbc.ext.useResults
import com.ustadmobile.door.paging.DoorLimitOffsetPagingSource
import com.ustadmobile.door.room.RoomDatabase
import com.ustadmobile.lib.db.entities.PersonPasskey
import kotlin.Boolean
import kotlin.IllegalArgumentException
import kotlin.Int
import kotlin.Long
import kotlin.String
import kotlin.collections.List
import kotlinx.coroutines.flow.Flow

public class PersonPasskeyDao_JdbcImpl(
  public val _db: RoomDatabase,
) : PersonPasskeyDao() {
  public val _insertAdapterPersonPasskey_abort: EntityInsertionAdapter<PersonPasskey> = object :
      EntityInsertionAdapter<PersonPasskey>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT INTO PersonPasskey (personPasskeyUid, ppPersonUid, ppAttestationObj, ppClientDataJson, ppOriginString, ppId, ppChallengeString, ppPublicKey, isRevoked, ppPasskeyLct) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO PersonPasskey (personPasskeyUid, ppPersonUid, ppAttestationObj, ppClientDataJson, ppOriginString, ppId, ppChallengeString, ppPublicKey, isRevoked, ppPasskeyLct) VALUES(COALESCE(?,nextval('PersonPasskey_personPasskeyUid_seq')), ?, ?, ?, ?, ?, ?, ?, ?, ?)" + if(returnsId) { " RETURNING personPasskeyUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: PersonPasskey) {
      if(entity.personPasskeyUid == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.personPasskeyUid)
      }
      stmt.setLong(2, entity.ppPersonUid)
      stmt.setString(3, entity.ppAttestationObj)
      stmt.setString(4, entity.ppClientDataJson)
      stmt.setString(5, entity.ppOriginString)
      stmt.setString(6, entity.ppId)
      stmt.setString(7, entity.ppChallengeString)
      stmt.setString(8, entity.ppPublicKey)
      stmt.setInt(9, entity.isRevoked)
      stmt.setLong(10, entity.ppPasskeyLct)
    }
  }

  override suspend fun insertAsync(personPasskey: PersonPasskey): Long {
    val _retVal = _insertAdapterPersonPasskey_abort.insertAndReturnIdAsync(personPasskey)
    return _retVal
  }

  override suspend fun allPasskey(): List<String> =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT PersonPasskey.ppId
    |          FROM PersonPasskey
    |              
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapRows {
        _result.getStringNonNull(1)
      }
    }
  }

  override fun getAllActivePasskeys(uid: Long): Flow<List<PersonPasskey>> =
      _db.doorFlow(arrayOf("PersonPasskey")) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        SELECT * 
      |          FROM PersonPasskey
      |         WHERE isRevoked = 0
      |         AND PersonPasskey.ppPersonUid = ?
      |              
      """.trimMargin(),
      readOnly = true,)
    ) { _stmt -> 
      _stmt.setLong(1,uid)
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapRows {
          val _tmp_personPasskeyUid = _result.getLong("personPasskeyUid")
          val _tmp_ppPersonUid = _result.getLong("ppPersonUid")
          val _tmp_ppAttestationObj = _result.getString("ppAttestationObj")
          val _tmp_ppClientDataJson = _result.getString("ppClientDataJson")
          val _tmp_ppOriginString = _result.getString("ppOriginString")
          val _tmp_ppId = _result.getString("ppId")
          val _tmp_ppChallengeString = _result.getString("ppChallengeString")
          val _tmp_ppPublicKey = _result.getString("ppPublicKey")
          val _tmp_isRevoked = _result.getInt("isRevoked")
          val _tmp_ppPasskeyLct = _result.getLong("ppPasskeyLct")
          PersonPasskey().apply {
            this.personPasskeyUid = _tmp_personPasskeyUid
            this.ppPersonUid = _tmp_ppPersonUid
            this.ppAttestationObj = _tmp_ppAttestationObj
            this.ppClientDataJson = _tmp_ppClientDataJson
            this.ppOriginString = _tmp_ppOriginString
            this.ppId = _tmp_ppId
            this.ppChallengeString = _tmp_ppChallengeString
            this.ppPublicKey = _tmp_ppPublicKey
            this.isRevoked = _tmp_isRevoked
            this.ppPasskeyLct = _tmp_ppPasskeyLct
          }
        }
      }
    }
  }

  override fun getAllActivePasskeysPaging(uid: Long): PagingSource<Int, PersonPasskey> = object :
      DoorLimitOffsetPagingSource<PersonPasskey>(db = _db
  , tableNames = arrayOf("PersonPasskey")
  ) {
    override suspend fun loadRows(_limit: Int, _offset: Int): List<PersonPasskey> =
        _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |SELECT * FROM (
      |        SELECT * 
      |          FROM PersonPasskey
      |         WHERE isRevoked = 0
      |          AND PersonPasskey.ppPersonUid = ?
      |              ) AS _PagingData LIMIT ? OFFSET ?
      """.trimMargin(),
      readOnly = true,)
    ) { _stmt -> 
      _stmt.setLong(1,uid)
      _stmt.setInt(2,_limit)
      _stmt.setInt(3,_offset)
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapRows {
          val _tmp_personPasskeyUid = _result.getLong("personPasskeyUid")
          val _tmp_ppPersonUid = _result.getLong("ppPersonUid")
          val _tmp_ppAttestationObj = _result.getString("ppAttestationObj")
          val _tmp_ppClientDataJson = _result.getString("ppClientDataJson")
          val _tmp_ppOriginString = _result.getString("ppOriginString")
          val _tmp_ppId = _result.getString("ppId")
          val _tmp_ppChallengeString = _result.getString("ppChallengeString")
          val _tmp_ppPublicKey = _result.getString("ppPublicKey")
          val _tmp_isRevoked = _result.getInt("isRevoked")
          val _tmp_ppPasskeyLct = _result.getLong("ppPasskeyLct")
          PersonPasskey().apply {
            this.personPasskeyUid = _tmp_personPasskeyUid
            this.ppPersonUid = _tmp_ppPersonUid
            this.ppAttestationObj = _tmp_ppAttestationObj
            this.ppClientDataJson = _tmp_ppClientDataJson
            this.ppOriginString = _tmp_ppOriginString
            this.ppId = _tmp_ppId
            this.ppChallengeString = _tmp_ppChallengeString
            this.ppPublicKey = _tmp_ppPublicKey
            this.isRevoked = _tmp_isRevoked
            this.ppPasskeyLct = _tmp_ppPasskeyLct
          }
        }
      }
    }

    override suspend fun countRows(): Int = _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |SELECT COUNT(*) FROM (
      |        SELECT * 
      |          FROM PersonPasskey
      |         WHERE isRevoked = 0
      |          AND PersonPasskey.ppPersonUid = ?
      |              ) AS _PagingCount
      """.trimMargin(),
      readOnly = true,)
    ) { _stmt -> 
      _stmt.setLong(1,uid)
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapNextRow(0) {
          _result.getInt(1)
        }
      }
    }
  }

  override suspend fun findPersonPasskeyFromClientDataJson(id: String): PersonPasskey? =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT *
    |          FROM PersonPasskey
    |         WHERE PersonPasskey.ppId = ? 
    |              
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setString(1,id)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_personPasskeyUid = _result.getLong("personPasskeyUid")
        val _tmp_ppPersonUid = _result.getLong("ppPersonUid")
        val _tmp_ppAttestationObj = _result.getString("ppAttestationObj")
        val _tmp_ppClientDataJson = _result.getString("ppClientDataJson")
        val _tmp_ppOriginString = _result.getString("ppOriginString")
        val _tmp_ppId = _result.getString("ppId")
        val _tmp_ppChallengeString = _result.getString("ppChallengeString")
        val _tmp_ppPublicKey = _result.getString("ppPublicKey")
        val _tmp_isRevoked = _result.getInt("isRevoked")
        val _tmp_ppPasskeyLct = _result.getLong("ppPasskeyLct")
        PersonPasskey().apply {
          this.personPasskeyUid = _tmp_personPasskeyUid
          this.ppPersonUid = _tmp_ppPersonUid
          this.ppAttestationObj = _tmp_ppAttestationObj
          this.ppClientDataJson = _tmp_ppClientDataJson
          this.ppOriginString = _tmp_ppOriginString
          this.ppId = _tmp_ppId
          this.ppChallengeString = _tmp_ppChallengeString
          this.ppPublicKey = _tmp_ppPublicKey
          this.isRevoked = _tmp_isRevoked
          this.ppPasskeyLct = _tmp_ppPasskeyLct
        }
      }
    }
  }

  override suspend fun revokePersonPasskey(uid: Long) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        UPDATE PersonPasskey
      |          set isRevoked = 1
      |         WHERE PersonPasskey.personPasskeyUid = ? 
      |              
      """.trimMargin(),
      readOnly = false,)
    ) { _stmt -> 
      _stmt.setLong(1,uid)
      _stmt.executeUpdateAsyncKmp()
    }
  }
}
