import { v } from 'convex/values';
import { internalMutation, query, mutation } from './_generated/server';
import type { Id } from './_generated/dataModel';
import { getCurrentUserFunction } from './auth';

/**
 * Listar todas as roles
 */
export const listar = query({
	args: {},
	handler: async (ctx) => {
		return await ctx.db.query('roles').collect();
	}
});

/**
 * Buscar role por ID
 */
export const buscarPorId = query({
	args: {
		roleId: v.id('roles')
	},
	returns: v.union(
		v.object({
			_id: v.id('roles'),
			nome: v.string(),
			descricao: v.string(),
			nivel: v.number(),
			setor: v.optional(v.string())
		}),
		v.null()
	),
	handler: async (ctx, args) => {
		return await ctx.db.get(args.roleId);
	}
});

const slugify = (value: string) =>
	value
		.normalize('NFD')
		.replace(/[\u0300-\u036f]/g, '')
		.trim()
		.toLowerCase()
		.replace(/[^a-z0-9]+/g, '_')
		.replace(/^_+|_+$/g, '')
		.replace(/_{2,}/g, '_');

export const criar = mutation({
	args: {
		nome: v.string(),
		descricao: v.string(),
		nivel: v.number(),
		setor: v.optional(v.string()),
		copiarDeRoleId: v.optional(v.id('roles'))
	},
	returns: v.union(
		v.object({ sucesso: v.literal(true), roleId: v.id('roles') }),
		v.object({ sucesso: v.literal(false), erro: v.string() })
	),
	handler: async (ctx, args) => {
		const usuarioAtual = await getCurrentUserFunction(ctx);
		if (!usuarioAtual) {
			return { sucesso: false as const, erro: 'nao_autenticado' };
		}

		const roleAtual = await ctx.db.get(usuarioAtual.roleId);
		if (!roleAtual || roleAtual.nivel > 1) {
			return { sucesso: false as const, erro: 'sem_permissao' };
		}

		const nomeNormalizado = slugify(args.nome);
		if (!nomeNormalizado) {
			return { sucesso: false as const, erro: 'nome_invalido' };
		}

		const existente = await ctx.db
			.query('roles')
			.withIndex('by_nome', (q) => q.eq('nome', nomeNormalizado))
			.unique();

		if (existente) {
			return { sucesso: false as const, erro: 'nome_ja_utilizado' };
		}

		let permissoesParaCopiar: Array<Id<'permissoes'>> = [];

		if (args.copiarDeRoleId) {
			const roleOrigem = await ctx.db.get(args.copiarDeRoleId);
			if (!roleOrigem) {
				return { sucesso: false as const, erro: 'role_origem_nao_encontrada' };
			}

			const permissoesOrigem = await ctx.db
				.query('rolePermissoes')
				.withIndex('by_role', (q) => q.eq('roleId', args.copiarDeRoleId!))
				.collect();

			permissoesParaCopiar = permissoesOrigem.map((item) => item.permissaoId);
		}

		// Agora só existem níveis 0 e 1.
		// 0 = máximo (acesso total), 1 = administrativo (também com acesso total).
		// Qualquer valor informado diferente de 0 é normalizado para 1.
		const nivelNormalizado = Math.round(args.nivel) <= 0 ? 0 : 1;
		const setor = args.setor?.trim();

		const roleId = await ctx.db.insert('roles', {
			nome: nomeNormalizado,
			descricao: args.descricao.trim() || args.nome.trim(),
			nivel: nivelNormalizado,
			setor: setor && setor.length > 0 ? setor : undefined,
			customizado: true,
			criadoPor: usuarioAtual._id,
			editavel: true
		});

		if (permissoesParaCopiar.length > 0) {
			for (const permissaoId of permissoesParaCopiar) {
				await ctx.db.insert('rolePermissoes', {
					roleId,
					permissaoId
				});
			}
		}

		return { sucesso: true as const, roleId };
	}
});

/**
 * Migração de níveis de roles para o novo modelo (apenas 0 e 1).
 * - Mantém níveis 0 e 1 como estão.
 * - Converte qualquer nível > 1 para 1.
 */
export const migrarNiveisRoles = internalMutation({
	args: {},
	returns: v.null(),
	handler: async (ctx) => {
		const roles = await ctx.db.query('roles').collect();

		for (const role of roles) {
			if (role.nivel <= 1) continue;

			await ctx.db.patch(role._id, {
				nivel: 1
			});
		}

		return null;
	}
});
