const LASTFM_API_KEY = process.env.FEED_LASTFM_API_KEY || "";
const LASTFM_USERNAME = process.env.FEED_LASTFM_USERNAME || "";
const LASTFM_API = "https://ws.audioscrobbler.com/2.0/";

function formatItem({
	artist,
	image,
	name,
	playcount,
	url,
}: {
	artist: { "#text": string };
	image?: { "#text": string }[];
	name: string;
	playcount: string;
	url: string;
}) {
	return {
		name: artist ? `${name} by ${artist?.["#text"]}` : name,
		image: image ? image?.at(-1)?.["#text"] : undefined,
		playcount,
		url,
	};
}

function epochToIso(epoch: string) {
	const date = new Date(Number.parseInt(epoch) * 1000);
	return date?.toISOString();
}

function isoToYearAndWeek(iso: string) {
	const date = new Date(iso);
	const year = date.getFullYear();
	const firstOfYear = new Date(year, 0, 1);
	const daysSinceFirstOfYear = Math.floor(
		(+date - +firstOfYear) / (24 * 60 * 60 * 1000),
	);
	const week = Math.ceil((date.getDay() + 1 + daysSinceFirstOfYear) / 7);
	return {
		week,
		year,
	};
}

function listToHTML(
	arr: {
		name: string;
		playcount: string;
		url: string;
	}[],
) {
	return arr
		.map(
			({ name, playcount, url }) =>
				`<li><a href="${url}">${name}</a> (${playcount} plays)</li>`,
		)
		.join("");
}

async function lastFm(method = "") {
	const data = await fetch(
		`${LASTFM_API}?method=${method}&api_key=${LASTFM_API_KEY}&format=json&user=${LASTFM_USERNAME}`,
	);
	return await data.json();
}

async function getWeeklyChartList(limit = 5) {
	const data = await lastFm("user.getWeeklyChartList");
	return data?.weeklychartlist?.chart?.slice(limit * -1);
}

async function getWeeklyTrackChart(to: string, from: string, limit = 5) {
	const data = await lastFm(`user.getWeeklyTrackChart&from=${from}&to=${to}`);
	return data?.weeklytrackchart?.track?.slice(0, limit)?.map(formatItem);
}

async function getWeeklyAlbumChart(to: string, from: string, limit = 5) {
	const data = await lastFm(`user.getWeeklyAlbumChart&from=${from}&to=${to}`);
	return data?.weeklyalbumchart?.album?.slice(0, limit)?.map(formatItem);
}

async function getWeeklyArtistChart(to: string, from: string, limit = 5) {
	const data = await lastFm(`user.getWeeklyArtistChart&from=${from}&to=${to}`);
	return data?.weeklyartistchart?.artist?.slice(0, limit)?.map(formatItem);
}

async function buildReport() {
	const output = [];
	const weeks = await getWeeklyChartList();
	for (const week of weeks) {
		output.push({
			to: week.to,
			from: week.from,
			tracks: await getWeeklyTrackChart(week.to, week.from),
			albums: await getWeeklyAlbumChart(week.to, week.from),
			artists: await getWeeklyArtistChart(week.to, week.from),
		});
	}
	return output;
}

export default async function buildFeedItems() {
	const data = await buildReport();
	return data
		.map(({ to, from, tracks, albums, artists }) => {
			const trackList = listToHTML(tracks);
			const albumList = listToHTML(albums);
			const artistList = listToHTML(artists);
			const toIso = epochToIso(to);
			const fromIso = epochToIso(from);
			const { week, year } = isoToYearAndWeek(fromIso);

			if (!trackList && !albumList && !artistList) {
				return;
			}

			return {
				content_html: [
					artistList ? `<h2>Top Artists</h2><ol>${artistList}</ol>` : "",
					albumList ? `<h2>Top Albums</h2><ol>${albumList}</ol>` : "",
					trackList ? `<h2>Top Tracks</h2><ol>${trackList}</ol>` : "",
				].join(""),
				date_published: toIso,
				id: `last.fm-${from}-${to}`,
				title: "weekly last.fm report",
				url: `https://www.last.fm/user/ZicklePop/listening-report/year/${year}/week/${week}`,
				tags: ["last.fm"],
			};
		})
		.filter((o) => !!o);
}