'use strict';

const logger = require('./logger')(__filename);
const knex = require('./knex');
const whereOr = require('./utils/where-or');

async function curateTag(tag) {
    const [aliases, media] = await Promise.all([
        knex('tags').where({ alias_for: tag.id }),
        knex('media')
            .where('domain', 'tags')
            .andWhere('target_id', tag.id)
            .orderBy('index'),
    ]);

    return {
        id: tag.id,
        name: tag.name,
        slug: tag.slug,
        description: tag.description,
        poster: media.find(photo => photo.role === 'poster'),
        photos: media.filter(photo => photo.role === 'photo'),
        group: {
            id: tag.group_id,
            name: tag.group_name,
            description: tag.group_description,
            slug: tag.group_slug,
        },
        aliases: aliases.map(({ name }) => name),
    };
}

function curateTags(tags) {
    return Promise.all(tags.map(async tag => curateTag(tag)));
}

async function matchTags(rawTags) {
    const filteredTags = rawTags.filter(Boolean);

    const tags = filteredTags
        .concat(filteredTags.map(tag => tag.toLowerCase()))
        .concat(filteredTags.map(tag => tag.toUpperCase()));

    const tagEntries = await knex('tags')
        .pluck('aliases.id')
        .whereIn('tags.name', tags)
        .leftJoin('tags as aliases', function join() {
            this
                .on('tags.alias_for', 'aliases.id')
                .orOn('tags.id', 'aliases.id');
        })
        .where(function where() {
            this
                .whereNull('tags.alias_for')
                .orWhereNull('aliases.alias_for');
        })
        .groupBy('aliases.id');

    return tagEntries;
}

async function associateTags(release, releaseId) {
    const siteTags = release.site.tags.filter(tag => tag.inherit === true).map(tag => tag.id);

    const rawReleaseTags = release.tags || [];
    const releaseTags = rawReleaseTags.some(tag => typeof tag === 'string')
        ? await matchTags(release.tags) // scraper returned raw tags
        : rawReleaseTags; // tags already matched by (outdated) scraper

    const tags = Array.from(new Set(releaseTags.concat(siteTags)));

    if (tags.length === 0) {
        logger.info(`No tags available for (${release.site.name}, ${releaseId}) "${release.title}"`);
        return;
    }

    const associationEntries = await knex('releases_tags')
        .where('release_id', releaseId)
        .whereIn('tag_id', tags);

    const existingAssociations = new Set(associationEntries.map(association => association.tag_id));
    const newAssociations = tags.filter(tagId => !existingAssociations.has(tagId));

    await knex('releases_tags').insert(newAssociations.map(tagId => ({
        tag_id: tagId,
        release_id: releaseId,
    })));
}

async function fetchTags(queryObject, groupsQueryObject, limit = 100) {
    const tags = await knex('tags')
        .where(builder => whereOr(queryObject, 'tags', builder))
        .orWhere(builder => whereOr(groupsQueryObject, 'tags_groups', builder))
        .andWhere({ 'tags.alias_for': null })
        .select(
            'tags.*',
            'tags_groups.id as group_id', 'tags_groups.name as group_name', 'tags_groups.slug as group_slug', 'tags_groups.description as groups_description',
        )
        .leftJoin('tags_groups', 'tags.group_id', 'tags_groups.id')
        .orderBy('name')
        .limit(limit);

    return curateTags(tags);
}

module.exports = {
    associateTags,
    fetchTags,
    matchTags,
};
