package world.respect.datalayer.db.school.xapi.entities

import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import kotlinx.serialization.Serializable
import world.respect.datalayer.ext.EPOCH
import kotlin.time.Clock
import kotlin.time.Instant

@Entity(
    indices = [
        Index("actorPersonUid", name = "idx_actorentity_uid_personuid"),
        Index("actorObjectType", name= "idx_actorentity_actorobjecttype"),
        Index("actorIsAnonGroup", name ="idx_actorentity_actorIsAnonGroup")
    ]
)

/**
 * Represents an xAPI Actor (Agent or Group). valious properties on a Statement are an actor that
 * can be a group or agent e.g. the actor themselves, instructor / team context, etc.
 *
 * Where the ActorEntity represents a group, then GroupMemberActorJoin is used to join the group
 * ActorEntity to a list of agent ActorEntities (members).
 *
 * @param actorUid For an agent or identified group, this is the XXHash64 of the mbox, openId, or
 *        "agentAccountName@agentHomePage" (e.g. username@https://example.com/path/ where
 *        https://example.com/path/ is the endpoint url) calculated as per XapiActor#identifierHash
 *
 *
 *
 * @param actorPersonUid the person uid (if any) for this actor. The Actor could be a group, in which
 *        case the personUid is zero. The actor could also from an outside system e.g. statements that
 *        were generated by a different system, in which case the person uid is also zero.
 *
 *        The Xapi Agents Resource ( as per
 *        https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#24-agents-resource )
 *        describes how different Agents can be linked to one person. This would apply when a person
 *        initially doesn't have a username (e.g. a student without login where the teacher is
 *        recording assignment marks, attendance, etc) and then a username is created. In this case
 *        there will be multiple actors which will all link to the same personUid.
 *
 * @param actorIsAnonGroup true if this is an anonymous group, false otherwise. Ordinarily storing
 *        this on its own is a violation of the DRY principle. This can be derived by checking if
 *        identifying properties are null, however that would make the standard group member join
 *        query much slower.
 *
 */
@Serializable
data class XapiActorEntity(
    @PrimaryKey(autoGenerate = true)
    val actorUid: Long = 0,

    val actorPersonUid: Long = 0,

    val actorName: String? = null,

    val actorMbox: String? = null,

    val actorMbox_sha1sum: String? = null,

    val actorOpenid: String? = null,

    val actorAccountName: String? = null,

    val actorAccountHomePage: String? = null,

    val actorStored: Instant = Clock.System.now(),

    val actorLastModified: Instant = Clock.System.now(),

    val actorGroupMembersLastUpdated: Instant = EPOCH,

    val actorIsAnonGroup: Boolean = false,

    val actorObjectType: XapiActorEntityTypeEnum = XapiActorEntityTypeEnum.AGENT,
) {
    companion object {
        const val TABLE_ID = 68
    }
}
