const axios = require('axios');

const apiUrl = 'https://api.cloudflare.com/client/v4'; // Endpoint 

const MailinaboxClass = require('./Mailinabox');

class CloudFlare {
    constructor(config) {
        this.config = config ? config : {};

        if (!this.config.arrTokens) {
            this.config.arrTokens = process.env.CLOUDFLARE_TOKENS;
        }
    }

    init() {
        if (this.config.arrTokens) {
            // this.zones = this.fetchCloudflareZones(this.config.apiToken);
            this.zones = [];
            this.dnsRecords = null;
        }
    }

    async fetchCloudflareZones(apiToken) {
        try {

            // Effettua una richiesta GET all'API di Cloudflare
            const response = await axios.get(apiUrl + '/zones', {
                headers: {
                    'Authorization': `Bearer ${apiToken}`, // Autenticazione con token
                    'Content-Type': 'application/json' // Tipo di contenuto
                }
            });

            // Estrai i dati dalla risposta
            this.zones = response.data.result;

            if (this.zones && this.zones.length > 0) {
                for (const zone of this.zones) {
                    let domain = zone.name;

                    await this.findAndUpdateOnSite(domain, zone, apiToken);

                    // Cerca su Site
                }
            }

            return this.zones;

            // Stampa le zone
            // console.log('Zone di Cloudflare:', zones);
        } catch (error) {
            console.error('Errore durante il recupero delle zone di Cloudflare:', error.message);
        }
    }

    async findAndUpdateOnSite(domain, zone, apiToken) {
        try {
            const Site = require('../models/site');

            domain = domain.replace(/^https?:\/\//, '');

            let normalizedDomain = domain.startsWith("http://") || domain.startsWith("https://") ? domain : "https://" + domain;

            // Trova il sito usando il dominio normalizzato
            const recsite = await Site.findOne({ host: normalizedDomain });


            if (recsite) {
                // Aggiorna i parametri token e zoneId

                let modif = (recsite.cf_token !== apiToken) || (recsite.cf_zoneId !== zone.id);

                if (modif && apiToken) {
                    // update the Site

                    await Site.findOneAndUpdate({ _id: recsite._id }, {
                        $set: {
                            cf_token: apiToken,
                            cf_zoneId: zone.id
                        }
                    }, { new: true })
                        .then((updatedSite) => {
                            console.log('Site aggiornato:', updatedSite);
                        })
                        .catch((error) => {
                            console.error('Errore durante l\'aggiornamento del Site:', error);
                        });
                }

                // Controlla se ho il DKIM !

                if (recsite.enable_servermail && !recsite.dkim) {
                    let miab = new MailinaboxClass(null);
                    miab.init();

                    const dkim = await miab.getDKIMRecord(recsite.host.replace(/^https?:\/\//, ''));

                    if (dkim) {
                        await Site.findOneAndUpdate({ _id: recsite._id }, {
                            $set: {
                                dkim,
                            }
                        })

                    }
                }
            }

        } catch (error) {
            console.error('Errore durante il recupero dei record DNS di Cloudflare:', error.message);
        }

    }

    // Funzione per estrarre i record DNS
    async fetchDNSRecords(apiToken, zoneId) {
        const apiUrlDNS = apiUrl + `/zones/${zoneId}/dns_records`;

        try {
            const response = await axios.get(apiUrlDNS, {
                headers: {
                    'Authorization': `Bearer ${apiToken}`, // Autenticazione con token
                    'Content-Type': 'application/json' // Tipo di contenuto
                }
            });

            this.dnsRecords = response.data.result;

            return this.dnsRecords;
        } catch (error) {
            console.error('Errore durante il recupero dei record DNS di Cloudflare:', error.message);
        }
    }

    // Funzione per aggiornare un record DNS
    async updateDNSRecord(apiToken, zoneId, dnsRecordId, newDnsRecordData) {
        const apiUrlDNS = apiUrl + `/zones/${zoneId}/dns_records/${dnsRecordId}`;

        try {

            const response = await axios.put(apiUrlDNS, newDnsRecordData, {
                headers: {
                    'Authorization': `Bearer ${apiToken}`, // Autenticazione con token
                    'Content-Type': 'application/json' // Tipo di contenuto
                }
            });

            const updatedRecord = response.data.result;

            // Stampa il record DNS aggiornato
            console.log('Record DNS aggiornato:', updatedRecord);

            return updatedRecord;
        } catch (error) {
            console.error('Errore durante l\'aggiornamento del record DNS:', error.message);
        }
    }

    // Funzione per creare un record DNS di CloudFlare
    async createDNSRecord(apiToken, zoneId, dnsRecordData) {
        const apiUrlDNS = apiUrl + `/zones/${zoneId}/dns_records`;
        try {
            const response = await axios.post(apiUrlDNS, dnsRecordData, {
                headers: {
                    'Authorization': `Bearer ${apiToken}`, // Autenticazione con token
                    'Content-Type': 'application/json' // Tipo di contenuto
                }
            });
            const createdRecord = response.data.result;
            console.log('Record DNS creato:', createdRecord);

            return createdRecord;
            // Stampa il record DNS creato
        } catch (error) {
            console.error('Errore durante la creazione del record DNS:', error.message);
        }
    }

    // Funzione per cancellare un record DNS di CloudFlare
    async deleteDNSRecord(apiToken, zoneId, dnsRecordId) {
        const apiUrlDNS = apiUrl + `/zones/${zoneId}/dns_records/${dnsRecordId}`;
        try {
            const response = await axios.delete(apiUrlDNS, {
                headers: {
                    'Authorization': `Bearer ${apiToken}`, // Autenticazione con token
                    'Content-Type': 'application/json' // Tipo di contenuto
                }
            });
            const deletedRecord = response.data.result;
            console.log('Record DNS cancellato:', deletedRecord);

            return deletedRecord;
        } catch (error) {
            console.error('Errore durante la cancellazione del record DNS:', error.message);
        }
    }


    async setCorrectIpsOnDNS(domainrec) {

        const arrparams = [{ urladd: '', paramsite: 'host_ip', type: 'A' },
        { urladd: 'test.', paramsite: 'host_test_ip', type: 'A' },
        { urladd: 'api.', paramsite: 'host_api_ip', type: 'A' },
        { urladd: 'testapi.', paramsite: 'host_testapi_ip', type: 'A' },
        { urladd: 'www.', paramsite: 'host', type: 'CNAME' },
        ]

        for (let i = 0; i < arrparams.length; i++) {
            await this.setSingleIpsOnDNS(domainrec.name, arrparams[i])
        }

        // Set Server Mail
        await this.setServerMail(domainrec);

        // pause for 3 second
        await new Promise(resolve => setTimeout(resolve, 3000));
    }

    async setServerMail(domainrec) {

        try {
            // get the parameters (Token and zoneId on Database)
            const Site = require('../models/site');

            let domain = domainrec.name.replace(/^https?:\/\//, '');

            let normalizedDomain = domain.startsWith("http://") || domain.startsWith("https://") ? domain : "https://" + domain;

            // Trova il sito usando il dominio normalizzato
            await Site.findOne({ host: normalizedDomain }).lean()
                .then(async (site) => {
                    if (site) {

                        if (site.cf_token && site.cf_zoneId) {
                            const myarrrecdns = await this.fetchDNSRecords(site.cf_token, site.cf_zoneId);

                            // MX:
                            let recTypeMX = await myarrrecdns.find((rec) => rec.type === 'MX' && (rec.name === (domain)));

                            if (recTypeMX) {
                                if (recTypeMX.content !== site.servermail) {
                                    recTypeMX.content = site.servermail;
                                    return this.updateDNSRecord(site.cf_token, site.cf_zoneId, recTypeMX.id, recTypeMX);
                                }
                            } else {
                                // create a new record
                                const newRecord = {
                                    type: 'MX',
                                    name: domain,
                                    content: site.servermail,
                                    ttl: 1,
                                    proxied: false,
                                    priority: 10,
                                };

                                await this.createDNSRecord(site.cf_token, site.cf_zoneId, newRecord);
                            }

                            // TXT record spf1 : TXT nomedominio v=spf1 a mx:box.lamiaposta.org ip4:65.21.63.147 ~all

                            let recTypeTXTspf1 = await myarrrecdns.find((rec) => rec.type === 'TXT' && (rec.name === (domain) && (rec.content.indexOf('v=spf1') > -1)));

                            if (site.servermailip) {
                                let contentTXTspf1 = `v=spf1 a mx:${site.servermail} ip4:${site.servermailip} ~all`;

                                if (recTypeTXTspf1) {
                                    if (recTypeTXTspf1.content !== contentTXTspf1) {
                                        recTypeTXTspf1.content = contentTXTspf1;
                                        return this.updateDNSRecord(site.cf_token, site.cf_zoneId, recTypeTXTspf1.id, recTypeTXTspf1);
                                    }
                                } else {
                                    // create a new record
                                    const newRecord = {
                                        type: 'TXT',
                                        name: domain,
                                        content: contentTXTspf1,
                                        ttl: 3600,
                                    };

                                    await this.createDNSRecord(site.cf_token, site.cf_zoneId, newRecord);
                                }
                            }

                            // mail._domainkey.nomedominio v=DKIM1; h=sha256; k=rsa; s=email; p=<dkim>

                            let nameDkimtoFind = `mail._domainkey.${domain}`;

                            let recTypeTXTDKIM = await myarrrecdns.find((rec) => rec.type === 'TXT' && (rec.name === nameDkimtoFind));

                            if (site.dkim) {
                                let contentTXTDKIM = `v=DKIM1; h=sha256; k=rsa; s=email; p=${site.dkim}`;

                                if (recTypeTXTDKIM) {
                                    if (recTypeTXTDKIM.content !== contentTXTDKIM) {
                                        recTypeTXTDKIM.content = contentTXTDKIM;
                                        return this.updateDNSRecord(site.cf_token, site.cf_zoneId, recTypeTXTDKIM.id, recTypeTXTDKIM);
                                    }
                                } else {
                                    // create a new record
                                    const newRecord = {
                                        type: 'TXT',
                                        name: nameDkimtoFind,
                                        content: contentTXTDKIM,
                                        ttl: 1,
                                    };

                                    await this.createDNSRecord(site.cf_token, site.cf_zoneId, newRecord);
                                }
                            }

                            // DMARC:

                            let nameDmarctoFind = `_dmarc.${domain}`;

                            let recTypeTXTdmarc = await myarrrecdns.find((rec) => rec.type === 'TXT' && (rec.name === (nameDmarctoFind) && (rec.content.indexOf('v=DMARC1') > -1)));

                            if (site.servermailip) {
                                let contentTXTdmarc = `v=DMARC1; p=quarantine; ruf=mailto:dmarc@${domain};`;

                                if (recTypeTXTdmarc) {
                                    if (recTypeTXTdmarc.content !== contentTXTdmarc) {
                                        recTypeTXTdmarc.content = contentTXTdmarc;
                                        return this.updateDNSRecord(site.cf_token, site.cf_zoneId, recTypeTXTspf1.id, recTypeTXTspf1);
                                    }
                                } else {
                                    // create a new record
                                    const newRecord = {
                                        type: 'TXT',
                                        name: nameDmarctoFind,
                                        content: contentTXTdmarc,
                                        ttl: 1,
                                    };

                                    await this.createDNSRecord(site.cf_token, site.cf_zoneId, newRecord);
                                }
                            }

                            // AutoConfig:
                            let myType = 'CNAME';
                            let strfind = `autoconfig.${domain}`;
                            let contentExpected = site.servermail;
                            let rectofind = await myarrrecdns.find((rec) => rec.type === 'CNAME' && (rec.name === strfind));

                            if (rectofind) {
                                if (rectofind.content !== contentExpected) {
                                    rectofind.content = contentExpected;
                                    return this.updateDNSRecord(site.cf_token, site.cf_zoneId, rectofind.id, rectofind);
                                }
                            } else {
                                // create a new record
                                const newRecord = {
                                    type: myType,
                                    name: strfind,
                                    content: contentExpected,
                                    ttl: 1,
                                };

                                await this.createDNSRecord(site.cf_token, site.cf_zoneId, newRecord);
                            }

                            // AutoDiscover:
                            myType = 'CNAME';
                            strfind = `autodiscover.${domain}`;
                            contentExpected = site.servermail;
                            rectofind = await myarrrecdns.find((rec) => rec.type === 'CNAME' && (rec.name === strfind));

                            if (rectofind) {
                                if (rectofind.content !== contentExpected) {
                                    rectofind.content = contentExpected;
                                    return this.updateDNSRecord(site.cf_token, site.cf_zoneId, rectofind.id, rectofind);
                                }
                            } else {
                                // create a new record
                                const newRecord = {
                                    type: myType,
                                    name: strfind,
                                    content: contentExpected,
                                    ttl: 1,
                                };

                                await this.createDNSRecord(site.cf_token, site.cf_zoneId, newRecord);
                            }

                        }
                    }
                });

        } catch (e) {
            console.error('e', e);
        }
    }

    async setSingleIpsOnDNS(domain, paramobj) {
        try {
            // get the parameters (Token and zoneId on Database)
            const Site = require('../models/site');

            domain = domain.replace(/^https?:\/\//, '');

            let normalizedDomain = domain.startsWith("http://") || domain.startsWith("https://") ? domain : "https://" + domain;

            // Trova il sito usando il dominio normalizzato
            await Site.findOne({ host: normalizedDomain }).lean()
                .then(async (site) => {
                    if (site) {

                        if (site.cf_token && site.cf_zoneId) {
                            const myarrrecdns = await this.fetchDNSRecords(site.cf_token, site.cf_zoneId);

                            let nametofind = paramobj.urladd + domain;

                            // find 
                            let recType = await myarrrecdns.find((rec) => rec.type === paramobj.type && (rec.name === nametofind));

                            let paramexpected = '';
                            try {
                                paramexpected = site[paramobj.paramsite].replace(/^https?:\/\//, '');;
                            } catch (e) {

                            }

                            if (recType && paramexpected) {
                                if (recType.content !== paramexpected) {
                                    recType.content = paramexpected;
                                    return this.updateDNSRecord(site.cf_token, site.cf_zoneId, recType.id, recType);
                                }
                            } else {
                                // create a new record
                                const newRecord = {
                                    type: paramobj.type,
                                    name: nametofind,
                                    content: paramexpected,
                                    ttl: 1,
                                    proxied: true,
                                };

                                await this.createDNSRecord(site.cf_token, site.cf_zoneId, newRecord);
                            }

                        }
                    }
                });

        } catch (e) {
            console.error('e', e);
        }
    }

}

module.exports = CloudFlare