const UserError = require('vn-loopback/util/user-error');
const validateTin = require('vn-loopback/util/validateTin');
const LoopBackContext = require('loopback-context');

module.exports = Self => {
    require('../methods/supplier/filter')(Self);
    require('../methods/supplier/getSummary')(Self);
    require('../methods/supplier/updateFiscalData')(Self);
    require('../methods/supplier/consumption')(Self);
    require('../methods/supplier/freeAgencies')(Self);
    require('../methods/supplier/campaignMetricsPdf')(Self);
    require('../methods/supplier/campaignMetricsEmail')(Self);
    require('../methods/supplier/newSupplier')(Self);

    Self.validatesPresenceOf('name', {
        message: 'The social name cannot be empty'
    });

    Self.validatesUniquenessOf('name', {
        message: 'The supplier name must be unique'
    });

    if (this.city) {
        Self.validatesPresenceOf('city', {
            message: 'City cannot be empty'
        });
    }

    if (this.nif) {
        Self.validatesPresenceOf('nif', {
            message: 'The nif cannot be empty'
        });
    }

    Self.validatesUniquenessOf('nif', {
        message: 'TIN must be unique'
    });

    Self.validateAsync('nif', tinIsValid, {
        message: 'Invalid TIN'
    });

    Self.validatesLengthOf('postCode', {
        allowNull: true,
        allowBlank: true,
        min: 3, max: 10
    });

    Self.validateAsync('postCode', hasValidPostcode, {
        message: `The postcode doesn't exist. Please enter a correct one`
    });

    async function hasValidPostcode(err, done) {
        if (!this.postcode)
            return done();

        const models = Self.app.models;
        const postcode = await models.Postcode.findById(this.postcode);

        if (!postcode) err();
        done();
    }

    async function tinIsValid(err, done) {
        if (!this.countryFk)
            return done();

        const filter = {
            fields: ['code'],
            where: {id: this.countryFk}
        };
        const country = await Self.app.models.Country.findOne(filter);
        const code = country ? country.code.toLowerCase() : null;

        if (!this.nif || !validateTin(this.nif, code))
            err();
        done();
    }

    function isAlpha(value) {
        const regexp = new RegExp(/^[ñça-zA-Z0-9\s]*$/i);

        return regexp.test(value);
    }

    Self.validateAsync('payMethodFk', hasSupplierAccount, {
        message: 'You can not select this payment method without a registered bankery account'
    });

    async function hasSupplierAccount(err, done) {
        if (!this.payMethodFk) return done();
        const payMethod = await Self.app.models.PayMethod.findById(this.payMethodFk);
        const supplierAccount = await Self.app.models.SupplierAccount.findOne({where: {supplierFk: this.id}});
        const hasIban = supplierAccount && supplierAccount.iban;
        const isMissingIban = payMethod && payMethod.isIbanRequiredForSuppliers && !hasIban;

        if (isMissingIban)
            err();

        done();
    }

    Self.observe('before save', async function(ctx) {
        if (ctx.isNewInstance) return;
        const loopbackContext = LoopBackContext.getCurrentContext();
        const changes = ctx.data || ctx.instance;
        const orgData = ctx.currentInstance;
        const userId = loopbackContext.active.accessToken.userId;

        const isNotFinancial = !await Self.app.models.Account.hasRole(userId, 'financial');
        const isPayMethodChecked = changes.isPayMethodChecked || orgData.isPayMethodChecked;
        const hasChanges = orgData && changes;
        const isPayMethodCheckedChanged = hasChanges
         && orgData.isPayMethodChecked != isPayMethodChecked;

        if (isNotFinancial && isPayMethodCheckedChanged)
            throw new UserError('You can not modify is pay method checked');
    });

    Self.observe('before save', async function(ctx) {
        const changes = ctx.data || ctx.instance;
        const orgData = ctx.currentInstance;

        const socialName = changes.name || orgData.name;
        const hasChanges = orgData && changes;
        const socialNameChanged = hasChanges
         && orgData.socialName != socialName;

        if ((socialNameChanged) && !isAlpha(socialName))
            throw new UserError('The social name has an invalid format');
    });
};