import { query, mutation } from './_generated/server';
import { internal } from './_generated/api';
import { v } from 'convex/values';
import { getCurrentUserFunction } from './auth';
import type { Id, Doc } from './_generated/dataModel';
import type { MutationCtx, QueryCtx } from './_generated/server';
import { flowTemplateStatus, flowInstanceStatus, flowInstanceStepStatus } from './schema';

// ============================================
// HELPER FUNCTIONS
// ============================================

/**
 * Criar notificações para todos os funcionários de um setor
 */
async function criarNotificacaoParaSetor(
	ctx: MutationCtx,
	setorId: Id<'setores'>,
	titulo: string,
	descricao: string
): Promise<void> {
	// Buscar funcionários do setor
	const funcionarioSetores = await ctx.db
		.query('funcionarioSetores')
		.withIndex('by_setorId', (q) => q.eq('setorId', setorId))
		.collect();

	// Para cada funcionário, buscar usuário correspondente e criar notificação
	for (const relacao of funcionarioSetores) {
		const funcionario = await ctx.db.get(relacao.funcionarioId);
		if (!funcionario) continue;

		// Buscar usuário por email
		const usuarios = await ctx.db.query('usuarios').collect();
		const usuario = usuarios.find((u: Doc<'usuarios'>) => u.email === funcionario.email);

		if (usuario) {
			await ctx.db.insert('notificacoes', {
				usuarioId: usuario._id,
				tipo: 'etapa_fluxo_concluida',
				titulo,
				descricao,
				lida: false,
				criadaEm: Date.now()
			});
		}
	}
}

/**
 * Obter a etapa anterior de um passo
 */
async function obterEtapaAnterior(
	ctx: MutationCtx | QueryCtx,
	instanceStepId: Id<'flowInstanceSteps'>
): Promise<Doc<'flowInstanceSteps'> | null> {
	const step = await ctx.db.get(instanceStepId);
	if (!step) {
		return null;
	}

	// Buscar todos os passos da instância
	const allSteps = await ctx.db
		.query('flowInstanceSteps')
		.withIndex('by_flowInstanceId', (q) => q.eq('flowInstanceId', step.flowInstanceId))
		.collect();

	// Obter posições de cada passo
	const stepsWithPosition: Array<{ step: Doc<'flowInstanceSteps'>; position: number }> = [];
	for (const s of allSteps) {
		const flowStep = await ctx.db.get(s.flowStepId);
		if (flowStep) {
			stepsWithPosition.push({ step: s, position: flowStep.position });
		}
	}

	// Ordenar por posição
	stepsWithPosition.sort((a, b) => a.position - b.position);

	// Encontrar posição do passo atual
	const currentIndex = stepsWithPosition.findIndex((s) => s.step._id === instanceStepId);

	// Se for o primeiro passo, não há etapa anterior
	if (currentIndex <= 0) {
		return null;
	}

	// Retornar etapa anterior
	return stepsWithPosition[currentIndex - 1].step;
}

/**
 * Verificar se usuário pertence a algum setor do fluxo
 */
async function usuarioPertenceAAlgumSetorDoFluxo(
	ctx: QueryCtx | MutationCtx,
	usuarioId: Id<'usuarios'>,
	flowInstanceId: Id<'flowInstances'>
): Promise<boolean> {
	// Buscar usuário
	const usuario = await ctx.db.get(usuarioId);
	if (!usuario) {
		return false;
	}

	// Buscar funcionário por email
	const funcionarios = await ctx.db.query('funcionarios').collect();
	const funcionario = funcionarios.find((f) => f.email === usuario.email);
	if (!funcionario) {
		return false;
	}

	// Buscar setores do funcionário
	const funcionarioSetores = await ctx.db
		.query('funcionarioSetores')
		.withIndex('by_funcionarioId', (q) => q.eq('funcionarioId', funcionario._id))
		.collect();

	const setoresDoFuncionario = new Set<Id<'setores'>>();
	for (const relacao of funcionarioSetores) {
		setoresDoFuncionario.add(relacao.setorId);
	}

	// Buscar todos os passos do fluxo
	const instanceSteps = await ctx.db
		.query('flowInstanceSteps')
		.withIndex('by_flowInstanceId', (q) => q.eq('flowInstanceId', flowInstanceId))
		.collect();

	// Extrair setores do fluxo
	const setoresDoFluxo = new Set<Id<'setores'>>();
	for (const step of instanceSteps) {
		setoresDoFluxo.add(step.setorId);
	}

	// Verificar interseção
	for (const setorId of setoresDoFuncionario) {
		if (setoresDoFluxo.has(setorId)) {
			return true;
		}
	}

	return false;
}

/**
 * Verificar se usuário tem permissão para ver todas as instâncias de fluxo
 */
async function verificarPermissaoVerTodasFluxos(ctx: QueryCtx | MutationCtx): Promise<boolean> {
	try {
		await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
			recurso: 'fluxos_instancias',
			acao: 'listar'
		});
		return true;
	} catch {
		return false;
	}
}

// ============================================
// FLOW TEMPLATES - CRUD
// ============================================

/**
 * Listar todos os templates de fluxo
 */
export const listTemplates = query({
	args: {
		status: v.optional(flowTemplateStatus)
	},
	returns: v.array(
		v.object({
			_id: v.id('flowTemplates'),
			_creationTime: v.number(),
			name: v.string(),
			description: v.optional(v.string()),
			status: flowTemplateStatus,
			createdBy: v.id('usuarios'),
			createdAt: v.number(),
			createdByName: v.optional(v.string()),
			stepsCount: v.number()
		})
	),
	handler: async (ctx, args) => {
		let templates;

		if (args.status) {
			templates = await ctx.db
				.query('flowTemplates')
				.withIndex('by_status', (q) => q.eq('status', args.status!))
				.order('desc')
				.collect();
		} else {
			templates = await ctx.db.query('flowTemplates').order('desc').collect();
		}

		const result: Array<{
			_id: Id<'flowTemplates'>;
			_creationTime: number;
			name: string;
			description: string | undefined;
			status: Doc<'flowTemplates'>['status'];
			createdBy: Id<'usuarios'>;
			createdAt: number;
			createdByName: string | undefined;
			stepsCount: number;
		}> = [];

		for (const template of templates) {
			const creator = await ctx.db.get(template.createdBy);
			const steps = await ctx.db
				.query('flowSteps')
				.withIndex('by_flowTemplateId', (q) => q.eq('flowTemplateId', template._id))
				.collect();

			result.push({
				_id: template._id,
				_creationTime: template._creationTime,
				name: template.name,
				description: template.description,
				status: template.status,
				createdBy: template.createdBy,
				createdAt: template.createdAt,
				createdByName: creator?.nome,
				stepsCount: steps.length
			});
		}

		return result;
	}
});

/**
 * Obter um template de fluxo pelo ID
 */
export const getTemplate = query({
	args: { id: v.id('flowTemplates') },
	returns: v.union(
		v.object({
			_id: v.id('flowTemplates'),
			_creationTime: v.number(),
			name: v.string(),
			description: v.optional(v.string()),
			status: flowTemplateStatus,
			createdBy: v.id('usuarios'),
			createdAt: v.number(),
			createdByName: v.optional(v.string())
		}),
		v.null()
	),
	handler: async (ctx, args) => {
		const template = await ctx.db.get(args.id);
		if (!template) return null;

		const creator = await ctx.db.get(template.createdBy);
		return {
			...template,
			createdByName: creator?.nome
		};
	}
});

/**
 * Criar um novo template de fluxo
 */
export const createTemplate = mutation({
	args: {
		name: v.string(),
		description: v.optional(v.string())
	},
	returns: v.id('flowTemplates'),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		const templateId = await ctx.db.insert('flowTemplates', {
			name: args.name,
			description: args.description,
			status: 'draft',
			createdBy: usuario._id,
			createdAt: Date.now()
		});

		return templateId;
	}
});

/**
 * Atualizar um template de fluxo
 */
export const updateTemplate = mutation({
	args: {
		id: v.id('flowTemplates'),
		name: v.optional(v.string()),
		description: v.optional(v.string()),
		status: v.optional(flowTemplateStatus)
	},
	returns: v.null(),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		const template = await ctx.db.get(args.id);
		if (!template) {
			throw new Error('Template não encontrado');
		}

		const updates: Partial<Doc<'flowTemplates'>> = {};
		if (args.name !== undefined) updates.name = args.name;
		if (args.description !== undefined) updates.description = args.description;
		if (args.status !== undefined) updates.status = args.status;

		await ctx.db.patch(args.id, updates);
		return null;
	}
});

/**
 * Excluir um template de fluxo
 */
export const deleteTemplate = mutation({
	args: { id: v.id('flowTemplates') },
	returns: v.null(),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		const template = await ctx.db.get(args.id);
		if (!template) {
			throw new Error('Template não encontrado');
		}

		// Verificar se há instâncias vinculadas
		const instancias = await ctx.db
			.query('flowInstances')
			.withIndex('by_flowTemplateId', (q) => q.eq('flowTemplateId', args.id))
			.first();
		if (instancias) {
			throw new Error('Não é possível excluir um template com instâncias vinculadas');
		}

		// Excluir todos os passos do template
		const steps = await ctx.db
			.query('flowSteps')
			.withIndex('by_flowTemplateId', (q) => q.eq('flowTemplateId', args.id))
			.collect();
		for (const step of steps) {
			await ctx.db.delete(step._id);
		}

		await ctx.db.delete(args.id);
		return null;
	}
});

// ============================================
// FLOW STEPS - CRUD
// ============================================

/**
 * Listar passos de um template
 */
export const listStepsByTemplate = query({
	args: { flowTemplateId: v.id('flowTemplates') },
	returns: v.array(
		v.object({
			_id: v.id('flowSteps'),
			_creationTime: v.number(),
			flowTemplateId: v.id('flowTemplates'),
			name: v.string(),
			description: v.optional(v.string()),
			position: v.number(),
			expectedDuration: v.number(),
			setorId: v.id('setores'),
			setorNome: v.optional(v.string()),
			defaultAssigneeId: v.optional(v.id('usuarios')),
			defaultAssigneeName: v.optional(v.string()),
			requiredDocuments: v.optional(v.array(v.string()))
		})
	),
	handler: async (ctx, args) => {
		const steps = await ctx.db
			.query('flowSteps')
			.withIndex('by_flowTemplateId', (q) => q.eq('flowTemplateId', args.flowTemplateId))
			.collect();

		// Ordenar por position
		steps.sort((a, b) => a.position - b.position);

		const result: Array<{
			_id: Id<'flowSteps'>;
			_creationTime: number;
			flowTemplateId: Id<'flowTemplates'>;
			name: string;
			description: string | undefined;
			position: number;
			expectedDuration: number;
			setorId: Id<'setores'>;
			setorNome: string | undefined;
			defaultAssigneeId: Id<'usuarios'> | undefined;
			defaultAssigneeName: string | undefined;
			requiredDocuments: string[] | undefined;
		}> = [];

		for (const step of steps) {
			const setor = await ctx.db.get(step.setorId);
			const assignee = step.defaultAssigneeId ? await ctx.db.get(step.defaultAssigneeId) : null;

			result.push({
				_id: step._id,
				_creationTime: step._creationTime,
				flowTemplateId: step.flowTemplateId,
				name: step.name,
				description: step.description,
				position: step.position,
				expectedDuration: step.expectedDuration,
				setorId: step.setorId,
				setorNome: setor?.nome,
				defaultAssigneeId: step.defaultAssigneeId,
				defaultAssigneeName: assignee?.nome,
				requiredDocuments: step.requiredDocuments
			});
		}

		return result;
	}
});

/**
 * Criar um novo passo
 */
export const createStep = mutation({
	args: {
		flowTemplateId: v.id('flowTemplates'),
		name: v.string(),
		description: v.optional(v.string()),
		expectedDuration: v.number(),
		setorId: v.id('setores'),
		defaultAssigneeId: v.optional(v.id('usuarios')),
		requiredDocuments: v.optional(v.array(v.string()))
	},
	returns: v.id('flowSteps'),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		// Verificar se o template existe
		const template = await ctx.db.get(args.flowTemplateId);
		if (!template) {
			throw new Error('Template não encontrado');
		}

		// Verificar se o setor existe
		const setor = await ctx.db.get(args.setorId);
		if (!setor) {
			throw new Error('Setor não encontrado');
		}

		// Obter a próxima posição
		const existingSteps = await ctx.db
			.query('flowSteps')
			.withIndex('by_flowTemplateId', (q) => q.eq('flowTemplateId', args.flowTemplateId))
			.collect();
		const maxPosition = existingSteps.reduce((max, step) => Math.max(max, step.position), 0);

		const stepId = await ctx.db.insert('flowSteps', {
			flowTemplateId: args.flowTemplateId,
			name: args.name,
			description: args.description,
			position: maxPosition + 1,
			expectedDuration: args.expectedDuration,
			setorId: args.setorId,
			defaultAssigneeId: args.defaultAssigneeId,
			requiredDocuments: args.requiredDocuments
		});

		return stepId;
	}
});

/**
 * Atualizar um passo
 */
export const updateStep = mutation({
	args: {
		id: v.id('flowSteps'),
		name: v.optional(v.string()),
		description: v.optional(v.string()),
		expectedDuration: v.optional(v.number()),
		setorId: v.optional(v.id('setores')),
		defaultAssigneeId: v.optional(v.id('usuarios')),
		requiredDocuments: v.optional(v.array(v.string()))
	},
	returns: v.null(),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		const step = await ctx.db.get(args.id);
		if (!step) {
			throw new Error('Passo não encontrado');
		}

		const updates: Partial<Doc<'flowSteps'>> = {};
		if (args.name !== undefined) updates.name = args.name;
		if (args.description !== undefined) updates.description = args.description;
		if (args.expectedDuration !== undefined) updates.expectedDuration = args.expectedDuration;
		if (args.setorId !== undefined) updates.setorId = args.setorId;
		if (args.defaultAssigneeId !== undefined) updates.defaultAssigneeId = args.defaultAssigneeId;
		if (args.requiredDocuments !== undefined) updates.requiredDocuments = args.requiredDocuments;

		await ctx.db.patch(args.id, updates);
		return null;
	}
});

/**
 * Reordenar passos
 */
export const reorderSteps = mutation({
	args: {
		flowTemplateId: v.id('flowTemplates'),
		stepIds: v.array(v.id('flowSteps'))
	},
	returns: v.null(),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		// Atualizar posições
		for (let i = 0; i < args.stepIds.length; i++) {
			await ctx.db.patch(args.stepIds[i], { position: i + 1 });
		}

		return null;
	}
});

/**
 * Excluir um passo
 */
export const deleteStep = mutation({
	args: { id: v.id('flowSteps') },
	returns: v.null(),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		const step = await ctx.db.get(args.id);
		if (!step) {
			throw new Error('Passo não encontrado');
		}

		await ctx.db.delete(args.id);
		return null;
	}
});

// ============================================
// FLOW INSTANCES
// ============================================

/**
 * Verificar permissões do usuário para um fluxo
 */
export const verificarPermissoesFluxo = query({
	args: { flowInstanceId: v.id('flowInstances') },
	returns: v.object({
		podeAtribuir: v.boolean(),
		podeEditar: v.boolean(),
		podeVer: v.boolean(),
		eCriador: v.boolean(),
		eGestor: v.boolean()
	}),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			return {
				podeAtribuir: false,
				podeEditar: false,
				podeVer: false,
				eCriador: false,
				eGestor: false
			};
		}

		const instance = await ctx.db.get(args.flowInstanceId);
		if (!instance) {
			return {
				podeAtribuir: false,
				podeEditar: false,
				podeVer: false,
				eCriador: false,
				eGestor: false
			};
		}

		// Verificar se é gestor
		const eGestor = instance.managerId === usuario._id;

		// Verificar se é criador do template
		const template = await ctx.db.get(instance.flowTemplateId);
		const eCriador = template?.createdBy === usuario._id;

		// Verificar permissão de ver todas
		const temPermissaoVerTodas = await verificarPermissaoVerTodasFluxos(ctx);

		// Verificar se pertence a setor do fluxo
		const pertenceAoSetor = await usuarioPertenceAAlgumSetorDoFluxo(
			ctx,
			usuario._id,
			args.flowInstanceId
		);

		// Pode ver se: tem permissão, é gestor, é criador, ou pertence ao setor
		const podeVer = temPermissaoVerTodas || eGestor || eCriador || pertenceAoSetor;

		// Pode editar se: é criador ou tem permissão de ver todas
		const podeEditar = eCriador || temPermissaoVerTodas;

		// Pode atribuir se: é criador, é gestor, ou tem permissão de ver todas
		const podeAtribuir = eCriador || eGestor || temPermissaoVerTodas;

		return {
			podeAtribuir,
			podeEditar,
			podeVer,
			eCriador,
			eGestor
		};
	}
});

/**
 * Listar instâncias de fluxo
 */
export const listInstances = query({
	args: {
		status: v.optional(flowInstanceStatus),
		contratoId: v.optional(v.id('contratos')),
		managerId: v.optional(v.id('usuarios'))
	},
	returns: v.array(
		v.object({
			_id: v.id('flowInstances'),
			_creationTime: v.number(),
			flowTemplateId: v.id('flowTemplates'),
			templateName: v.optional(v.string()),
			contratoId: v.optional(v.id('contratos')),
			managerId: v.id('usuarios'),
			managerName: v.optional(v.string()),
			status: flowInstanceStatus,
			startedAt: v.number(),
			finishedAt: v.optional(v.number()),
			currentStepId: v.optional(v.id('flowInstanceSteps')),
			currentStepName: v.optional(v.string()),
			progress: v.object({
				completed: v.number(),
				total: v.number()
			})
		})
	),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			return [];
		}

		// Verificar se usuário tem permissão para ver todas
		const temPermissaoVerTodas = await verificarPermissaoVerTodasFluxos(ctx);

		let instances;

		if (args.status) {
			instances = await ctx.db
				.query('flowInstances')
				.withIndex('by_status', (q) => q.eq('status', args.status!))
				.order('desc')
				.collect();
		} else if (args.managerId) {
			instances = await ctx.db
				.query('flowInstances')
				.withIndex('by_managerId', (q) => q.eq('managerId', args.managerId!))
				.order('desc')
				.collect();
		} else if (args.contratoId) {
			instances = await ctx.db
				.query('flowInstances')
				.withIndex('by_contratoId', (q) => q.eq('contratoId', args.contratoId!))
				.order('desc')
				.collect();
		} else {
			instances = await ctx.db.query('flowInstances').order('desc').collect();
		}

		const result: Array<{
			_id: Id<'flowInstances'>;
			_creationTime: number;
			flowTemplateId: Id<'flowTemplates'>;
			templateName: string | undefined;
			contratoId: Id<'contratos'> | undefined;
			managerId: Id<'usuarios'>;
			managerName: string | undefined;
			status: Doc<'flowInstances'>['status'];
			startedAt: number;
			finishedAt: number | undefined;
			currentStepId: Id<'flowInstanceSteps'> | undefined;
			currentStepName: string | undefined;
			progress: { completed: number; total: number };
		}> = [];

		for (const instance of instances) {
			// Se não tem permissão de ver todas, verificar se usuário pertence a algum setor do fluxo
			if (!temPermissaoVerTodas) {
				const pertenceAoSetor = await usuarioPertenceAAlgumSetorDoFluxo(
					ctx,
					usuario._id,
					instance._id
				);
				if (!pertenceAoSetor) {
					// Também verificar se é o manager
					if (instance.managerId !== usuario._id) {
						continue; // Pular esta instância
					}
				}
			}

			const template = await ctx.db.get(instance.flowTemplateId);
			const manager = await ctx.db.get(instance.managerId);

			// Obter passos da instância
			const instanceSteps = await ctx.db
				.query('flowInstanceSteps')
				.withIndex('by_flowInstanceId', (q) => q.eq('flowInstanceId', instance._id))
				.collect();

			const completedSteps = instanceSteps.filter((s) => s.status === 'completed').length;

			// Obter nome do passo atual
			let currentStepName: string | undefined;
			if (instance.currentStepId) {
				const currentStep = await ctx.db.get(instance.currentStepId);
				if (currentStep) {
					const flowStep = await ctx.db.get(currentStep.flowStepId);
					currentStepName = flowStep?.name;
				}
			}

			result.push({
				_id: instance._id,
				_creationTime: instance._creationTime,
				flowTemplateId: instance.flowTemplateId,
				templateName: template?.name,
				contratoId: instance.contratoId,
				managerId: instance.managerId,
				managerName: manager?.nome,
				status: instance.status,
				startedAt: instance.startedAt,
				finishedAt: instance.finishedAt,
				currentStepId: instance.currentStepId,
				currentStepName,
				progress: {
					completed: completedSteps,
					total: instanceSteps.length
				}
			});
		}

		return result;
	}
});

/**
 * Obter uma instância de fluxo com seus passos
 */
export const getInstanceWithSteps = query({
	args: { id: v.id('flowInstances') },
	returns: v.union(
		v.object({
			instance: v.object({
				_id: v.id('flowInstances'),
				_creationTime: v.number(),
				flowTemplateId: v.id('flowTemplates'),
				templateName: v.optional(v.string()),
				contratoId: v.optional(v.id('contratos')),
				managerId: v.id('usuarios'),
				managerName: v.optional(v.string()),
				status: flowInstanceStatus,
				startedAt: v.number(),
				finishedAt: v.optional(v.number()),
				currentStepId: v.optional(v.id('flowInstanceSteps')),
				prazoTotalDias: v.number()
			}),
			steps: v.array(
				v.object({
					_id: v.id('flowInstanceSteps'),
					_creationTime: v.number(),
					flowInstanceId: v.id('flowInstances'),
					flowStepId: v.id('flowSteps'),
					stepName: v.string(),
					stepDescription: v.optional(v.string()),
					setorId: v.id('setores'),
					setorNome: v.optional(v.string()),
					assignedToId: v.optional(v.id('usuarios')),
					assignedToName: v.optional(v.string()),
					status: flowInstanceStepStatus,
					startedAt: v.optional(v.number()),
					finishedAt: v.optional(v.number()),
					notes: v.optional(v.string()),
					notesUpdatedBy: v.optional(v.id('usuarios')),
					notesUpdatedByName: v.optional(v.string()),
					notesUpdatedAt: v.optional(v.number()),
					dueDate: v.optional(v.number()),
					position: v.number(),
					expectedDuration: v.number(),
					documents: v.array(
						v.object({
							_id: v.id('flowInstanceDocuments'),
							name: v.string(),
							uploadedAt: v.number(),
							uploadedByName: v.optional(v.string())
						})
					)
				})
			)
		}),
		v.null()
	),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			return null;
		}

		const instance = await ctx.db.get(args.id);
		if (!instance) return null;

		// Verificar permissão de visualização
		const temPermissaoVerTodas = await verificarPermissaoVerTodasFluxos(ctx);
		
		if (!temPermissaoVerTodas) {
			// Verificar se usuário pertence a algum setor do fluxo ou é o manager
			const pertenceAoSetor = await usuarioPertenceAAlgumSetorDoFluxo(
				ctx,
				usuario._id,
				instance._id
			);
			
			if (!pertenceAoSetor && instance.managerId !== usuario._id) {
				return null; // Usuário não tem acesso
			}
		}

		const template = await ctx.db.get(instance.flowTemplateId);
		const manager = await ctx.db.get(instance.managerId);

		// Obter passos da instância
		const instanceSteps = await ctx.db
			.query('flowInstanceSteps')
			.withIndex('by_flowInstanceId', (q) => q.eq('flowInstanceId', args.id))
			.collect();

		// Mapear passos com informações adicionais
		const stepsWithDetails: Array<{
			_id: Id<'flowInstanceSteps'>;
			_creationTime: number;
			flowInstanceId: Id<'flowInstances'>;
			flowStepId: Id<'flowSteps'>;
			stepName: string;
			stepDescription: string | undefined;
			setorId: Id<'setores'>;
			setorNome: string | undefined;
			assignedToId: Id<'usuarios'> | undefined;
			assignedToName: string | undefined;
			status: Doc<'flowInstanceSteps'>['status'];
			startedAt: number | undefined;
			finishedAt: number | undefined;
			notes: string | undefined;
			notesUpdatedBy: Id<'usuarios'> | undefined;
			notesUpdatedByName: string | undefined;
			notesUpdatedAt: number | undefined;
			dueDate: number | undefined;
			position: number;
			expectedDuration: number;
			documents: Array<{
				_id: Id<'flowInstanceDocuments'>;
				name: string;
				uploadedAt: number;
				uploadedByName: string | undefined;
			}>;
		}> = [];

		for (const step of instanceSteps) {
			const flowStep = await ctx.db.get(step.flowStepId);
			const setor = await ctx.db.get(step.setorId);
			const assignee = step.assignedToId ? await ctx.db.get(step.assignedToId) : null;

			// Obter documentos do passo
			const documents = await ctx.db
				.query('flowInstanceDocuments')
				.withIndex('by_flowInstanceStepId', (q) => q.eq('flowInstanceStepId', step._id))
				.collect();

			const docsWithUploader: Array<{
				_id: Id<'flowInstanceDocuments'>;
				name: string;
				uploadedAt: number;
				uploadedByName: string | undefined;
			}> = [];
			for (const doc of documents) {
				const uploader = await ctx.db.get(doc.uploadedById);
				docsWithUploader.push({
					_id: doc._id,
					name: doc.name,
					uploadedAt: doc.uploadedAt,
					uploadedByName: uploader?.nome
				});
			}

			const notesUpdater = step.notesUpdatedBy ? await ctx.db.get(step.notesUpdatedBy) : null;
			stepsWithDetails.push({
				_id: step._id,
				_creationTime: step._creationTime,
				flowInstanceId: step.flowInstanceId,
				flowStepId: step.flowStepId,
				stepName: flowStep?.name ?? 'Passo desconhecido',
				stepDescription: flowStep?.description,
				setorId: step.setorId,
				setorNome: setor?.nome,
				assignedToId: step.assignedToId,
				assignedToName: assignee?.nome,
				status: step.status,
				startedAt: step.startedAt,
				finishedAt: step.finishedAt,
				notes: step.notes,
				notesUpdatedBy: step.notesUpdatedBy,
				notesUpdatedByName: notesUpdater?.nome,
				notesUpdatedAt: step.notesUpdatedAt,
				dueDate: step.dueDate,
				position: flowStep?.position ?? 0,
				expectedDuration: flowStep?.expectedDuration ?? 0,
				documents: docsWithUploader
			});
		}

		// Ordenar por position
		stepsWithDetails.sort((a, b) => a.position - b.position);

		// Calcular prazo total do fluxo (soma de expectedDuration de todas as etapas)
		const prazoTotalDias = stepsWithDetails.reduce((sum, step) => sum + step.expectedDuration, 0);

		return {
			instance: {
				_id: instance._id,
				_creationTime: instance._creationTime,
				flowTemplateId: instance.flowTemplateId,
				templateName: template?.name,
				contratoId: instance.contratoId,
				managerId: instance.managerId,
				managerName: manager?.nome,
				status: instance.status,
				startedAt: instance.startedAt,
				finishedAt: instance.finishedAt,
				currentStepId: instance.currentStepId,
				prazoTotalDias
			},
			steps: stepsWithDetails as Array<{
				_id: Id<'flowInstanceSteps'>;
				_creationTime: number;
				flowInstanceId: Id<'flowInstances'>;
				flowStepId: Id<'flowSteps'>;
				stepName: string;
				stepDescription: string | undefined;
				setorId: Id<'setores'>;
				setorNome: string | undefined;
				assignedToId: Id<'usuarios'> | undefined;
				assignedToName: string | undefined;
				status: Doc<'flowInstanceSteps'>['status'];
				startedAt: number | undefined;
				finishedAt: number | undefined;
				notes: string | undefined;
				notesUpdatedBy: Id<'usuarios'> | undefined;
				notesUpdatedByName: string | undefined;
				notesUpdatedAt: number | undefined;
				dueDate: number | undefined;
				position: number;
				expectedDuration: number;
				documents: Array<{
					_id: Id<'flowInstanceDocuments'>;
					name: string;
					uploadedAt: number;
					uploadedByName: string | undefined;
				}>;
			}>
		};
	}
});

/**
 * Instanciar um fluxo
 */
export const instantiateFlow = mutation({
	args: {
		flowTemplateId: v.id('flowTemplates'),
		contratoId: v.optional(v.id('contratos')),
		managerId: v.id('usuarios')
	},
	returns: v.id('flowInstances'),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		// Verificar se o template existe e está publicado
		const template = await ctx.db.get(args.flowTemplateId);
		if (!template) {
			throw new Error('Template não encontrado');
		}
		if (template.status !== 'published') {
			throw new Error('Apenas templates publicados podem ser instanciados');
		}

		// Obter passos do template
		const templateSteps = await ctx.db
			.query('flowSteps')
			.withIndex('by_flowTemplateId', (q) => q.eq('flowTemplateId', args.flowTemplateId))
			.collect();

		if (templateSteps.length === 0) {
			throw new Error('O template não possui passos definidos');
		}

		// Ordenar por position
		templateSteps.sort((a, b) => a.position - b.position);

		const now = Date.now();

		// Criar a instância
		const instanceId = await ctx.db.insert('flowInstances', {
			flowTemplateId: args.flowTemplateId,
			contratoId: args.contratoId,
			managerId: args.managerId,
			status: 'active',
			startedAt: now
		});

		// Criar os passos da instância
		let firstStepId: Id<'flowInstanceSteps'> | undefined;
		let cumulativeDays = 0;

		for (let i = 0; i < templateSteps.length; i++) {
			const step = templateSteps[i];
			const dueDate = now + cumulativeDays * 24 * 60 * 60 * 1000 + step.expectedDuration * 24 * 60 * 60 * 1000;
			cumulativeDays += step.expectedDuration;

			const instanceStepId = await ctx.db.insert('flowInstanceSteps', {
				flowInstanceId: instanceId,
				flowStepId: step._id,
				setorId: step.setorId,
				assignedToId: step.defaultAssigneeId,
				status: i === 0 ? 'pending' : 'pending',
				dueDate
			});

			if (i === 0) {
				firstStepId = instanceStepId;
			}
		}

		// Atualizar o currentStepId da instância
		if (firstStepId) {
			await ctx.db.patch(instanceId, { currentStepId: firstStepId });
		}

		// Notificar gestor sobre criação do fluxo
		if (args.managerId) {
			await ctx.db.insert('notificacoes', {
				usuarioId: args.managerId,
				tipo: 'etapa_fluxo_concluida',
				titulo: 'Novo Fluxo Criado',
				descricao: `Um novo fluxo "${template?.name ?? 'Fluxo'}" foi criado e você foi designado como gestor.`,
				lida: false,
				criadaEm: Date.now()
			});
		}

		return instanceId;
	}
});

/**
 * Completar um passo da instância
 */
export const completeStep = mutation({
	args: {
		instanceStepId: v.id('flowInstanceSteps'),
		notes: v.optional(v.string())
	},
	returns: v.null(),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		const step = await ctx.db.get(args.instanceStepId);
		if (!step) {
			throw new Error('Passo não encontrado');
		}

		if (step.status === 'completed') {
			throw new Error('Este passo já foi concluído');
		}

		const instance = await ctx.db.get(step.flowInstanceId);
		if (!instance) {
			throw new Error('Instância não encontrada');
		}

		if (instance.status !== 'active') {
			throw new Error('Não é possível completar passos de uma instância inativa');
		}

		const now = Date.now();

		// Marcar passo como completado
		await ctx.db.patch(args.instanceStepId, {
			status: 'completed',
			finishedAt: now,
			notes: args.notes
		});

		// Obter todos os passos da instância
		const allSteps = await ctx.db
			.query('flowInstanceSteps')
			.withIndex('by_flowInstanceId', (q) => q.eq('flowInstanceId', step.flowInstanceId))
			.collect();

		// Encontrar o próximo passo pendente
		const flowSteps: Array<{ stepId: Id<'flowInstanceSteps'>; position: number }> = [];
		for (const s of allSteps) {
			const flowStep = await ctx.db.get(s.flowStepId);
			if (flowStep) {
				flowSteps.push({ stepId: s._id, position: flowStep.position });
			}
		}
		flowSteps.sort((a, b) => a.position - b.position);

		const currentPosition = flowSteps.findIndex((s) => s.stepId === args.instanceStepId);
		const nextStep = flowSteps[currentPosition + 1];

		// Obter informações do template e do passo para as notificações
		const template = await ctx.db.get(instance.flowTemplateId);
		const flowStep = await ctx.db.get(step.flowStepId);
		const setorAtual = await ctx.db.get(step.setorId);

		// Criar notificações para o setor do passo concluído
		if (setorAtual && flowStep) {
			const tituloSetorAtual = 'Etapa de Fluxo Concluída';
			const descricaoSetorAtual = `A etapa "${flowStep.name}" do fluxo "${template?.name ?? 'Fluxo'}" foi concluída.`;
			await criarNotificacaoParaSetor(ctx, step.setorId, tituloSetorAtual, descricaoSetorAtual);
		}

		// Notificar gestor sobre conclusão da etapa
		if (instance.managerId) {
			await ctx.db.insert('notificacoes', {
				usuarioId: instance.managerId,
				tipo: 'etapa_fluxo_concluida',
				titulo: 'Etapa de Fluxo Concluída',
				descricao: `A etapa "${flowStep?.name ?? 'Etapa'}" do fluxo "${template?.name ?? 'Fluxo'}" foi concluída.`,
				lida: false,
				criadaEm: Date.now()
			});
		}

		if (nextStep) {
			// Atualizar currentStepId para o próximo passo
			await ctx.db.patch(step.flowInstanceId, { currentStepId: nextStep.stepId });

			// Criar notificações para o setor do próximo passo
			const nextStepData = await ctx.db.get(nextStep.stepId);
			if (nextStepData) {
				const nextFlowStep = await ctx.db.get(nextStepData.flowStepId);
				const nextSetor = await ctx.db.get(nextStepData.setorId);

				if (nextSetor && nextFlowStep) {
					const tituloProximoSetor = 'Nova Etapa de Fluxo Disponível';
					const descricaoProximoSetor = `A etapa "${nextFlowStep.name}" do fluxo "${template?.name ?? 'Fluxo'}" está pronta para ser iniciada.`;
					await criarNotificacaoParaSetor(ctx, nextStepData.setorId, tituloProximoSetor, descricaoProximoSetor);
				}
			}
		} else {
			// Todos os passos concluídos, marcar instância como completada
			await ctx.db.patch(step.flowInstanceId, {
				status: 'completed',
				finishedAt: now,
				currentStepId: undefined
			});

			// Notificar gestor sobre conclusão do fluxo
			if (instance.managerId) {
				await ctx.db.insert('notificacoes', {
					usuarioId: instance.managerId,
					tipo: 'etapa_fluxo_concluida',
					titulo: 'Fluxo Concluído',
					descricao: `O fluxo "${template?.name ?? 'Fluxo'}" foi concluído com sucesso.`,
					lida: false,
					criadaEm: Date.now()
				});
			}
		}

		return null;
	}
});

/**
 * Atualizar status de um passo
 */
export const updateStepStatus = mutation({
	args: {
		instanceStepId: v.id('flowInstanceSteps'),
		status: flowInstanceStepStatus
	},
	returns: v.null(),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		const step = await ctx.db.get(args.instanceStepId);
		if (!step) {
			throw new Error('Passo não encontrado');
		}

		const updates: Partial<Doc<'flowInstanceSteps'>> = { status: args.status };

		if (args.status === 'in_progress' && !step.startedAt) {
			updates.startedAt = Date.now();

			// Notificar gestor sobre início da etapa
			const instance = await ctx.db.get(step.flowInstanceId);
			if (instance?.managerId) {
				const flowStep = await ctx.db.get(step.flowStepId);
				const template = await ctx.db.get(instance.flowTemplateId);
				await ctx.db.insert('notificacoes', {
					usuarioId: instance.managerId,
					tipo: 'etapa_fluxo_concluida',
					titulo: 'Etapa de Fluxo Iniciada',
					descricao: `A etapa "${flowStep?.name ?? 'Etapa'}" do fluxo "${template?.name ?? 'Fluxo'}" foi iniciada.`,
					lida: false,
					criadaEm: Date.now()
				});
			}
		}

		await ctx.db.patch(args.instanceStepId, updates);
		return null;
	}
});

/**
 * Alterar gestor do fluxo
 */
export const alterarGestorFluxo = mutation({
	args: {
		flowInstanceId: v.id('flowInstances'),
		novoManagerId: v.id('usuarios')
	},
	returns: v.null(),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		const instance = await ctx.db.get(args.flowInstanceId);
		if (!instance) {
			throw new Error('Fluxo não encontrado');
		}

		// Verificar se usuário atual é gestor ou tem permissão
		const eGestor = instance.managerId === usuario._id;
		const temPermissao = await verificarPermissaoVerTodasFluxos(ctx);
		const template = await ctx.db.get(instance.flowTemplateId);
		const eCriador = template?.createdBy === usuario._id;

		if (!eGestor && !temPermissao && !eCriador) {
			throw new Error('Somente o gestor atual, criador do template ou usuário com permissão pode alterar o gestor');
		}

		// Verificar se novo gestor existe
		const novoGestor = await ctx.db.get(args.novoManagerId);
		if (!novoGestor) {
			throw new Error('Usuário não encontrado');
		}

		// Atualizar gestor
		await ctx.db.patch(args.flowInstanceId, {
			managerId: args.novoManagerId
		});

		// Criar notificação para novo gestor
		await ctx.db.insert('notificacoes', {
			usuarioId: args.novoManagerId,
			tipo: 'etapa_fluxo_concluida',
			titulo: 'Você foi designado como gestor de um fluxo',
			descricao: `Você foi designado como gestor do fluxo "${template?.name ?? 'Fluxo'}"`,
			lida: false,
			criadaEm: Date.now()
		});

		return null;
	}
});

/**
 * Reatribuir responsável de um passo
 */
export const reassignStep = mutation({
	args: {
		instanceStepId: v.id('flowInstanceSteps'),
		assignedToId: v.id('usuarios')
	},
	returns: v.null(),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		const step = await ctx.db.get(args.instanceStepId);
		if (!step) {
			throw new Error('Passo não encontrado');
		}

		// Verificar se o usuário existe
		const assignee = await ctx.db.get(args.assignedToId);
		if (!assignee) {
			throw new Error('Usuário não encontrado');
		}

		// Verificar se o usuário atual pode atribuir esta etapa
		const instance = await ctx.db.get(step.flowInstanceId);
		if (!instance) {
			throw new Error('Instância não encontrada');
		}

		// Verificar se é criador do template
		const template = await ctx.db.get(instance.flowTemplateId);
		const eCriador = template?.createdBy === usuario._id;

		// Se for criador, permitir atribuição
		if (!eCriador) {
			// Se não for criador, verificar regra normal
			const etapaAnterior = await obterEtapaAnterior(ctx, args.instanceStepId);
			
			if (etapaAnterior) {
				// Se há etapa anterior, verificar se o usuário atual é a pessoa atribuída
				if (etapaAnterior.assignedToId) {
					if (etapaAnterior.assignedToId !== usuario._id) {
						throw new Error('Somente a pessoa da etapa anterior pode atribuir esta etapa');
					}
				}
				// Se a etapa anterior não tem atribuição, permitir (vai para o setor)
			} else {
				// Se não há etapa anterior (primeiro passo), verificar se é o manager
				// Se não for o manager, verificar permissão
				if (instance.managerId !== usuario._id) {
					const temPermissao = await verificarPermissaoVerTodasFluxos(ctx);
					if (!temPermissao) {
						throw new Error('Somente o gerente do fluxo ou pessoa da etapa anterior pode atribuir esta etapa');
					}
				}
			}
		}

		// Verificar se o funcionário atribuído pertence ao setor do passo
		const funcionarioSetores = await ctx.db
			.query('funcionarioSetores')
			.withIndex('by_setorId', (q) => q.eq('setorId', step.setorId))
			.collect();

		// Buscar funcionários vinculados a este setor
		const funcionariosDoSetor = [];
		for (const relacao of funcionarioSetores) {
			const funcionario = await ctx.db.get(relacao.funcionarioId);
			if (funcionario) {
				funcionariosDoSetor.push(funcionario);
			}
		}

		// Verificar se o usuário atribuído corresponde a um funcionário do setor
		const funcionarioDoUsuario = funcionariosDoSetor.find(
			(f) => f.email === assignee.email
		);

		if (!funcionarioDoUsuario) {
			throw new Error('O funcionário atribuído não pertence ao setor deste passo');
		}

		await ctx.db.patch(args.instanceStepId, { assignedToId: args.assignedToId });
		return null;
	}
});

/**
 * Atualizar notas de um passo
 */
export const updateStepNotes = mutation({
	args: {
		instanceStepId: v.id('flowInstanceSteps'),
		notes: v.string()
	},
	returns: v.null(),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		const step = await ctx.db.get(args.instanceStepId);
		if (!step) {
			throw new Error('Passo não encontrado');
		}

		await ctx.db.patch(args.instanceStepId, { 
			notes: args.notes,
			notesUpdatedBy: usuario._id,
			notesUpdatedAt: Date.now()
		});
		return null;
	}
});

/**
 * Cancelar uma instância de fluxo
 */
export const cancelInstance = mutation({
	args: { id: v.id('flowInstances') },
	returns: v.null(),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		const instance = await ctx.db.get(args.id);
		if (!instance) {
			throw new Error('Instância não encontrada');
		}

		if (instance.status !== 'active') {
			throw new Error('Apenas instâncias ativas podem ser canceladas');
		}

		await ctx.db.patch(args.id, {
			status: 'cancelled',
			finishedAt: Date.now()
		});

		return null;
	}
});

// ============================================
// SUB-ETAPAS
// ============================================

/**
 * Listar sub-etapas de um step
 */
export const listarSubEtapas = query({
	args: {
		flowStepId: v.optional(v.id('flowSteps')),
		flowInstanceStepId: v.optional(v.id('flowInstanceSteps'))
	},
	returns: v.array(
		v.object({
			_id: v.id('flowSubSteps'),
			_creationTime: v.number(),
			flowStepId: v.optional(v.id('flowSteps')),
			flowInstanceStepId: v.optional(v.id('flowInstanceSteps')),
			name: v.string(),
			description: v.optional(v.string()),
			status: v.union(
				v.literal('pending'),
				v.literal('in_progress'),
				v.literal('completed'),
				v.literal('blocked')
			),
			position: v.number(),
			createdBy: v.id('usuarios'),
			createdByName: v.optional(v.string())
		})
	),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			return [];
		}

		let subEtapas;
		if (args.flowStepId) {
			subEtapas = await ctx.db
				.query('flowSubSteps')
				.withIndex('by_flowStepId', (q) => q.eq('flowStepId', args.flowStepId))
				.collect();
		} else if (args.flowInstanceStepId) {
			subEtapas = await ctx.db
				.query('flowSubSteps')
				.withIndex('by_flowInstanceStepId', (q) => q.eq('flowInstanceStepId', args.flowInstanceStepId))
				.collect();
		} else {
			return [];
		}

		// Ordenar por position
		subEtapas.sort((a, b) => a.position - b.position);

		// Adicionar nome do criador
		const subEtapasComCriador = await Promise.all(
			subEtapas.map(async (subEtapa) => {
				const criador = await ctx.db.get(subEtapa.createdBy);
				return {
					_id: subEtapa._id,
					_creationTime: subEtapa._creationTime,
					flowStepId: subEtapa.flowStepId,
					flowInstanceStepId: subEtapa.flowInstanceStepId,
					name: subEtapa.name,
					description: subEtapa.description,
					status: subEtapa.status,
					position: subEtapa.position,
					createdBy: subEtapa.createdBy,
					createdByName: criador?.nome
				};
			})
		);

		return subEtapasComCriador;
	}
});

/**
 * Criar sub-etapa
 */
export const criarSubEtapa = mutation({
	args: {
		flowStepId: v.optional(v.id('flowSteps')),
		flowInstanceStepId: v.optional(v.id('flowInstanceSteps')),
		name: v.string(),
		description: v.optional(v.string())
	},
	returns: v.id('flowSubSteps'),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		if (!args.flowStepId && !args.flowInstanceStepId) {
			throw new Error('É necessário fornecer flowStepId ou flowInstanceStepId');
		}

		// Verificar se o step existe
		if (args.flowStepId) {
			const flowStep = await ctx.db.get(args.flowStepId);
			if (!flowStep) {
				throw new Error('Passo do template não encontrado');
			}
		}

		if (args.flowInstanceStepId) {
			const flowInstanceStep = await ctx.db.get(args.flowInstanceStepId);
			if (!flowInstanceStep) {
				throw new Error('Passo da instância não encontrado');
			}
		}

		// Obter posição máxima
		let maxPosition = 0;
		if (args.flowStepId) {
			const existingSubEtapas = await ctx.db
				.query('flowSubSteps')
				.withIndex('by_flowStepId', (q) => q.eq('flowStepId', args.flowStepId))
				.collect();
			if (existingSubEtapas.length > 0) {
				maxPosition = Math.max(...existingSubEtapas.map((s) => s.position));
			}
		} else if (args.flowInstanceStepId) {
			const existingSubEtapas = await ctx.db
				.query('flowSubSteps')
				.withIndex('by_flowInstanceStepId', (q) => q.eq('flowInstanceStepId', args.flowInstanceStepId))
				.collect();
			if (existingSubEtapas.length > 0) {
				maxPosition = Math.max(...existingSubEtapas.map((s) => s.position));
			}
		}

		const subEtapaId = await ctx.db.insert('flowSubSteps', {
			flowStepId: args.flowStepId,
			flowInstanceStepId: args.flowInstanceStepId,
			name: args.name,
			description: args.description,
			status: 'pending',
			position: maxPosition + 1,
			createdBy: usuario._id,
			createdAt: Date.now()
		});

		return subEtapaId;
	}
});

/**
 * Atualizar sub-etapa
 */
export const atualizarSubEtapa = mutation({
	args: {
		subEtapaId: v.id('flowSubSteps'),
		name: v.optional(v.string()),
		description: v.optional(v.string()),
		status: v.optional(
			v.union(
				v.literal('pending'),
				v.literal('in_progress'),
				v.literal('completed'),
				v.literal('blocked')
			)
		)
	},
	returns: v.null(),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		const subEtapa = await ctx.db.get(args.subEtapaId);
		if (!subEtapa) {
			throw new Error('Sub-etapa não encontrada');
		}

		const updates: Partial<Doc<'flowSubSteps'>> = {};
		if (args.name !== undefined) {
			updates.name = args.name;
		}
		if (args.description !== undefined) {
			updates.description = args.description;
		}
		if (args.status !== undefined) {
			updates.status = args.status;
		}

		await ctx.db.patch(args.subEtapaId, updates);
		return null;
	}
});

/**
 * Deletar sub-etapa
 */
export const deletarSubEtapa = mutation({
	args: { subEtapaId: v.id('flowSubSteps') },
	returns: v.null(),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		const subEtapa = await ctx.db.get(args.subEtapaId);
		if (!subEtapa) {
			throw new Error('Sub-etapa não encontrada');
		}

		await ctx.db.delete(args.subEtapaId);
		return null;
	}
});

/**
 * Reordenar sub-etapas
 */
export const reordenarSubEtapas = mutation({
	args: {
		subEtapaIds: v.array(v.id('flowSubSteps'))
	},
	returns: v.null(),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		// Atualizar posições
		for (let i = 0; i < args.subEtapaIds.length; i++) {
			await ctx.db.patch(args.subEtapaIds[i], { position: i + 1 });
		}

		return null;
	}
});

// ============================================
// NOTAS
// ============================================

/**
 * Listar notas de um step ou sub-etapa
 */
export const listarNotas = query({
	args: {
		flowStepId: v.optional(v.id('flowSteps')),
		flowInstanceStepId: v.optional(v.id('flowInstanceSteps')),
		flowSubStepId: v.optional(v.id('flowSubSteps'))
	},
	returns: v.array(
		v.object({
			_id: v.id('flowStepNotes'),
			_creationTime: v.number(),
			flowStepId: v.optional(v.id('flowSteps')),
			flowInstanceStepId: v.optional(v.id('flowInstanceSteps')),
			flowSubStepId: v.optional(v.id('flowSubSteps')),
			texto: v.string(),
			criadoPor: v.id('usuarios'),
			criadoPorNome: v.optional(v.string()),
			criadoEm: v.number(),
			arquivos: v.array(
				v.object({
					storageId: v.id('_storage'),
					name: v.string()
				})
			)
		})
	),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			return [];
		}

		let notas;
		if (args.flowStepId) {
			notas = await ctx.db
				.query('flowStepNotes')
				.withIndex('by_flowStepId', (q) => q.eq('flowStepId', args.flowStepId))
				.collect();
		} else if (args.flowInstanceStepId) {
			notas = await ctx.db
				.query('flowStepNotes')
				.withIndex('by_flowInstanceStepId', (q) => q.eq('flowInstanceStepId', args.flowInstanceStepId))
				.collect();
		} else if (args.flowSubStepId) {
			notas = await ctx.db
				.query('flowStepNotes')
				.withIndex('by_flowSubStepId', (q) => q.eq('flowSubStepId', args.flowSubStepId))
				.collect();
		} else {
			return [];
		}

		// Ordenar por data de criação (mais recente primeiro)
		notas.sort((a, b) => b.criadoEm - a.criadoEm);

		// Adicionar nome do criador e informações dos arquivos
		const notasComDetalhes = await Promise.all(
			notas.map(async (nota) => {
				const criador = await ctx.db.get(nota.criadoPor);
				
				// Obter informações dos arquivos
				const arquivosComNome = await Promise.all(
					nota.arquivos.map(async (storageId) => {
						// Buscar documento que referencia este storageId
						// Como não temos uma tabela direta, vamos buscar nos flowInstanceDocuments
						const documentos = await ctx.db
							.query('flowInstanceDocuments')
							.collect();
						const documento = documentos.find((d) => d.storageId === storageId);
						
						return {
							storageId,
							name: documento?.name ?? 'Arquivo'
						};
					})
				);

				return {
					_id: nota._id,
					_creationTime: nota._creationTime,
					flowStepId: nota.flowStepId,
					flowInstanceStepId: nota.flowInstanceStepId,
					flowSubStepId: nota.flowSubStepId,
					texto: nota.texto,
					criadoPor: nota.criadoPor,
					criadoPorNome: criador?.nome,
					criadoEm: nota.criadoEm,
					arquivos: arquivosComNome
				};
			})
		);

		return notasComDetalhes;
	}
});

/**
 * Adicionar nota
 */
export const adicionarNota = mutation({
	args: {
		flowStepId: v.optional(v.id('flowSteps')),
		flowInstanceStepId: v.optional(v.id('flowInstanceSteps')),
		flowSubStepId: v.optional(v.id('flowSubSteps')),
		texto: v.string(),
		arquivos: v.optional(v.array(v.id('_storage')))
	},
	returns: v.id('flowStepNotes'),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		if (!args.flowStepId && !args.flowInstanceStepId && !args.flowSubStepId) {
			throw new Error('É necessário fornecer flowStepId, flowInstanceStepId ou flowSubStepId');
		}

		const notaId = await ctx.db.insert('flowStepNotes', {
			flowStepId: args.flowStepId,
			flowInstanceStepId: args.flowInstanceStepId,
			flowSubStepId: args.flowSubStepId,
			texto: args.texto,
			criadoPor: usuario._id,
			criadoEm: Date.now(),
			arquivos: args.arquivos ?? []
		});

		return notaId;
	}
});

/**
 * Atualizar nota
 */
export const atualizarNota = mutation({
	args: {
		notaId: v.id('flowStepNotes'),
		texto: v.string()
	},
	returns: v.null(),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		const nota = await ctx.db.get(args.notaId);
		if (!nota) {
			throw new Error('Nota não encontrada');
		}

		// Verificar se o usuário é o criador
		if (nota.criadoPor !== usuario._id) {
			throw new Error('Somente o criador da nota pode editá-la');
		}

		await ctx.db.patch(args.notaId, { texto: args.texto });
		return null;
	}
});

/**
 * Deletar nota
 */
export const deletarNota = mutation({
	args: { notaId: v.id('flowStepNotes') },
	returns: v.null(),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		const nota = await ctx.db.get(args.notaId);
		if (!nota) {
			throw new Error('Nota não encontrada');
		}

		// Verificar se o usuário é o criador
		if (nota.criadoPor !== usuario._id) {
			throw new Error('Somente o criador da nota pode deletá-la');
		}

		await ctx.db.delete(args.notaId);
		return null;
	}
});

/**
 * Adicionar arquivo a uma nota
 */
export const adicionarArquivoNota = mutation({
	args: {
		notaId: v.id('flowStepNotes'),
		storageId: v.id('_storage')
	},
	returns: v.null(),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		const nota = await ctx.db.get(args.notaId);
		if (!nota) {
			throw new Error('Nota não encontrada');
		}

		// Verificar se o usuário é o criador
		if (nota.criadoPor !== usuario._id) {
			throw new Error('Somente o criador da nota pode adicionar arquivos');
		}

		// Adicionar arquivo à lista
		const novosArquivos = [...nota.arquivos, args.storageId];
		await ctx.db.patch(args.notaId, { arquivos: novosArquivos });
		return null;
	}
});

/**
 * Remover arquivo de uma nota
 */
export const removerArquivoNota = mutation({
	args: {
		notaId: v.id('flowStepNotes'),
		storageId: v.id('_storage')
	},
	returns: v.null(),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		const nota = await ctx.db.get(args.notaId);
		if (!nota) {
			throw new Error('Nota não encontrada');
		}

		// Verificar se o usuário é o criador
		if (nota.criadoPor !== usuario._id) {
			throw new Error('Somente o criador da nota pode remover arquivos');
		}

		// Remover arquivo da lista
		const novosArquivos = nota.arquivos.filter((id) => id !== args.storageId);
		await ctx.db.patch(args.notaId, { arquivos: novosArquivos });
		return null;
	}
});

// ============================================
// FLOW DOCUMENTS
// ============================================

/**
 * Listar documentos de um passo
 */
export const listDocumentsByStep = query({
	args: { flowInstanceStepId: v.id('flowInstanceSteps') },
	returns: v.array(
		v.object({
			_id: v.id('flowInstanceDocuments'),
			_creationTime: v.number(),
			flowInstanceStepId: v.id('flowInstanceSteps'),
			uploadedById: v.id('usuarios'),
			uploadedByName: v.optional(v.string()),
			storageId: v.id('_storage'),
			name: v.string(),
			uploadedAt: v.number(),
			url: v.optional(v.string())
		})
	),
	handler: async (ctx, args) => {
		const documents = await ctx.db
			.query('flowInstanceDocuments')
			.withIndex('by_flowInstanceStepId', (q) => q.eq('flowInstanceStepId', args.flowInstanceStepId))
			.collect();

		const result: Array<{
			_id: Id<'flowInstanceDocuments'>;
			_creationTime: number;
			flowInstanceStepId: Id<'flowInstanceSteps'>;
			uploadedById: Id<'usuarios'>;
			uploadedByName: string | undefined;
			storageId: Id<'_storage'>;
			name: string;
			uploadedAt: number;
			url: string | undefined;
		}> = [];

		for (const doc of documents) {
			const uploader = await ctx.db.get(doc.uploadedById);
			const url = await ctx.storage.getUrl(doc.storageId);

			result.push({
				...doc,
				uploadedByName: uploader?.nome,
				url: url ?? undefined
			});
		}

		return result;
	}
});

/**
 * Registrar upload de documento
 */
export const registerDocument = mutation({
	args: {
		flowInstanceStepId: v.id('flowInstanceSteps'),
		storageId: v.id('_storage'),
		name: v.string()
	},
	returns: v.id('flowInstanceDocuments'),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		const step = await ctx.db.get(args.flowInstanceStepId);
		if (!step) {
			throw new Error('Passo não encontrado');
		}

		const documentId = await ctx.db.insert('flowInstanceDocuments', {
			flowInstanceStepId: args.flowInstanceStepId,
			uploadedById: usuario._id,
			storageId: args.storageId,
			name: args.name,
			uploadedAt: Date.now()
		});

		return documentId;
	}
});

/**
 * Excluir documento
 */
export const deleteDocument = mutation({
	args: { id: v.id('flowInstanceDocuments') },
	returns: v.null(),
	handler: async (ctx, args) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		const document = await ctx.db.get(args.id);
		if (!document) {
			throw new Error('Documento não encontrado');
		}

		// Excluir o arquivo do storage
		await ctx.storage.delete(document.storageId);

		// Excluir o registro do documento
		await ctx.db.delete(args.id);

		return null;
	}
});

/**
 * Gerar URL de upload
 */
export const generateUploadUrl = mutation({
	args: {},
	returns: v.string(),
	handler: async (ctx) => {
		const usuario = await getCurrentUserFunction(ctx);
		if (!usuario) {
			throw new Error('Usuário não autenticado');
		}

		return await ctx.storage.generateUploadUrl();
	}
});

/**
 * Obter usuários que podem ser atribuídos a um passo (baseado no setor)
 */
export const getUsuariosBySetorForAssignment = query({
	args: { setorId: v.id('setores') },
	returns: v.array(
		v.object({
			_id: v.id('usuarios'),
			_creationTime: v.number(),
			nome: v.string(),
			email: v.string()
		})
	),
	handler: async (ctx, args) => {
		// Buscar funcionários do setor
		const funcionarioSetores = await ctx.db
			.query('funcionarioSetores')
			.withIndex('by_setorId', (q) => q.eq('setorId', args.setorId))
			.collect();

		const usuarios: Array<{
			_id: Id<'usuarios'>;
			_creationTime: number;
			nome: string;
			email: string;
		}> = [];

		// Para cada funcionário do setor, buscar o usuário correspondente
		for (const relacao of funcionarioSetores) {
			const funcionario = await ctx.db.get(relacao.funcionarioId);
			if (!funcionario) continue;

			// Buscar usuário por email
			const usuariosList = await ctx.db.query('usuarios').collect();
			const usuario = usuariosList.find((u) => u.email === funcionario.email);

			if (usuario && !usuarios.find((u) => u._id === usuario._id)) {
				usuarios.push({
					_id: usuario._id,
					_creationTime: usuario._creationTime,
					nome: usuario.nome,
					email: usuario.email
				});
			}
		}

		return usuarios;
	}
});

