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.mapRows
import com.ustadmobile.door.jdbc.ext.useResults
import com.ustadmobile.door.room.RoomDatabase
import com.ustadmobile.lib.db.entities.PersonGroupMember
import kotlin.Boolean
import kotlin.IllegalArgumentException
import kotlin.Int
import kotlin.Long
import kotlin.String
import kotlin.collections.List

public class PersonGroupMemberDao_JdbcImpl(
  public val _db: RoomDatabase,
) : PersonGroupMemberDao() {
  public val _insertAdapterPersonGroupMember_abort: EntityInsertionAdapter<PersonGroupMember> =
      object : EntityInsertionAdapter<PersonGroupMember>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT INTO PersonGroupMember (groupMemberUid, groupMemberActive, groupMemberPersonUid, groupMemberGroupUid, groupMemberMasterCsn, groupMemberLocalCsn, groupMemberLastChangedBy, groupMemberLct) VALUES(?, ?, ?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO PersonGroupMember (groupMemberUid, groupMemberActive, groupMemberPersonUid, groupMemberGroupUid, groupMemberMasterCsn, groupMemberLocalCsn, groupMemberLastChangedBy, groupMemberLct) VALUES(COALESCE(?,nextval('PersonGroupMember_groupMemberUid_seq')), ?, ?, ?, ?, ?, ?, ?)" + if(returnsId) { " RETURNING groupMemberUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: PersonGroupMember) {
      if(entity.groupMemberUid == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.groupMemberUid)
      }
      stmt.setBoolean(2, entity.groupMemberActive)
      stmt.setLong(3, entity.groupMemberPersonUid)
      stmt.setLong(4, entity.groupMemberGroupUid)
      stmt.setLong(5, entity.groupMemberMasterCsn)
      stmt.setLong(6, entity.groupMemberLocalCsn)
      stmt.setInt(7, entity.groupMemberLastChangedBy)
      stmt.setLong(8, entity.groupMemberLct)
    }
  }

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

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

  public override fun insertList(entityList: List<PersonGroupMember>) {
    _insertAdapterPersonGroupMember_abort.insertList(entityList)
  }

  public override fun update(entity: PersonGroupMember) {
    val _sql =
        "UPDATE PersonGroupMember SET groupMemberActive = ?, groupMemberPersonUid = ?, groupMemberGroupUid = ?, groupMemberMasterCsn = ?, groupMemberLocalCsn = ?, groupMemberLastChangedBy = ?, groupMemberLct = ? WHERE groupMemberUid = ?"
    _db.prepareAndUseStatement(_sql) {
       _stmt ->
      _stmt.setBoolean(1, entity.groupMemberActive)
      _stmt.setLong(2, entity.groupMemberPersonUid)
      _stmt.setLong(3, entity.groupMemberGroupUid)
      _stmt.setLong(4, entity.groupMemberMasterCsn)
      _stmt.setLong(5, entity.groupMemberLocalCsn)
      _stmt.setInt(6, entity.groupMemberLastChangedBy)
      _stmt.setLong(7, entity.groupMemberLct)
      _stmt.setLong(8, entity.groupMemberUid)
      _stmt.executeUpdate()
    }
  }

  override suspend fun findAllGroupWherePersonIsIn(personUid: Long): List<PersonGroupMember> =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql =
        "SELECT * FROM PersonGroupMember WHERE groupMemberPersonUid = ? AND PersonGroupMember.groupMemberActive",
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,personUid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapRows {
        val _tmp_groupMemberUid = _result.getLong("groupMemberUid")
        val _tmp_groupMemberActive = _result.getBoolean("groupMemberActive")
        val _tmp_groupMemberPersonUid = _result.getLong("groupMemberPersonUid")
        val _tmp_groupMemberGroupUid = _result.getLong("groupMemberGroupUid")
        val _tmp_groupMemberMasterCsn = _result.getLong("groupMemberMasterCsn")
        val _tmp_groupMemberLocalCsn = _result.getLong("groupMemberLocalCsn")
        val _tmp_groupMemberLastChangedBy = _result.getInt("groupMemberLastChangedBy")
        val _tmp_groupMemberLct = _result.getLong("groupMemberLct")
        PersonGroupMember().apply {
          this.groupMemberUid = _tmp_groupMemberUid
          this.groupMemberActive = _tmp_groupMemberActive
          this.groupMemberPersonUid = _tmp_groupMemberPersonUid
          this.groupMemberGroupUid = _tmp_groupMemberGroupUid
          this.groupMemberMasterCsn = _tmp_groupMemberMasterCsn
          this.groupMemberLocalCsn = _tmp_groupMemberLocalCsn
          this.groupMemberLastChangedBy = _tmp_groupMemberLastChangedBy
          this.groupMemberLct = _tmp_groupMemberLct
        }
      }
    }
  }

  override suspend fun checkPersonBelongsToGroup(groupUid: Long, personUid: Long):
      List<PersonGroupMember> = _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |SELECT * FROM PersonGroupMember WHERE groupMemberGroupUid = ? 
    |             AND groupMemberPersonUid = ? AND PersonGroupMember.groupMemberActive
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,groupUid)
    _stmt.setLong(2,personUid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapRows {
        val _tmp_groupMemberUid = _result.getLong("groupMemberUid")
        val _tmp_groupMemberActive = _result.getBoolean("groupMemberActive")
        val _tmp_groupMemberPersonUid = _result.getLong("groupMemberPersonUid")
        val _tmp_groupMemberGroupUid = _result.getLong("groupMemberGroupUid")
        val _tmp_groupMemberMasterCsn = _result.getLong("groupMemberMasterCsn")
        val _tmp_groupMemberLocalCsn = _result.getLong("groupMemberLocalCsn")
        val _tmp_groupMemberLastChangedBy = _result.getInt("groupMemberLastChangedBy")
        val _tmp_groupMemberLct = _result.getLong("groupMemberLct")
        PersonGroupMember().apply {
          this.groupMemberUid = _tmp_groupMemberUid
          this.groupMemberActive = _tmp_groupMemberActive
          this.groupMemberPersonUid = _tmp_groupMemberPersonUid
          this.groupMemberGroupUid = _tmp_groupMemberGroupUid
          this.groupMemberMasterCsn = _tmp_groupMemberMasterCsn
          this.groupMemberLocalCsn = _tmp_groupMemberLocalCsn
          this.groupMemberLastChangedBy = _tmp_groupMemberLastChangedBy
          this.groupMemberLct = _tmp_groupMemberLct
        }
      }
    }
  }

  override suspend fun moveGroupAsync(
    personUid: Long,
    newGroup: Long,
    oldGroup: Long,
    changeTime: Long,
  ): Int = _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        UPDATE PersonGroupMember 
    |           SET groupMemberGroupUid = ?,
    |               groupMemberLct = ?
    |         WHERE groupMemberPersonUid = ? 
    |           AND groupMemberGroupUid = ? 
    |           AND PersonGroupMember.groupMemberActive
    """.trimMargin(),
    readOnly = false,)
  ) { _stmt -> 
    _stmt.setLong(1,newGroup)
    _stmt.setLong(2,changeTime)
    _stmt.setLong(3,personUid)
    _stmt.setLong(4,oldGroup)
    _stmt.executeUpdateAsyncKmp()
  }

  override suspend fun updateGroupMemberActive(
    activeStatus: Boolean,
    personUid: Long,
    groupUid: Long,
    updateTime: Long,
  ) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        UPDATE PersonGroupMember 
      |           SET groupMemberActive = ?,
      |               groupMemberLct = ?
      |        WHERE groupMemberPersonUid = ? 
      |          AND groupMemberGroupUid = ? 
      |          AND PersonGroupMember.groupMemberActive
      """.trimMargin(),
      readOnly = false,)
    ) { _stmt -> 
      _stmt.setBoolean(1,activeStatus)
      _stmt.setLong(2,updateTime)
      _stmt.setLong(3,personUid)
      _stmt.setLong(4,groupUid)
      _stmt.executeUpdateAsyncKmp()
    }
  }

  override suspend fun findByPersonUidAndGroupUid(personUid: Long, groupUid: Long):
      PersonGroupMember? = _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT PersonGroupMember.*
    |          FROM PersonGroupMember
    |         WHERE PersonGroupMember.groupMemberPersonUid = ?
    |           AND PersonGroupMember.groupMemberGroupUid = ?
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,personUid)
    _stmt.setLong(2,groupUid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_groupMemberUid = _result.getLong("groupMemberUid")
        val _tmp_groupMemberActive = _result.getBoolean("groupMemberActive")
        val _tmp_groupMemberPersonUid = _result.getLong("groupMemberPersonUid")
        val _tmp_groupMemberGroupUid = _result.getLong("groupMemberGroupUid")
        val _tmp_groupMemberMasterCsn = _result.getLong("groupMemberMasterCsn")
        val _tmp_groupMemberLocalCsn = _result.getLong("groupMemberLocalCsn")
        val _tmp_groupMemberLastChangedBy = _result.getInt("groupMemberLastChangedBy")
        val _tmp_groupMemberLct = _result.getLong("groupMemberLct")
        PersonGroupMember().apply {
          this.groupMemberUid = _tmp_groupMemberUid
          this.groupMemberActive = _tmp_groupMemberActive
          this.groupMemberPersonUid = _tmp_groupMemberPersonUid
          this.groupMemberGroupUid = _tmp_groupMemberGroupUid
          this.groupMemberMasterCsn = _tmp_groupMemberMasterCsn
          this.groupMemberLocalCsn = _tmp_groupMemberLocalCsn
          this.groupMemberLastChangedBy = _tmp_groupMemberLastChangedBy
          this.groupMemberLct = _tmp_groupMemberLct
        }
      }
    }
  }
}
