import { Router } from 'express';
import omit from 'object.omit';

import {
	createActor,
	createActorRevision,
	fetchActorRevisions,
	fetchActors,
	fetchActorsById,
	mergeActors,
	reviewActorRevision,
} from '../actors.js';

import { fetchStashByUsernameAndSlug } from '../stashes.js';

export function curateActorsQuery(query) {
	return {
		query: query.q,
		gender: query.gender,
		age: query.age?.split(',').map((age) => Number(age)),
		dateOfBirth: query.dob && new Date(query.dob),
		dobType: ({ dob: 'dateOfBirth', bd: 'birthday' })[query.dobt] || 'birthday',
		cup: query.cup?.split(','),
		country: query.c,
		naturalBoobs: query.nb,
		height: query.height?.split(',').map((height) => Number(height)),
		weight: query.weight?.split(',').map((weight) => Number(weight)),
		requireAvatar: query.avatar,
		stashId: Number(query.stashId) || null,
		isGlobal: Object.hasOwn(query, 'global'),
		includeAliased: Object.hasOwn(query, 'alias'),
	};
}

export async function fetchActorsApi(req, res) {
	const {
		actors,
		countries,
		limit,
		total,
	} = await fetchActors(curateActorsQuery(req.query), {
		page: Number(req.query.page) || 1,
		limit: Number(req.query.limit) || 120,
		order: req.query.order?.split('.') || ['likes', 'desc'],
	}, req.user);

	res.send({
		actors,
		countries,
		limit,
		total,
	});
}

export const actorsSchema = `
	extend type Query {
		actors(
			query: String
			stash: String
			limit: Int! = 30
			page: Int! = 1
			order: [String!]
		): ActorsResult

		actor(
			id: Int!
		): Actor

		actorsById(
			ids: [Int!]!
		): [Actor]
	}

	type Country {
		alpha2: String
		name: String
	}

	type Location {
		country: Country
		city: String
		state: String
	}

	type ActorsResult {
		nodes: [Actor!]!
		total: Int
	}

	type Actor {
		id: Int!
		name: String
		slug: String
		allowGlobalMatch: Boolean
		gender: String
		dateOfBirth: Date
		dateOfDeath: Date
		age: Int
		ageFromBirth: Int
		ageThen: Int
		origin: Location
		residence: Location
		ethnicity: String
		height: Int
		bust: String
		hip: Int
		waist: Int
		naturalBoobs: Boolean
		naturalButt: Boolean
		penisLength: Int
		penisGirth: Int
		isCircumcised: Boolean
		eyes: String
		hairColor: String
		hasTattoos: Boolean
		tattoos: String
		hasPiercings: Boolean
		piercings: String
		agency: String
		entity: Entity
		aliases: [Actor!]!
		scenes: Int
		likes: Int
		stashes: [Stash!]!
	}
`;

function curateGraphqlActor(actor) {
	return {
		...actor,
		age: actor.ageFromBirth,
		height: actor.height?.metric,
		weight: actor.weight?.metric,
	};
}

function getOrder(query) {
	if (query.order) {
		return query.order;
	}

	if (query.query) {
		return ['results', 'desc'];
	}

	if (query.stash) {
		return ['stashed', 'desc'];
	}

	return ['likes', 'desc'];
}

export async function fetchActorsGraphql(query, req) {
	const stash = query.stash && req.user
		? await fetchStashByUsernameAndSlug(req.user.id, query.stash, req.user)
		: null;

	const {
		actors,
		total,
	} = await fetchActors({
		query: query.query,
		stashId: stash?.id,
	}, {
		limit: query.limit,
		page: query.page,
		order: getOrder(query),
		aggregateCountries: false,
	}, req.user);

	return {
		nodes: actors.map((actor) => curateGraphqlActor(actor)),
		total,
	};
}

export async function fetchActorsByIdGraphql(query, _req, _info) {
	const actors = await fetchActorsById([].concat(query.id, query.ids).filter(Boolean));
	const curatedActors = actors.map((actor) => curateGraphqlActor(actor));

	if (query.ids) {
		return curatedActors;
	}

	return curatedActors[0];
}

export async function createActorApi(req, res) {
	const actor = await createActor(req.body.actor, omit(req.body, ['actor']), req.user);

	res.send({ actor });
}

export async function mergeActorsApi(req, res) {
	const result = await mergeActors(
		Number(req.params.targetActorId),
		req.body.actorIds,
		req.body.comment,
		req.user,
	);

	res.send(result);
}

async function fetchActorRevisionsApi(req, res) {
	const revisions = await fetchActorRevisions(Number(req.params.revisionId) || null, req.query, req.user);

	res.send(revisions);
}

async function createActorRevisionApi(req, res) {
	await createActorRevision(Number(req.body.actorId), req.body, req.user);

	res.status(204).send();
}

async function reviewActorRevisionApi(req, res) {
	await reviewActorRevision(Number(req.params.revisionId), req.body.isApproved, req.body, req.user);

	res.status(204).send();
}

export const actorsRouter = Router();

actorsRouter.get('/api/actors', fetchActorsApi);
actorsRouter.post('/api/actors', createActorApi);

actorsRouter.post('/api/actors/:targetActorId/merge', mergeActorsApi);

actorsRouter.get('/api/revisions/actors', fetchActorRevisionsApi);
actorsRouter.get('/api/revisions/actors/:revisionId', fetchActorRevisionsApi);
actorsRouter.post('/api/revisions/actors', createActorRevisionApi);
actorsRouter.post('/api/revisions/actors/:revisionId/reviews', reviewActorRevisionApi);
