'use strict';

const fs = require('fs')
const axios = require('axios');
const AcmeClient = require('acme-client');
const sleep = require('./sleep');

// https://dns.google/resolve?name=${name}&type=TXT


class LetsEncrypt{
	static AcmeClient = AcmeClient;

	constructor(options){
		this.loadAccountKey(options.accountKeyPath || './le_key.cert', (key)=>{
			this.client = new AcmeClient.Client({
				directoryUrl: options.directoryUrl || AcmeClient.directory.letsencrypt.production,
				accountKey: key,
			});
		});
	}

	loadAccountKey(accountKeyPath, cb){
		try{
			// Load the account key if it exists
			cb(fs.readFileSync(accountKeyPath, 'utf8'));
		}catch(error){
			if(error.code === 'ENOENT'){
				// Generate a new account key if it doesn't exist
				AcmeClient.crypto.createPrivateKey().then(function(accountKey){
					fs.writeFileSync(accountKeyPath, accountKey.toString(), 'utf8');
					cb(accountKey.toString());
				});
			}else{
				throw error;
			}
		}
	}

	async dnsWildcard(domain, options){
		/*
		https://github.com/publishlab/node-acme-client/tree/master/examples/dns-01
		*/

		try{
			domain = domain.replace(/^\*\./, '');

			const [key, csr] = await AcmeClient.crypto.createCsr({
				altNames: [domain, `*.${domain}`],
			});

			let dnsToAdd = 0;
			let dnsFound = 0;

			const cert = await this.client.auto({
				csr,
				email: 'wmantly@gmail.com',
				termsOfServiceAgreed: true,
				challengePriority: ['dns-01'],
				skipChallengeVerification: true,
				challengeCreateFn: async (authz, challenge, keyAuthorization) => {
					try{
						dnsToAdd++;
						let resCheck = await axios.get(`https://dns.google/resolve?name=_acme-challenge.${authz.identifier.value}&type=TXT`);
						if(resCheck.data.Answer && resCheck.data.Answer.some(record => record.data === keyAuthorization)){
							dnsFound++;
							if(dnsFound === dnsToAdd){
								options.onDnsCheckFound(authz, dnsFound);
							}
							return;
						}

						await options.challengeCreateFn(authz, challenge, keyAuthorization);

						let checkCount = 0;
						while(true){
							options.onDnsCheck(authz, checkCount);
							let res = await axios.get(`https://dns.google/resolve?name=_acme-challenge.${authz.identifier.value}&type=TXT`);
							if(res.data.Answer && res.data.Answer.some(record => record.data === keyAuthorization)){
								dnsFound++;
								if(dnsFound === dnsToAdd){
									options.onDnsCheckFound(authz, dnsFound);
								}
								await sleep(10000);
								break;
							}
							if(checkCount++ > 60) throw new Error('challengeCreateFn validation timed out');
							await sleep(1500);
						}
					}catch(error){
						console.log('dns check failed error:', error)
						options.onDnsCheckFail(authz, error)
					}
				},
				challengeRemoveFn: options.challengeRemoveFn,
			});

			return {
				key,
				csr,
				cert,
			};

		}catch(error){
			console.log('Error in LetsEncrypt.dnsChallenge', error)
		}
	}
}

module.exports = LetsEncrypt;

if(require.main === module){(async function(){try{

	const tldExtract = require('tld-extract').parse_host;
	const PorkBun = require('./porkbun');
	const conf = require('@simpleworkjs/conf');

	let porkBun = new PorkBun(conf.porkBun.apiKey, conf.porkBun.secretApiKey);
	let letsEncrypt = new LetsEncrypt({
		directoryUrl: AcmeClient.directory.letsencrypt.staging,
	});

	let cert = await letsEncrypt.dnsWildcard('dev.test.holycore.quest', {
		challengeCreateFn: async (authz, challenge, keyAuthorization) => {
			let parts = tldExtract(authz.identifier.value);
			let res = await porkBun.createRecordForce(parts.domain, {type:'TXT', name: `_acme-challenge${parts.sub ? `.${parts.sub}` : ''}`, content: `${keyAuthorization}`});
		},
		challengeRemoveFn: async (authz, challenge, keyAuthorization)=>{
			let parts = tldExtract(authz.identifier.value);
			await porkBun.deleteRecords(parts.domain, {type:'TXT', name: `_acme-challenge${parts.sub ? `.${parts.sub}` : ''}`, content: `${keyAuthorization}`});
		},
	});

	console.log('IIFE csr', cert.csr.toString())
	// console.log(cert.cert.split('\n\n')[0])
	// console.log('IIFE cert info', +AcmeClient.crypto.readCertificateInfo(cert.cert).notAfter/1000,  'IIFE cert:\n', String(cert.cert).split('\n\n') , /*'IIFE privKey\n', cert.key.toString()*/);
}catch(error){
	console.log('IIFE Error:', error)
}})()}

















