const { Controller } = require('libflitter')
const uuid = require('uuid').v4

class ReflectController extends Controller {
    static get services() {
        return [...super.services, 'routers', 'models', 'activity']
    }

    async get_tokens(req, res, next) {
        const Oauth2BearerToken = this.models.get('auth::Oauth2BearerToken')
        const tokens = await Oauth2BearerToken.find({
            expires: { $gt: new Date },
            userID: req.user.id,
        })

        const Client = this.models.get('oauth:Client')
        const data = []
        for ( const token of tokens ) {
            const client = await Client.findOne({ uuid: token.clientID })
            let client_display = client && client.active ? client.name : req.T('api.nonexistent_client')

            data.push({
                id: token.id,
                token: token.accessToken,
                client_id: token.clientID,
                client_display,
                expires: token.expires,
                user_id: token.userID,
            })
        }

        return res.api(data)
    }

    async get_token(req, res, next) {
        const Oauth2BearerToken = this.models.get('auth::Oauth2BearerToken')
        const token = await Oauth2BearerToken.findById(req.params.id)

        if ( !token || token.userID !== req.user.id || token.expires <= new Date )
            return res.status(404)
                .message(req.T('api.token_not_found'))
                .api()

        return res.api({
            id: token.id,
            token: token.accessToken,
            client_id: token.clientID,
            expires: token.expires,
            user_id: token.userID,
        })
    }

    async create_token(req, res, next) {
        const Oauth2BearerToken = this.models.get('auth::Oauth2BearerToken')

        if ( !req.body.client_id )
            return res.status(400)
                .message(`${req.T('api.missing_field')} client_id`)
                .api()

        const Client = this.models.get('oauth:Client')
        const client = await Client.findOne({uuid: req.body.client_id})
        if ( !client || !client.active )
            return res.status(400)
                .message(`${req.T('common.invalid')} client_id.`)
                .api()

        if ( !req.user.can(`oauth:client:${client.id}:view`) )
            return res.status(401)
                .message(req.T('api.insufficient_permissions'))
                .api()

        const expires = new Date()
        expires.setDate(expires.getDate() + 7)

        const token = new Oauth2BearerToken({
            accessToken: String(uuid()).replace(/-/g, ''),
            clientID: client.uuid,
            expires,
            userID: req.user.id,
        })

        await token.save()
        await this.activity.api_token_created({ req, oauth_client_id: client.uuid })
        return res.api({
            id: token.id,
            token: token.accessToken,
            client_id: token.clientID,
            expires: token.expires,
            user_id: token.userID,
        })
    }

    async update_token(req, res, next) {
        const Oauth2BearerToken = this.models.get('auth::Oauth2BearerToken')
        const token = await Oauth2BearerToken.findById(req.params.id)

        if ( !token || token.userID !== req.user.id || token.expires <= new Date )
            return res.status(404)
                .message(req.T('api.token_not_found'))
                .api()

        if ( !req.body.client_id )
            return res.status(400)
                .message(`${req.T('api.missing_field')} client_id`)
                .api()

        const Client = this.models.get('oauth:Client')
        const client = await Client.findOne({uuid: req.body.client_id})

        if ( !client || !client.active || !req.user.can(`oauth:client:${client.id}:view`) )
            return res.status(400)
                .message(`${req.T('common.invalid')} client_id.`)
                .api()

        token.client_id = client.uuid
        await token.save()
        return res.api()
    }

    async delete_token(req, res, next) {
        const Oauth2BearerToken = this.models.get('auth::Oauth2BearerToken')
        const token = await Oauth2BearerToken.findById(req.params.id)

        if ( !token || token.userID !== req.user.id || token.expires <= new Date )
            return res.status(404)
                .message(req.T('api.token_not_found'))
                .api()

        await token.delete()
        return res.api()
    }

    api_scopes() {
        const routers = this.routers.canonical_items
        const scopes = []

        for ( const prefix in routers ) {
            if ( !routers.hasOwnProperty(prefix) ) continue
            const router = routers[prefix].schema

            const supported_verbs = ['get', 'post', 'put', 'delete', 'copy', 'patch']
            for ( const verb of supported_verbs ) {
                if ( typeof router[verb] === 'object' ) {
                    const defs = router[verb]
                    for ( const def of Object.values(defs) ) {
                        if ( Array.isArray(def) ) {
                            for ( const layer of def ) {
                                if ( Array.isArray(layer) && layer.length > 1 && layer[0] === 'middleware::api:Permission' ) {
                                    if ( typeof layer[1] === 'object' && layer[1].check ) {
                                        scopes.push(layer[1].check)
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        scopes.sort()
        return scopes
    }

    async get_scopes(req, res, next) {
        const scopes = this.api_scopes()
        return res.api(scopes.map(x => {
            return { scope: x }
        }))
    }

    async check_permissions(req, res, next) {
        if ( !req.body.permissions )
            return res.status(400)
                .message(`${req.T('api.missing_field')} permissions`)
                .api()

        const parsed = typeof req.body.permissions === 'string' ? this.utility.infer(req.body.permissions) : req.body.permissions
        const permissions = Array.isArray(parsed) ? parsed : [parsed]

        const returns = {}
        for ( const permission of permissions ) {
            returns[permission] = req.user.can(permission)
        }

        return res.api(returns)
    }
}

module.exports = exports = ReflectController
