const Connection = require('flitter-orm/src/services/Connection')
const { ObjectId } = require('mongodb')

let DB

/**
 * An OpenID Connect provider adapter with some CoreID specific tweaks.
 */
class CoreIDAdapter {
    constructor(name) {
        this.name = 'openid_' + name
    }

    async upsert(_id, payload, expiresIn) {
        let expiresAt

        if (expiresIn) {
            expiresAt = new Date(Date.now() + (expiresIn * 1000))
        }

        if ( payload.uid ) {
            payload.originalUid = payload.uid
            payload.uid = payload.uid.toLowerCase()
        }

        await this.coll().updateOne(
            { _id },
            { $set: { payload, ...(expiresAt ? { expiresAt } : undefined) } },
            { upsert: true },
        )
    }

    async find(_id) {
        if ( this.name === 'openid_Client' ) _id = ObjectId(_id)

        const result = await this.coll().find(
            { _id },
            { payload: 1 },
        ).limit(1).next()

        if (!result) return undefined

        if ( result?.payload?.originalUid ) {
            result.payload.uid = result.payload.originalUid
        }

        return result.payload
    }

    async findByUserCode(userCode) {
        const result = await this.coll().find(
            { 'payload.userCode': userCode },
            { payload: 1 },
        ).limit(1).next()

        if (!result) return undefined
        return result.payload
    }

    async findByUid(uid) {
        const result = await this.coll().find(
            { 'payload.uid': uid.toLowerCase() },
            { payload: 1 },
        ).limit(1).next()

        if (!result) return undefined

        if ( result?.payload?.originalUid ) {
            result.payload.uid = result.payload.originalUid
        }

        return result.payload
    }

    async destroy(_id) {
        await this.coll().deleteOne({ _id })
    }

    async revokeByGrantId(grantId) {
        await this.coll().deleteMany({ 'payload.grantId': grantId })
    }

    async consume(_id) {
        await this.coll().findOneAndUpdate(
            { _id },
            { $set: { 'payload.consumed': Math.floor(Date.now() / 1000) } },
        )
    }

    coll(name) {
        return this.constructor.coll(name || this.name)
    }

    static coll(name) {
        return DB.collection(name)
    }

    static connect(app) {
        DB = app.di().get(Connection.name)
    }
}

module.exports = CoreIDAdapter

