const Unit = require('libflitter/Unit')
const { Provider, interactionPolicy: { Prompt, base: policy } } = require('oidc-provider')
const uuid = require('uuid').v4
const CoreIDAdapter = require('../classes/oidc/CoreIDAdapter')
const RequestLocalizationHelper = require('flitter-i18n/src/RequestLocalizationHelper')
const ResponseSystemMiddleware = require('libflitter/routing/ResponseSystemMiddleware')

class OpenIDConnectUnit extends Unit {
    static get name() {
        return 'openid_connect'
    }

    static get services() {
        return [...super.services, 'output', 'configs', 'models']
    }

    async go(app) {
        this.Vue = this.app.di().get('Vue')
        const issuer = this.configs.get('app.url')
        const configuration = this.configs.get('oidc.provider')
        const interactions = policy()
        const User = this.models.get('auth:User')

        CoreIDAdapter.connect(app)

        this.provider = new Provider(issuer, {
            adapter: CoreIDAdapter,
            clients: [],
            interactions: {
                interactions,
                url: (ctx, interaction) => `/openid/interaction/${ctx.oidc.uid.toLowerCase()}`,
            },
            cookies: {
                long: { signed: true, maxAge: 24 * 60 * 60 * 1000 }, // 1 day, ms
                short: { signed: true },
                keys: [this.configs.get('server.session.secret') || uuid()]
            },
            claims: {
                email: ['email'],
                profile: [
                    'first_name', 'last_name', 'picture', 'tagline', 'username',
                ],
            },
            features: {
                devInteractions: { enabled: false },
                deviceFlow: { enabled: true },
                introspection: { enabled: true },
                revocation: { enabled: true },
            },
            ttl: {
                AccessToken: 60 * 60, // 1 hour in seconds
                AuthorizationCode: 10 * 60, // 10 minutes in seconds
                IdToken: 60 * 60, // 1 hour in seconds
                DeviceCode: 10 * 60, // 10 minutes in seconds
                RefreshToken: 24 * 60 * 60, // 1 day in seconds
            },
            findAccount: (...args) => User.findAccount(...args),
            ...configuration,
        })

        if ( configuration.proxy ) this.provider.proxy = true
        app.express.use('/oidc', this.wrap(this.provider.callback))
    }

    wrap(callback) {
        return async (req, res, next) => {
            const client_id = req?.query?.client_id

            // Provide some basic Flitter niceties in the request
            req.i18n = new RequestLocalizationHelper(req, res)
            new ResponseSystemMiddleware(this.app, res, req)

            // If we got a client ID, make sure the current user has access to it
            if ( req?.session?.auth?.user_id && client_id ) {
                const User = this.models.get('auth:User')
                const Application = this.models.get('Application')
                const Policy = this.models.get('iam:Policy')

                const user = await User.findById(req.session.auth.user_id)
                const application = await Application.findOne({ openid_client_ids: client_id })
                if ( !application ) {
                    return this.Vue.auth_message(res, {
                        message: req.T('saml.no_access').replace('APP_NAME', 'this application'),
                        next_destination: '/dash',
                    })
                } else if ( !(await Policy.check_user_access(user, application.id)) ) {
                    return this.Vue.auth_message(res, {
                        message: req.T('saml.no_access').replace('APP_NAME', application.name),
                        next_destination: '/dash',
                    })
                }
            }

            return callback(req, res, next)
        }
    }
}

module.exports = exports = OpenIDConnectUnit
