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.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.ContentEntryVersion
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 ContentEntryVersionDao_JdbcImpl(
  public val _db: RoomDatabase,
) : ContentEntryVersionDao() {
  public val _insertAdapterContentEntryVersion_abort: EntityInsertionAdapter<ContentEntryVersion> =
      object : EntityInsertionAdapter<ContentEntryVersion>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT INTO ContentEntryVersion (cevUid, cevContentEntryUid, cevOpenUri, cevContentType, cevManifestUrl, cevSize, cevInActive, cevLastModified, cevLct, cevStorageSize, cevOriginalSize) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO ContentEntryVersion (cevUid, cevContentEntryUid, cevOpenUri, cevContentType, cevManifestUrl, cevSize, cevInActive, cevLastModified, cevLct, cevStorageSize, cevOriginalSize) VALUES(COALESCE(?,nextval('ContentEntryVersion_cevUid_seq')), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + if(returnsId) { " RETURNING cevUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: ContentEntryVersion) {
      if(entity.cevUid == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.cevUid)
      }
      stmt.setLong(2, entity.cevContentEntryUid)
      stmt.setString(3, entity.cevOpenUri)
      stmt.setString(4, entity.cevContentType)
      stmt.setString(5, entity.cevManifestUrl)
      stmt.setLong(6, entity.cevSize)
      stmt.setBoolean(7, entity.cevInActive)
      stmt.setLong(8, entity.cevLastModified)
      stmt.setLong(9, entity.cevLct)
      stmt.setLong(10, entity.cevStorageSize)
      stmt.setLong(11, entity.cevOriginalSize)
    }
  }

  override suspend fun insertAsync(contentEntryVersion: ContentEntryVersion): Long {
    val _retVal =
        _insertAdapterContentEntryVersion_abort.insertAndReturnIdAsync(contentEntryVersion)
    return _retVal
  }

  override suspend fun findByUidAsync(cevUid: Long): ContentEntryVersion? =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT ContentEntryVersion.*
    |          FROM ContentEntryVersion
    |         WHERE cevUid = ? 
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,cevUid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_cevUid = _result.getLong("cevUid")
        val _tmp_cevContentEntryUid = _result.getLong("cevContentEntryUid")
        val _tmp_cevOpenUri = _result.getString("cevOpenUri")
        val _tmp_cevContentType = _result.getString("cevContentType")
        val _tmp_cevManifestUrl = _result.getString("cevManifestUrl")
        val _tmp_cevSize = _result.getLong("cevSize")
        val _tmp_cevInActive = _result.getBoolean("cevInActive")
        val _tmp_cevLastModified = _result.getLong("cevLastModified")
        val _tmp_cevLct = _result.getLong("cevLct")
        val _tmp_cevStorageSize = _result.getLong("cevStorageSize")
        val _tmp_cevOriginalSize = _result.getLong("cevOriginalSize")
        ContentEntryVersion().apply {
          this.cevUid = _tmp_cevUid
          this.cevContentEntryUid = _tmp_cevContentEntryUid
          this.cevOpenUri = _tmp_cevOpenUri
          this.cevContentType = _tmp_cevContentType
          this.cevManifestUrl = _tmp_cevManifestUrl
          this.cevSize = _tmp_cevSize
          this.cevInActive = _tmp_cevInActive
          this.cevLastModified = _tmp_cevLastModified
          this.cevLct = _tmp_cevLct
          this.cevStorageSize = _tmp_cevStorageSize
          this.cevOriginalSize = _tmp_cevOriginalSize
        }
      }
    }
  }

  override suspend fun findLatestVersionUidByContentEntryUidEntity(contentEntryUid: Long):
      ContentEntryVersion? = _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT ContentEntryVersion.*
    |          FROM ContentEntryVersion
    |         WHERE ContentEntryVersion.cevContentEntryUid = ?
    |      ORDER BY ContentEntryVersion.cevLastModified DESC
    |         LIMIT 1
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,contentEntryUid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_cevUid = _result.getLong("cevUid")
        val _tmp_cevContentEntryUid = _result.getLong("cevContentEntryUid")
        val _tmp_cevOpenUri = _result.getString("cevOpenUri")
        val _tmp_cevContentType = _result.getString("cevContentType")
        val _tmp_cevManifestUrl = _result.getString("cevManifestUrl")
        val _tmp_cevSize = _result.getLong("cevSize")
        val _tmp_cevInActive = _result.getBoolean("cevInActive")
        val _tmp_cevLastModified = _result.getLong("cevLastModified")
        val _tmp_cevLct = _result.getLong("cevLct")
        val _tmp_cevStorageSize = _result.getLong("cevStorageSize")
        val _tmp_cevOriginalSize = _result.getLong("cevOriginalSize")
        ContentEntryVersion().apply {
          this.cevUid = _tmp_cevUid
          this.cevContentEntryUid = _tmp_cevContentEntryUid
          this.cevOpenUri = _tmp_cevOpenUri
          this.cevContentType = _tmp_cevContentType
          this.cevManifestUrl = _tmp_cevManifestUrl
          this.cevSize = _tmp_cevSize
          this.cevInActive = _tmp_cevInActive
          this.cevLastModified = _tmp_cevLastModified
          this.cevLct = _tmp_cevLct
          this.cevStorageSize = _tmp_cevStorageSize
          this.cevOriginalSize = _tmp_cevOriginalSize
        }
      }
    }
  }

  override fun findLatestByContentEntryUidAsFlow(contentEntryUid: Long): Flow<ContentEntryVersion?>
      = _db.doorFlow(arrayOf("ContentEntryVersion")) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        SELECT ContentEntryVersion.*
      |          FROM ContentEntryVersion
      |         WHERE ContentEntryVersion.cevContentEntryUid = ?
      |      ORDER BY ContentEntryVersion.cevLastModified DESC
      |         LIMIT 1
      |    
      """.trimMargin(),
      readOnly = true,)
    ) { _stmt -> 
      _stmt.setLong(1,contentEntryUid)
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapNextRow(null) {
          val _tmp_cevUid = _result.getLong("cevUid")
          val _tmp_cevContentEntryUid = _result.getLong("cevContentEntryUid")
          val _tmp_cevOpenUri = _result.getString("cevOpenUri")
          val _tmp_cevContentType = _result.getString("cevContentType")
          val _tmp_cevManifestUrl = _result.getString("cevManifestUrl")
          val _tmp_cevSize = _result.getLong("cevSize")
          val _tmp_cevInActive = _result.getBoolean("cevInActive")
          val _tmp_cevLastModified = _result.getLong("cevLastModified")
          val _tmp_cevLct = _result.getLong("cevLct")
          val _tmp_cevStorageSize = _result.getLong("cevStorageSize")
          val _tmp_cevOriginalSize = _result.getLong("cevOriginalSize")
          ContentEntryVersion().apply {
            this.cevUid = _tmp_cevUid
            this.cevContentEntryUid = _tmp_cevContentEntryUid
            this.cevOpenUri = _tmp_cevOpenUri
            this.cevContentType = _tmp_cevContentType
            this.cevManifestUrl = _tmp_cevManifestUrl
            this.cevSize = _tmp_cevSize
            this.cevInActive = _tmp_cevInActive
            this.cevLastModified = _tmp_cevLastModified
            this.cevLct = _tmp_cevLct
            this.cevStorageSize = _tmp_cevStorageSize
            this.cevOriginalSize = _tmp_cevOriginalSize
          }
        }
      }
    }
  }

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

  override suspend fun findContentEntryVersionsWithoutCacheLock(): List<ContentEntryVersion> =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT ContentEntryVersion.*
    |          FROM ContentEntryVersion
    |         WHERE NOT EXISTS(
    |               SELECT CacheLockJoin.cljId
    |                 FROM CacheLockJoin
    |                WHERE CacheLockJoin.cljTableId = 738
    |                  AND CacheLockJoin.cljEntityUid = ContentEntryVersion.cevUid
    |                  AND CacheLockJoin.cljUrl = ContentEntryVersion.cevManifestUrl) 
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapRows {
        val _tmp_cevUid = _result.getLong("cevUid")
        val _tmp_cevContentEntryUid = _result.getLong("cevContentEntryUid")
        val _tmp_cevOpenUri = _result.getString("cevOpenUri")
        val _tmp_cevContentType = _result.getString("cevContentType")
        val _tmp_cevManifestUrl = _result.getString("cevManifestUrl")
        val _tmp_cevSize = _result.getLong("cevSize")
        val _tmp_cevInActive = _result.getBoolean("cevInActive")
        val _tmp_cevLastModified = _result.getLong("cevLastModified")
        val _tmp_cevLct = _result.getLong("cevLct")
        val _tmp_cevStorageSize = _result.getLong("cevStorageSize")
        val _tmp_cevOriginalSize = _result.getLong("cevOriginalSize")
        ContentEntryVersion().apply {
          this.cevUid = _tmp_cevUid
          this.cevContentEntryUid = _tmp_cevContentEntryUid
          this.cevOpenUri = _tmp_cevOpenUri
          this.cevContentType = _tmp_cevContentType
          this.cevManifestUrl = _tmp_cevManifestUrl
          this.cevSize = _tmp_cevSize
          this.cevInActive = _tmp_cevInActive
          this.cevLastModified = _tmp_cevLastModified
          this.cevLct = _tmp_cevLct
          this.cevStorageSize = _tmp_cevStorageSize
          this.cevOriginalSize = _tmp_cevOriginalSize
        }
      }
    }
  }
}
