const axios = require('axios');
const {DOMParser} = require('xmldom');
const UserError = require('vn-loopback/util/user-error');

module.exports = Self => {
    Self.remoteMethod('exchangeRateUpdate', {
        description: 'Updates the exchange rates from an XML feed',
        accessType: 'WRITE',
        accepts: [],
        http: {
            path: '/exchangeRateUpdate',
            verb: 'post'
        }
    });

    Self.exchangeRateUpdate = async() => {
        const response = await axios.get('http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml');
        const xmlData = response.data;

        const doc = new DOMParser({errorHandler: {warning: () => {}}})?.parseFromString(xmlData, 'text/xml');
        const cubes = doc?.getElementsByTagName('Cube');
        if (!cubes || cubes.length === 0)
            throw new UserError('No cubes found. Exiting the method.');

        const models = Self.app.models;

        const maxDateRecord = await models.ReferenceRate.findOne({order: 'dated DESC'});

        const maxDate = maxDateRecord?.dated ? new Date(maxDateRecord.dated) : null;

        for (const cube of Array.from(cubes)) {
            if (cube.nodeType === doc.ELEMENT_NODE && cube.attributes.getNamedItem('time')) {
                const xmlDate = new Date(cube.getAttribute('time'));
                const xmlDateWithoutTime = new Date(xmlDate.getFullYear(), xmlDate.getMonth(), xmlDate.getDate());
                if (!maxDate || maxDate < xmlDateWithoutTime) {
                    for (const rateCube of Array.from(cube.childNodes)) {
                        if (rateCube.nodeType === doc.ELEMENT_NODE) {
                            const currencyCode = rateCube.getAttribute('currency');
                            const rate = rateCube.getAttribute('rate');
                            if (['USD', 'CNY', 'GBP'].includes(currencyCode)) {
                                const currency = await models.Currency.findOne({where: {code: currencyCode}});
                                if (!currency) throw new UserError(`Currency not found for code: ${currencyCode}`);
                                const existingRate = await models.ReferenceRate.findOne({
                                    where: {currencyFk: currency.id, dated: xmlDate}
                                });

                                if (existingRate) {
                                    if (existingRate.value !== rate)
                                        await existingRate.updateAttributes({value: rate});
                                } else {
                                    await models.ReferenceRate.create({
                                        currencyFk: currency.id,
                                        dated: xmlDate,
                                        value: rate
                                    });
                                }
                                const monday = 1;
                                if (xmlDateWithoutTime.getDay() === monday) {
                                    const saturday = new Date(xmlDateWithoutTime);
                                    saturday.setDate(xmlDateWithoutTime.getDate() - 2);
                                    const sunday = new Date(xmlDateWithoutTime);
                                    sunday.setDate(xmlDateWithoutTime.getDate() - 1);

                                    for (const date of [saturday, sunday]) {
                                        await models.ReferenceRate.upsertWithWhere(
                                            {currencyFk: currency.id, dated: date},
                                            {currencyFk: currency.id, dated: date, value: rate}
                                        );
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    };
};