package com.ustadmobile.core.db.dao.xapi

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.jdbc.PreparedStatement
import com.ustadmobile.door.jdbc.ext.executeQueryAsyncKmp
import com.ustadmobile.door.jdbc.ext.executeUpdateAsyncKmp
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.xapi.ActivityLangMapEntry
import kotlin.Boolean
import kotlin.IllegalArgumentException
import kotlin.Long
import kotlin.String
import kotlin.collections.List

public class ActivityLangMapEntryDao_JdbcImpl(
  public val _db: RoomDatabase,
) : ActivityLangMapEntryDao() {
  public val _insertAdapterActivityLangMapEntry_upsert: EntityInsertionAdapter<ActivityLangMapEntry>
      = object : EntityInsertionAdapter<ActivityLangMapEntry>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT OR REPLACE INTO ActivityLangMapEntry (almeActivityUid, almeHash, almeLangCode, almeValue, almeAieHash, almeLastMod) VALUES(?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO ActivityLangMapEntry (almeActivityUid, almeHash, almeLangCode, almeValue, almeAieHash, almeLastMod) VALUES(?, ?, ?, ?, ?, ?) ON CONFLICT (almeActivityUid, almeHash) DO UPDATE SET almeLangCode = excluded.almeLangCode,almeValue = excluded.almeValue,almeAieHash = excluded.almeAieHash,almeLastMod = excluded.almeLastMod" + if(returnsId) { " RETURNING almeActivityUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: ActivityLangMapEntry) {
      stmt.setLong(1, entity.almeActivityUid)
      stmt.setLong(2, entity.almeHash)
      stmt.setString(3, entity.almeLangCode)
      stmt.setString(4, entity.almeValue)
      stmt.setLong(5, entity.almeAieHash)
      stmt.setLong(6, entity.almeLastMod)
    }
  }

  override suspend fun upsertList(entities: List<ActivityLangMapEntry>) {
    _insertAdapterActivityLangMapEntry_upsert.insertListAsync(entities)
  }

  override suspend fun upsertIfInteractionEntityExists(
    almeActivityUid: Long,
    almeHash: Long,
    almeLangCode: String?,
    almeValue: String?,
    almeAieHash: Long,
    almeLastMod: Long,
  ) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        INSERT OR REPLACE 
      |        INTO ActivityLangMapEntry(almeActivityUid, almeHash, almeLangCode, almeValue, almeAieHash, almeLastMod)
      |        SELECT ? AS almeActivityUid,
      |               ? AS almeHash,
      |               ? AS almeLangCode,
      |               ? AS almeValue,
      |               ? AS almeAieHash,
      |               ? AS almeLastMod
      |         WHERE EXISTS(SELECT 1
      |                        FROM ActivityInteractionEntity
      |                       WHERE ActivityInteractionEntity.aieActivityUid = ?
      |                         AND ActivityInteractionEntity.aieHash = ?)
      |          
      |    
      """.trimMargin(),
      postgreSql = """
      |
      |        INSERT 
      |        INTO ActivityLangMapEntry(almeActivityUid, almeHash, almeLangCode, almeValue, almeAieHash, almeLastMod)
      |        SELECT ? AS almeActivityUid,
      |               ? AS almeHash,
      |               ? AS almeLangCode,
      |               ? AS almeValue,
      |               ? AS almeAieHash,
      |               ? AS almeLastMod
      |         WHERE EXISTS(SELECT 1
      |                        FROM ActivityInteractionEntity
      |                       WHERE ActivityInteractionEntity.aieActivityUid = ?
      |                         AND ActivityInteractionEntity.aieHash = ?)
      |    
      |        ON CONFLICT(almeActivityUid, almeHash) DO UPDATE
      |        SET almeValue = EXCLUDED.almeValue,
      |            almeLastMod = EXCLUDED.almeLastMod
      |    
      """.trimMargin(),
      readOnly = false,)
    ) { _stmt -> 
      _stmt.setLong(1,almeActivityUid)
      _stmt.setLong(2,almeHash)
      _stmt.setString(3,almeLangCode)
      _stmt.setString(4,almeValue)
      _stmt.setLong(5,almeAieHash)
      _stmt.setLong(6,almeLastMod)
      _stmt.setLong(7,almeActivityUid)
      _stmt.setLong(8,almeAieHash)
      _stmt.executeUpdateAsyncKmp()
    }
  }

  override suspend fun updateIfChanged(
    almeActivityUid: Long,
    almeHash: Long,
    almeValue: String?,
    almeLastMod: Long,
  ) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        UPDATE ActivityLangMapEntry
      |           SET almeValue = ?,
      |               almeLastMod = ?
      |         WHERE almeActivityUid = ?
      |           AND almeHash = ?
      |           AND almeValue != ?       
      |    
      """.trimMargin(),
      readOnly = false,)
    ) { _stmt -> 
      _stmt.setString(1,almeValue)
      _stmt.setLong(2,almeLastMod)
      _stmt.setLong(3,almeActivityUid)
      _stmt.setLong(4,almeHash)
      _stmt.setString(5,almeValue)
      _stmt.executeUpdateAsyncKmp()
    }
  }

  override suspend fun findAllByActivityUid(activityUid: Long): List<ActivityLangMapEntry> =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT ActivityLangMapEntry.*
    |          FROM ActivityLangMapEntry
    |         WHERE ActivityLangMapEntry.almeActivityUid = ?
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,activityUid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapRows {
        val _tmp_almeActivityUid = _result.getLong("almeActivityUid")
        val _tmp_almeHash = _result.getLong("almeHash")
        val _tmp_almeLangCode = _result.getString("almeLangCode")
        val _tmp_almeValue = _result.getString("almeValue")
        val _tmp_almeAieHash = _result.getLong("almeAieHash")
        val _tmp_almeLastMod = _result.getLong("almeLastMod")
        ActivityLangMapEntry().apply {
          this.almeActivityUid = _tmp_almeActivityUid
          this.almeHash = _tmp_almeHash
          this.almeLangCode = _tmp_almeLangCode
          this.almeValue = _tmp_almeValue
          this.almeAieHash = _tmp_almeAieHash
          this.almeLastMod = _tmp_almeLastMod
        }
      }
    }
  }
}
