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

const logger = initLogger();

export function curateEntity(entity, context) {
	if (!entity) {
		return null;
	}

	return {
		id: entity.id,
		name: entity.name,
		slug: entity.slug,
		type: entity.type,
		url: entity.url,
		isIndependent: entity.independent,
		hasLogo: entity.has_logo,
		parent: curateEntity(entity.parent, context),
		children: context?.children?.filter((child) => child.parent_id === entity.id).map((child) => curateEntity({ ...child, parent: entity }, { parent: entity })) || [],
		...context?.append?.[entity.id],
	};
}

export async function fetchEntities(options) {
	const entities = await knex('entities')
		.modify((builder) => {
			if (options.type === 'primary') {
				builder
					.where('type', 'network')
					.orWhere('independent', true)
					.orWhereNull('parent_id');

				return;
			}

			if (options.type) {
				builder.where('type', options.type);
			}
		})
		.orderBy(...(options.order || ['name', 'asc']))
		.limit(options.limit || 1000);

	return entities.map((entityEntry) => curateEntity(entityEntry));
}

export async function fetchEntitiesById(entityIds, options = {}) {
	const [entities, children] = await Promise.all([
		knex('entities')
			.select('entities.*', knex.raw('row_to_json(parents) as parent'))
			.whereIn('entities.id', entityIds)
			.leftJoin('entities as parents', 'parents.id', 'entities.parent_id')
			.modify((builder) => {
				if (options.order) {
					builder.orderBy(...options.order);
				}
			})
			.groupBy('entities.id', 'parents.id'),
		options.includeChildren ? knex('entities')
			.whereIn('entities.parent_id', entityIds)
			.orderBy('slug') : [],
	]);

	if (options.order) {
		return entities.map((entityEntry) => curateEntity(entityEntry, {
			append: options.append,
			children,
		}));
	}

	const curatedEntities = entityIds.map((entityId) => {
		const entity = entities.find((entityEntry) => entityEntry.id === entityId);

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

		return curateEntity(entity, {
			append: options.append,
			children,
		});
	}).filter(Boolean);

	return curatedEntities;
}

export async function getEntityIdsBySlug(slugs, domain) {
	const ids = await Promise.all(slugs.map(async (slug) => {
		if (!slug) {
			return null;
		}

		if (Number(slug)) {
			return Number(slug); // already an ID or missing
		}

		const id = await redis.hGet(`traxxx:${domain}:id_by_slug`, slug);

		return Number(id);
	}));

	return ids.filter(Boolean);
}

export async function cacheEntityIds() {
	const entities = await knex('entities').select('id', 'slug', 'type');

	await redis.del('traxxx:entities:id_by_slug');
	await redis.hSet('traxxx:entities:id_by_slug', entities.map((entity) => [entity.type === 'network' ? `_${entity.slug}` : entity.slug, entity.id]));

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