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.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.mapNextRow
import com.ustadmobile.door.jdbc.ext.useResults
import com.ustadmobile.door.room.RoomDatabase
import com.ustadmobile.lib.db.entities.PersonPicture
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 PersonPictureDao_JdbcImpl(
  public val _db: RoomDatabase,
) : PersonPictureDao() {
  public val _insertAdapterPersonPicture_upsert: EntityInsertionAdapter<PersonPicture> = object :
      EntityInsertionAdapter<PersonPicture>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT OR REPLACE INTO PersonPicture (personPictureUid, personPictureLct, personPictureUri, personPictureThumbnailUri, fileSize, personPictureActive) VALUES(?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO PersonPicture (personPictureUid, personPictureLct, personPictureUri, personPictureThumbnailUri, fileSize, personPictureActive) VALUES(COALESCE(?,nextval('PersonPicture_personPictureUid_seq')), ?, ?, ?, ?, ?) ON CONFLICT (personPictureUid) DO UPDATE SET personPictureLct = excluded.personPictureLct,personPictureUri = excluded.personPictureUri,personPictureThumbnailUri = excluded.personPictureThumbnailUri,fileSize = excluded.fileSize,personPictureActive = excluded.personPictureActive" + if(returnsId) { " RETURNING personPictureUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: PersonPicture) {
      if(entity.personPictureUid == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.personPictureUid)
      }
      stmt.setLong(2, entity.personPictureLct)
      stmt.setString(3, entity.personPictureUri)
      stmt.setString(4, entity.personPictureThumbnailUri)
      stmt.setInt(5, entity.fileSize)
      stmt.setBoolean(6, entity.personPictureActive)
    }
  }

  public val _insertAdapterPersonPicture_abort: EntityInsertionAdapter<PersonPicture> = object :
      EntityInsertionAdapter<PersonPicture>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT INTO PersonPicture (personPictureUid, personPictureLct, personPictureUri, personPictureThumbnailUri, fileSize, personPictureActive) VALUES(?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO PersonPicture (personPictureUid, personPictureLct, personPictureUri, personPictureThumbnailUri, fileSize, personPictureActive) VALUES(COALESCE(?,nextval('PersonPicture_personPictureUid_seq')), ?, ?, ?, ?, ?)" + if(returnsId) { " RETURNING personPictureUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: PersonPicture) {
      if(entity.personPictureUid == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.personPictureUid)
      }
      stmt.setLong(2, entity.personPictureLct)
      stmt.setString(3, entity.personPictureUri)
      stmt.setString(4, entity.personPictureThumbnailUri)
      stmt.setInt(5, entity.fileSize)
      stmt.setBoolean(6, entity.personPictureActive)
    }
  }

  override suspend fun upsert(personPicture: PersonPicture) {
    _insertAdapterPersonPicture_upsert.insertAsync(personPicture)
  }

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

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

  public override fun insertList(entityList: List<PersonPicture>) {
    _insertAdapterPersonPicture_abort.insertList(entityList)
  }

  override suspend fun updateAsync(personPicture: PersonPicture) {
    val _sql =
        "UPDATE PersonPicture SET personPictureLct = ?, personPictureUri = ?, personPictureThumbnailUri = ?, fileSize = ?, personPictureActive = ? WHERE personPictureUid = ?"
    _db.prepareAndUseStatementAsync(_sql) {
       _stmt ->
      _stmt.setLong(1, personPicture.personPictureLct)
      _stmt.setString(2, personPicture.personPictureUri)
      _stmt.setString(3, personPicture.personPictureThumbnailUri)
      _stmt.setInt(4, personPicture.fileSize)
      _stmt.setBoolean(5, personPicture.personPictureActive)
      _stmt.setLong(6, personPicture.personPictureUid)
      _stmt.executeUpdateAsyncKmp()
    }
  }

  public override fun update(entity: PersonPicture) {
    val _sql =
        "UPDATE PersonPicture SET personPictureLct = ?, personPictureUri = ?, personPictureThumbnailUri = ?, fileSize = ?, personPictureActive = ? WHERE personPictureUid = ?"
    _db.prepareAndUseStatement(_sql) {
       _stmt ->
      _stmt.setLong(1, entity.personPictureLct)
      _stmt.setString(2, entity.personPictureUri)
      _stmt.setString(3, entity.personPictureThumbnailUri)
      _stmt.setInt(4, entity.fileSize)
      _stmt.setBoolean(5, entity.personPictureActive)
      _stmt.setLong(6, entity.personPictureUid)
      _stmt.executeUpdate()
    }
  }

  override suspend fun findByPersonUidAsync(personUid: Long): PersonPicture? =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT * 
    |          FROM PersonPicture 
    |         WHERE personPictureUid = ?
    |           AND CAST(personPictureActive AS INTEGER) = 1
    |         
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,personUid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_personPictureUid = _result.getLong("personPictureUid")
        val _tmp_personPictureLct = _result.getLong("personPictureLct")
        val _tmp_personPictureUri = _result.getString("personPictureUri")
        val _tmp_personPictureThumbnailUri = _result.getString("personPictureThumbnailUri")
        val _tmp_fileSize = _result.getInt("fileSize")
        val _tmp_personPictureActive = _result.getBoolean("personPictureActive")
        PersonPicture().apply {
          this.personPictureUid = _tmp_personPictureUid
          this.personPictureLct = _tmp_personPictureLct
          this.personPictureUri = _tmp_personPictureUri
          this.personPictureThumbnailUri = _tmp_personPictureThumbnailUri
          this.fileSize = _tmp_fileSize
          this.personPictureActive = _tmp_personPictureActive
        }
      }
    }
  }

  override fun findByPersonUidLive(personUid: Long): Flow<PersonPicture?> =
      _db.doorFlow(arrayOf("PersonPicture")) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |         SELECT * 
      |           FROM PersonPicture 
      |          WHERE personPictureUid = ? 
      |          
      """.trimMargin(),
      readOnly = true,)
    ) { _stmt -> 
      _stmt.setLong(1,personUid)
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapNextRow(null) {
          val _tmp_personPictureUid = _result.getLong("personPictureUid")
          val _tmp_personPictureLct = _result.getLong("personPictureLct")
          val _tmp_personPictureUri = _result.getString("personPictureUri")
          val _tmp_personPictureThumbnailUri = _result.getString("personPictureThumbnailUri")
          val _tmp_fileSize = _result.getInt("fileSize")
          val _tmp_personPictureActive = _result.getBoolean("personPictureActive")
          PersonPicture().apply {
            this.personPictureUid = _tmp_personPictureUid
            this.personPictureLct = _tmp_personPictureLct
            this.personPictureUri = _tmp_personPictureUri
            this.personPictureThumbnailUri = _tmp_personPictureThumbnailUri
            this.fileSize = _tmp_fileSize
            this.personPictureActive = _tmp_personPictureActive
          }
        }
      }
    }
  }

  override fun findByPersonUidAsFlow(personUid: Long): Flow<PersonPicture?> =
      _db.doorFlow(arrayOf("PersonPicture")) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        SELECT * 
      |          FROM PersonPicture 
      |         WHERE personPictureUid = ?
      |          AND CAST(personPictureActive AS INTEGER) = 1
      |        LIMIT 1
      |        
      """.trimMargin(),
      readOnly = true,)
    ) { _stmt -> 
      _stmt.setLong(1,personUid)
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapNextRow(null) {
          val _tmp_personPictureUid = _result.getLong("personPictureUid")
          val _tmp_personPictureLct = _result.getLong("personPictureLct")
          val _tmp_personPictureUri = _result.getString("personPictureUri")
          val _tmp_personPictureThumbnailUri = _result.getString("personPictureThumbnailUri")
          val _tmp_fileSize = _result.getInt("fileSize")
          val _tmp_personPictureActive = _result.getBoolean("personPictureActive")
          PersonPicture().apply {
            this.personPictureUid = _tmp_personPictureUid
            this.personPictureLct = _tmp_personPictureLct
            this.personPictureUri = _tmp_personPictureUri
            this.personPictureThumbnailUri = _tmp_personPictureThumbnailUri
            this.fileSize = _tmp_fileSize
            this.personPictureActive = _tmp_personPictureActive
          }
        }
      }
    }
  }

  override suspend fun updateLct(uid: Long, time: Long) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        UPDATE PersonPicture
      |           SET personPictureLct = ?
      |         WHERE personPictureUid = ?   
      |    
      """.trimMargin(),
      readOnly = false,)
    ) { _stmt -> 
      _stmt.setLong(1,time)
      _stmt.setLong(2,uid)
      _stmt.executeUpdateAsyncKmp()
    }
  }

  override suspend fun updateUri(
    uid: Long,
    uri: String?,
    thumbnailUri: String?,
    time: Long,
  ) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        UPDATE PersonPicture
      |           SET personPictureUri = ?,
      |               personPictureThumbnailUri = ?,
      |               personPictureLct = ?
      |         WHERE personPictureUid = ?      
      |    
      """.trimMargin(),
      readOnly = false,)
    ) { _stmt -> 
      _stmt.setString(1,uri)
      _stmt.setString(2,thumbnailUri)
      _stmt.setLong(3,time)
      _stmt.setLong(4,uid)
      _stmt.executeUpdateAsyncKmp()
    }
  }

  override suspend fun updateTransferJobItemEtag(entityUid: Long, transferJobItemUid: Int) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        UPDATE TransferJobItem
      |           SET tjiEntityEtag = 
      |               (SELECT personPictureLct
      |                  FROM PersonPicture
      |                 WHERE personPictureUid = ?)
      |         WHERE tjiUid = ?      
      |    
      """.trimMargin(),
      readOnly = false,)
    ) { _stmt -> 
      _stmt.setLong(1,entityUid)
      _stmt.setInt(2,transferJobItemUid)
      _stmt.executeUpdateAsyncKmp()
    }
  }
}
