import { format } from 'date-fns';
import formatTemplate from 'template-format';
import { parse } from 'yaml';

import ellipsis from '#/utils/ellipsis.js';
import slugify from '#/utils/slugify.js';

const genderMap = {
	f: 'female',
	m: 'male',
	t: 'transsexual',
	o: 'other',
	u: null,
};

const propProcessors = {
	...Object.fromEntries(Object.entries({
		// aliases
		shoot: 'shootId',
	}).map(([key, alias]) => [key, (sceneInfo) => sceneInfo[alias]])),
	link: (sceneInfo, _options, env) => `${env.origin}/scene/${sceneInfo.id}/${sceneInfo.slug}`,
	channel: (sceneInfo) => sceneInfo.channel?.name || sceneInfo.network?.name,
	network: (sceneInfo) => sceneInfo.network?.name || sceneInfo.channel?.name,
	actors: (sceneInfo, options) => {
		const genders = (options.genders || 'fmtou').split('').map((genderKey) => genderMap[genderKey]);

		return sceneInfo.actors
			.filter((actor) => genders.includes(actor.gender))
			.map((actor) => {
				const curatedActor = {
					...actor,
					age: actor.ageThen,
					ageNow: actor.age,
					g: actor.gender?.charAt(0),
					G: actor.gender?.charAt(0).toUpperCase(),
					dateOfBirth: actor.dateOfBirth && format(actor.dateOfBirth, 'yyyy-MM-dd'),
				};

				if (options.format) {
					return formatTemplate(options.format, Object.fromEntries(Object.entries(curatedActor).map(([key, value]) => [key, value ?? '']))); // don't render `null`
				}

				if (options.details) {
					return options.details
						.map((parentDetail) => {
							if (parentDetail.details) { // already nested
								return {
									...parentDetail,
									details: parentDetail.details.map((detail) => {
										if (detail.key) {
											return detail;
										}

										return { key: detail };
									}),
								};
							}

							if (parentDetail.key) {
								return {
									details: [parentDetail],
								};
							}

							return {
								details: [{ key: parentDetail }],
							};
						})
						.map((parentDetail) => {
							const curatedDetails = parentDetail.details
								.filter((detail) => !!curatedActor[detail.key])
								.map((detail) => {
									if (detail.wrap) {
										return `${detail.wrap[0]}${curatedActor[detail.key]}${detail.wrap[1]}`;
									}

									return curatedActor[detail.key];
								})
								.join(parentDetail.delimiter ?? ' ');

							if (curatedDetails && parentDetail.wrap) {
								return `${parentDetail.wrap[0]}${curatedDetails}${parentDetail.wrap[1]}`;
							}

							return curatedDetails;
						})
						.filter((parentDetail) => !!parentDetail)
						.join(options.detailDelimiter ?? ' ');
				}

				return actor.name;
			});
	},
	tags: (sceneInfo, options) => sceneInfo.tags
		?.filter((tag) => {
			if (options.include && !options.include.includes(tag.slug)) {
				return false;
			}

			if (options.exclude?.includes(tag.slug)) {
				return false;
			}

			if (options.priority && tag.priority < options.priority) {
				return false;
			}

			return true;
		})
		.map((tag) => tag.name),
	movie: (sceneInfo) => sceneInfo.movies?.[0]?.title,
	date: (sceneInfo, options) => {
		const dateFormat = options.format || 'yyyy-MM-dd';

		if (sceneInfo.date) {
			return format(sceneInfo.date, dateFormat);
		}

		if (options.fallbackToAdded && sceneInfo.effectiveDate) {
			return format(sceneInfo.effectiveDate, dateFormat);
		}

		return null;
	},
	createdAt: (sceneInfo, options) => format(sceneInfo.createdAt, options.format || 'yyyy-MM-dd'),
};

function curateValue(value, item) {
	return [].concat(value).filter((listValue) => !!listValue).slice(0, item.limit || Infinity).map((listValue) => (item.slugify ? slugify(listValue, item.slugify) : listValue)).map((listValue) => ellipsis(listValue, item.slice || Infinity, item.ellipsis || '')).map((listValue) => `${item.wrap?.[0] || ''}${listValue}${item.wrap?.[1] || ''}`).join(item.delimit || ', ');
}

function traverseTemplate(chain, release, env, { delimit = ' ' } = {}) {
	const results = chain.reduce((result, item) => {
		const keys = typeof item === 'string' ? item : item.key;

		if ((item.channels && !item.channels.includes(release.channel?.slug)) || item.notChannels?.includes(release.channel?.slug)) {
			return result;
		}

		if ((item.networks && !item.networks.includes(release.network?.slug)) || item.notNetworks?.includes(release.network?.slug)) {
			return result;
		}

		if (item.text) {
			return result.concat(curateValue(item.text, item));
		}

		if (keys) {
			const value = keys.split('|').reduce((acc, key) => acc
				|| propProcessors[key]?.(release, typeof item === 'string' ? { key } : item, env)
				|| release[key], null);

			return result.concat(curateValue(value, item));
		}

		if (item.items) {
			const group = traverseTemplate(item.items, release, env, {
				delimit: item.delimit,
			});

			return result.concat(curateValue(group, item));
		}

		return result;
	}, []);

	if (results.length > 0) {
		// return `${wrap[0] || ''}${results.filter(Boolean).join(delimit)}${wrap[1] || ''}`;
		return results.filter(Boolean).join(delimit);
	}

	return '';
}

export default function processSummaryTemplate(template, release, env) {
	const chain = parse(template);

	return traverseTemplate(chain, release, env);
}
