const SunCalc = require("suncalc");

const athens = [37.9795, 23.7162];
const newcastle = [54.9738, -1.6132];

const oneDay = 24 * 60 * 60 * 1000;
const oneMonth = 29.53 * oneDay;

const sundial = (date, location) => {
	let x = SunCalc.getTimes(date, ...location);
	x.length = x.sunset - x.sunrise;
	return x;
};

const phase = date => SunCalc.getMoonIllumination(date).phase;

const noon = date => {
	let x = new Date(date);
	x.setHours(12, 0, 0, 0);
	if (date > sundial(x, newcastle).sunset) {
		x.setDate(x.getDate() + 1);
	}
	return x;
};

const addDays = (date, days) => {
	let x = new Date(date);
	x.setDate(x.getDate() + days);
	return x;
};

const scanNights = date => {
	const today = noon(date);
	const dawn = d => sundial(d, athens).dawn;

	let results = {
		lastNewMoon: undefined,
		nextNewMoon: undefined,
		dayOfMonth: undefined,
		signedDate: undefined
	};

	let currentNext = phase(dawn(today));
	let currentLast = phase(dawn(today));
	for (i = 1; results.lastNewMoon === undefined && i < 50; i++) {
		const yester = addDays(today, -i);
		if (phase(dawn(yester)) > currentLast) {
			results.lastNewMoon = addDays(today, -(i - 1));
		}
		currentLast = phase(dawn(yester));
	}
	for (i = 1; results.nextNewMoon === undefined && i < 50; i++) {
		const morrow = addDays(today, i);
		if (phase(dawn(morrow)) < currentNext) {
			results.nextNewMoon = addDays(today, i);
		}
		currentNext = phase(dawn(morrow));
	}

	results.dayOfMonth = Math.round((today - results.lastNewMoon) / oneDay) + 1;

	if (Math.round((results.nextNewMoon - today) / oneDay) == 1) {
		results.signedDate = 0;
	} else {
		results.signedDate =
			results.dayOfMonth > 20
				? results.dayOfMonth - 31
				: results.dayOfMonth;
	}

	return results;
};

const solstice = year => {
	let dates = [20, 21, 22].map(x => new Date(`${year}-06-${x}T12:00:00`));
	let dayLengths = dates.map(x => sundial(x, athens).length);

	let results = {
		midsummer: noon(dates[dayLengths.indexOf(Math.max(...dayLengths))])
	};

	results.newYear = scanNights(results.midsummer).nextNewMoon;

	return results;
};

const years = date => {
	const currentYear = date.getFullYear();
	const surroundingYears = [
		solstice(currentYear - 1).newYear,
		solstice(currentYear).newYear,
		solstice(currentYear + 1).newYear
	];

	if (date < surroundingYears[1]) {
		return surroundingYears.slice(0, 2);
	} else {
		return surroundingYears.slice(1, 3);
	}
};

const attic = gregorian => {
	gregorian = noon(gregorian);
	let output = {
		olympiad: undefined,
		year: undefined,
		month: undefined,
		rawDate: undefined,
		signedDate: undefined,
		weekday: gregorian.getDay()
	};

	const scanned = scanNights(gregorian);
	output.rawDate = scanned.dayOfMonth;
	output.signedDate = scanned.signedDate;
	const noumenia = scanned.lastNewMoon;
	const newYear = years(noumenia)[0];
	const isLeapYear =
		Math.round((years(noumenia)[1] - newYear) / oneMonth) == 13;

	output.month = Math.round((noumenia - newYear) / oneMonth);
	// Month 6 (zero-indexing from Hecatombæon) is always Poseideon II, so we bump the rest up if it’s not a leap year
	if (!isLeapYear && output.month >= 6) {
		output.month += 1;
	}

	const rawYear = newYear.getFullYear() - -775;
	output.olympiad = Math.floor(rawYear / 4) + 1;
	output.year = (rawYear % 4) + 1;

	return output;
};

module.exports = attic;