const { Model } = require('flitter-orm')

// TODO - remove specific :create checks; auto-grant permissions on create

class PolicyModel extends Model {
    static get services() {
        return [...super.services, 'models', 'canon']
    }

    static get schema() {
        return {
            entity_type: String, // user | group
            entity_id: String,
            access_type: String, // allow | deny
            target_type: { type: String, default: 'application' }, // application | api_scope
            target_id: String,
            active: { type: Boolean, default: true },
        }
    }

    static async check_allow(entity_id, target_id) {
        const policies = await this.find({
            entity_id,
            target_id,
            access_type: 'allow',
            active: true,
        })

        return policies.length > 0
    }

    static async check_deny(entity_id, target_id) {
        const policies = await this.find({
            entity_id,
            target_id,
            access_type: 'deny',
            active: true,
        })

        return policies.length === 0
    }

    static async check_entity_access(entity_id, target_id) {
        return (await this.check_allow(entity_id, target_id)) && !(await this.check_deny(entity_id, target_id))
    }

    static async check_user_denied(user, target_id) {
        const groups = await user.groups()
        const group_ids = groups.map(x => x.id)

        const user_denials = await this.find({
            entity_id: user.id,
            target_id,
            access_type: 'deny',
            active: true,
        })

        const group_denials = await this.find({
            entity_id: { $in: group_ids },
            target_id,
            access_type: 'deny',
            active: true,
        })

        return user_denials.length > 0 || group_denials.length > 0
    }

    static async check_user_access(user, target_id) {
        const groups = await user.groups()
        const group_ids = groups.map(x => x.id)

        const user_approvals = await this.find({
            entity_id: user.id,
            target_id,
            access_type: 'allow',
            active: true,
        })

        const user_denials = await this.find({
            entity_id: user.id,
            target_id,
            access_type: 'deny',
            active: true,
        })

        const group_approvals = await this.find({
            entity_id: { $in: group_ids },
            target_id,
            access_type: 'allow',
            active: true,
        })

        const group_denials = await this.find({
            entity_id: { $in: group_ids },
            target_id,
            access_type: 'deny',
            active: true,
        })

        // IF user has explicit denial, deny
        if ( user_denials.length > 0 ) return false

        // ELSE IF user has explicit approval, approve
        if ( user_approvals.length > 0 ) return true

        // ELSE IF group has denial, deny
        if ( group_denials.length > 0 ) return false

        // ELSE IF group has approval, approve
        if ( group_approvals.length > 0 ) return true

        // ELSE deny
        return false
    }

    async to_api() {
        let entity_display = ''
        if ( this.entity_type === 'user' ) {
            const User = this.models.get('auth:User')
            const user = await User.findById(this.entity_id)
            entity_display = `User: ${user.last_name}, ${user.first_name} (${user.uid})`
        } else if ( this.entity_type === 'group' ) {
            const Group = this.models.get('auth:Group')
            const group = await Group.findById(this.entity_id)
            entity_display = `Group: ${group.name} (${group.user_ids.length} users)`
        }

        let target_display = ''
        if ( this.target_type === 'application' ) {
            const Application = this.models.get('Application')
            const app = await Application.findById(this.target_id)
            target_display = `Application: ${app.name}`
        } else if ( this.target_type === 'api_scope' ) {
            target_display = `API Scope: ${this.target_id}`
        }

        return {
            id: this.id,
            entity_display,
            entity_type: this.entity_type,
            entity_id: this.entity_id,
            access_type: this.access_type,
            target_display,
            target_type: this.target_type,
            target_id: this.target_id,
        }
    }
}

module.exports = exports = PolicyModel
