import knex from './knex.js';
import redis from './redis.js';
import initLogger from './logger.js';

import { curateEntity } from './entities.js';

const logger = initLogger();

function curateTag(tag, context) {
	return {
		id: tag.id,
		name: tag.name,
		slug: tag.slug,
		description: tag.description,
		priority: tag.priority,
		poster: tag.poster && {
			id: tag.poster.id,
			path: tag.poster.path,
			thumbnail: tag.poster.thumbnail,
			lazy: tag.poster.lazy,
			isS3: tag.poster.is_s3,
			comment: tag.poster.comment,
			entity: tag.poster.entity && curateEntity({
				...tag.poster.entity,
				parent: tag.poster.entity_parent,
			}),
		},
		...context?.append?.[tag.id],
	};
}

export async function fetchTags(options = {}) {
	const [tags, posters] = await Promise.all([
		knex('tags')
			.select('tags.*', 'tags_posters.media_id as poster_id')
			.modify((builder) => {
				if (!options.includeAliases) {
					builder.whereNull('alias_for');
				}
			}),
		knex('tags_posters')
			.select('tags_posters.tag_id', 'media.*', knex.raw('row_to_json(entities) as entity'), knex.raw('row_to_json(parents) as entity_parent'))
			.leftJoin('media', 'media.id', 'tags_posters.media_id')
			.leftJoin('entities', 'entities.id', 'media.entity_id')
			.leftJoin('entities as parents', 'entities.id', 'entities.parent_id'),
	]);

	const postersByTagId = Object.fromEntries(posters.map((poster) => [poster.tag_id, poster]));

	return tags.map((tagEntry) => curateTag({
		...tagEntry,
		poster: postersByTagId[tagEntry.id],
	}));
}

export async function fetchTagsById(tagIds, options = {}) {
	const [tags, posters] = await Promise.all([
		knex('tags')
			.whereIn('tags.id', tagIds.filter((tagId) => typeof tagId === 'number'))
			.orWhereIn('tags.slug', tagIds.filter((tagId) => typeof tagId === 'string'))
			.modify((builder) => {
				if (options.order) {
					builder.orderBy(...options.order);
				}
			}),
		knex('tags_posters')
			.select('tags_posters.tag_id', 'media.*', knex.raw('row_to_json(entities) as entity'), knex.raw('row_to_json(parents) as entity_parent'))
			.leftJoin('tags', 'tags.id', 'tags_posters.tag_id')
			.leftJoin('media', 'media.id', 'tags_posters.media_id')
			.leftJoin('entities', 'entities.id', 'media.entity_id')
			.leftJoin('entities as parents', 'parents.id', 'entities.parent_id')
			.whereIn('tags.id', tagIds.filter((tagId) => typeof tagId === 'number'))
			.orWhereIn('tags.slug', tagIds.filter((tagId) => typeof tagId === 'string')),
	]);

	const postersByTagId = Object.fromEntries(posters.map((poster) => [poster.tag_id, poster]));

	if (options.order) {
		return tags.map((tagEntry) => curateTag({
			...tagEntry,
			poster: postersByTagId[tagEntry.id],
		}, { append: options.append }));
	}

	const curatedTags = tagIds.map((tagId) => {
		const tag = tags.find((tagEntry) => (typeof tagId === 'string' ? tagEntry.slug === tagId : tagEntry.id === tagId));

		if (!tag) {
			console.warn(`Can't match tag ${tagId}`);
			return null;
		}

		return curateTag({
			...tag,
			poster: postersByTagId[tag.id],
		}, { append: options.append });
	}).filter(Boolean);

	return curatedTags;
}

export async function cacheTagIds() {
	const tags = await knex('tags')
		.select('id', 'slug')
		.whereNull('alias_for')
		.whereNotNull('slug');

	await redis.del('traxxx:tags:id_by_slug');
	await redis.hSet('traxxx:tags:id_by_slug', tags.map((tag) => [tag.slug, tag.id]));

	logger.info('Cached tag IDs by slug');
}
