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.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.mapNextRow
import com.ustadmobile.door.jdbc.ext.useResults
import com.ustadmobile.door.room.RoomDatabase
import com.ustadmobile.lib.db.entities.PersonAuth2
import kotlin.Boolean
import kotlin.IllegalArgumentException
import kotlin.Long
import kotlin.String
import kotlin.collections.List
import kotlinx.coroutines.flow.Flow

public class PersonAuth2Dao_JdbcImpl(
  public val _db: RoomDatabase,
) : PersonAuth2Dao() {
  public val _insertAdapterPersonAuth2_abort: EntityInsertionAdapter<PersonAuth2> = object :
      EntityInsertionAdapter<PersonAuth2>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT INTO PersonAuth2 (pauthUid, pauthMechanism, pauthAuth, pauthLcsn, pauthPcsn, pauthLcb, pauthLct) VALUES(?, ?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO PersonAuth2 (pauthUid, pauthMechanism, pauthAuth, pauthLcsn, pauthPcsn, pauthLcb, pauthLct) VALUES(?, ?, ?, ?, ?, ?, ?)" + if(returnsId) { " RETURNING pauthUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: PersonAuth2) {
      stmt.setLong(1, entity.pauthUid)
      stmt.setString(2, entity.pauthMechanism)
      stmt.setString(3, entity.pauthAuth)
      stmt.setLong(4, entity.pauthLcsn)
      stmt.setLong(5, entity.pauthPcsn)
      stmt.setLong(6, entity.pauthLcb)
      stmt.setLong(7, entity.pauthLct)
    }
  }

  public val _insertAdapterPersonAuth2_upsert: EntityInsertionAdapter<PersonAuth2> = object :
      EntityInsertionAdapter<PersonAuth2>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT OR REPLACE INTO PersonAuth2 (pauthUid, pauthMechanism, pauthAuth, pauthLcsn, pauthPcsn, pauthLcb, pauthLct) VALUES(?, ?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO PersonAuth2 (pauthUid, pauthMechanism, pauthAuth, pauthLcsn, pauthPcsn, pauthLcb, pauthLct) VALUES(?, ?, ?, ?, ?, ?, ?) ON CONFLICT (pauthUid) DO UPDATE SET pauthMechanism = excluded.pauthMechanism,pauthAuth = excluded.pauthAuth,pauthLcsn = excluded.pauthLcsn,pauthPcsn = excluded.pauthPcsn,pauthLcb = excluded.pauthLcb,pauthLct = excluded.pauthLct" + if(returnsId) { " RETURNING pauthUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: PersonAuth2) {
      stmt.setLong(1, entity.pauthUid)
      stmt.setString(2, entity.pauthMechanism)
      stmt.setString(3, entity.pauthAuth)
      stmt.setLong(4, entity.pauthLcsn)
      stmt.setLong(5, entity.pauthPcsn)
      stmt.setLong(6, entity.pauthLcb)
      stmt.setLong(7, entity.pauthLct)
    }
  }

  override suspend fun insertListAsync(auths: List<PersonAuth2>) {
    _insertAdapterPersonAuth2_abort.insertListAsync(auths)
  }

  override suspend fun insertAsync(auth: PersonAuth2): Long {
    val _retVal = _insertAdapterPersonAuth2_upsert.insertAndReturnIdAsync(auth)
    return _retVal
  }

  override suspend fun findByPersonUid(personUid: Long): PersonAuth2? =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |         SELECT PersonAuth2.* FROM PersonAuth2 where PersonAuth2.pauthUid = ?
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,personUid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_pauthUid = _result.getLong("pauthUid")
        val _tmp_pauthMechanism = _result.getString("pauthMechanism")
        val _tmp_pauthAuth = _result.getString("pauthAuth")
        val _tmp_pauthLcsn = _result.getLong("pauthLcsn")
        val _tmp_pauthPcsn = _result.getLong("pauthPcsn")
        val _tmp_pauthLcb = _result.getLong("pauthLcb")
        val _tmp_pauthLct = _result.getLong("pauthLct")
        PersonAuth2().apply {
          this.pauthUid = _tmp_pauthUid
          this.pauthMechanism = _tmp_pauthMechanism
          this.pauthAuth = _tmp_pauthAuth
          this.pauthLcsn = _tmp_pauthLcsn
          this.pauthPcsn = _tmp_pauthPcsn
          this.pauthLcb = _tmp_pauthLcb
          this.pauthLct = _tmp_pauthLct
        }
      }
    }
  }

  override fun findByPersonUidFlow(personUid: Long): Flow<PersonAuth2?> =
      _db.doorFlow(arrayOf("PersonAuth2")) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        SELECT PersonAuth2.*
      |          FROM PersonAuth2
      |         WHERE PersonAuth2.pauthUid = ? 
      |    
      """.trimMargin(),
      readOnly = true,)
    ) { _stmt -> 
      _stmt.setLong(1,personUid)
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapNextRow(null) {
          val _tmp_pauthUid = _result.getLong("pauthUid")
          val _tmp_pauthMechanism = _result.getString("pauthMechanism")
          val _tmp_pauthAuth = _result.getString("pauthAuth")
          val _tmp_pauthLcsn = _result.getLong("pauthLcsn")
          val _tmp_pauthPcsn = _result.getLong("pauthPcsn")
          val _tmp_pauthLcb = _result.getLong("pauthLcb")
          val _tmp_pauthLct = _result.getLong("pauthLct")
          PersonAuth2().apply {
            this.pauthUid = _tmp_pauthUid
            this.pauthMechanism = _tmp_pauthMechanism
            this.pauthAuth = _tmp_pauthAuth
            this.pauthLcsn = _tmp_pauthLcsn
            this.pauthPcsn = _tmp_pauthPcsn
            this.pauthLcb = _tmp_pauthLcb
            this.pauthLct = _tmp_pauthLct
          }
        }
      }
    }
  }

  override suspend fun findByUsername(username: String): PersonAuth2? =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT PersonAuth2.*
    |          FROM PersonAuth2
    |               JOIN Person ON PersonAuth2.pauthUid = Person.personUid
    |         WHERE Person.username = ?
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setString(1,username)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_pauthUid = _result.getLong("pauthUid")
        val _tmp_pauthMechanism = _result.getString("pauthMechanism")
        val _tmp_pauthAuth = _result.getString("pauthAuth")
        val _tmp_pauthLcsn = _result.getLong("pauthLcsn")
        val _tmp_pauthPcsn = _result.getLong("pauthPcsn")
        val _tmp_pauthLcb = _result.getLong("pauthLcb")
        val _tmp_pauthLct = _result.getLong("pauthLct")
        PersonAuth2().apply {
          this.pauthUid = _tmp_pauthUid
          this.pauthMechanism = _tmp_pauthMechanism
          this.pauthAuth = _tmp_pauthAuth
          this.pauthLcsn = _tmp_pauthLcsn
          this.pauthPcsn = _tmp_pauthPcsn
          this.pauthLcb = _tmp_pauthLcb
          this.pauthLct = _tmp_pauthLct
        }
      }
    }
  }

  override fun findByUsernames(username: String): Flow<PersonAuth2?> =
      _db.doorFlow(arrayOf("PersonAuth2", "Person")) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        SELECT PersonAuth2.*
      |          FROM PersonAuth2
      |               JOIN Person ON PersonAuth2.pauthUid = Person.personUid
      |         WHERE Person.username = ?
      |    
      """.trimMargin(),
      readOnly = true,)
    ) { _stmt -> 
      _stmt.setString(1,username)
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapNextRow(null) {
          val _tmp_pauthUid = _result.getLong("pauthUid")
          val _tmp_pauthMechanism = _result.getString("pauthMechanism")
          val _tmp_pauthAuth = _result.getString("pauthAuth")
          val _tmp_pauthLcsn = _result.getLong("pauthLcsn")
          val _tmp_pauthPcsn = _result.getLong("pauthPcsn")
          val _tmp_pauthLcb = _result.getLong("pauthLcb")
          val _tmp_pauthLct = _result.getLong("pauthLct")
          PersonAuth2().apply {
            this.pauthUid = _tmp_pauthUid
            this.pauthMechanism = _tmp_pauthMechanism
            this.pauthAuth = _tmp_pauthAuth
            this.pauthLcsn = _tmp_pauthLcsn
            this.pauthPcsn = _tmp_pauthPcsn
            this.pauthLcb = _tmp_pauthLcb
            this.pauthLct = _tmp_pauthLct
          }
        }
      }
    }
  }
}
