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

export const list = query({
	args: {},
	handler: async (ctx) => {
		await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
			recurso: 'empresas',
			acao: 'listar'
		});

		const empresas = await ctx.db.query('empresas').collect();
		return empresas;
	}
});

export const getById = query({
	args: { id: v.id('empresas') },
	handler: async (ctx, args) => {
		await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
			recurso: 'empresas',
			acao: 'ver'
		});

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

		const contatos = await ctx.db
			.query('contatosEmpresa')
			.withIndex('by_empresa', (q) => q.eq('empresaId', args.id))
			.collect();
		const endereco = empresa.enderecoId
			? await ctx.db.get(empresa.enderecoId as Id<'enderecos'>)
			: null;

		return { ...empresa, endereco, contatos };
	}
});

const contatoInput = v.object({
	_id: v.optional(v.id('contatosEmpresa')),
	empresaId: v.optional(v.id('empresas')),
	nome: v.string(),
	funcao: v.string(),
	email: v.string(),
	telefone: v.string(),
	adicionadoPor: v.optional(v.id('usuarios')),
	descricao: v.optional(v.string()),
	_deleted: v.optional(v.boolean())
});

const enderecoInput = v.object({
	cep: v.string(),
	logradouro: v.string(),
	numero: v.string(),
	complemento: v.optional(v.string()),
	bairro: v.string(),
	cidade: v.string(),
	uf: v.string()
});

export const create = mutation({
	args: {
		razao_social: v.string(),
		nome_fantasia: v.optional(v.string()),
		cnpj: v.string(),
		telefone: v.string(),
		email: v.string(),
		descricao: v.optional(v.string()),
		endereco: v.optional(enderecoInput),
		contatos: v.optional(v.array(contatoInput))
	},
	returns: v.id('empresas'),
	handler: async (ctx, args) => {
		const usuarioAtual = await getCurrentUserFunction(ctx);

		if (!usuarioAtual) {
			throw new Error('Usuário não autenticado.');
		}

		await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
			recurso: 'empresas',
			acao: 'criar'
		});

		const cnpjExistente = await ctx.db
			.query('empresas')
			.withIndex('by_cnpj', (q) => q.eq('cnpj', args.cnpj))
			.unique();

		if (cnpjExistente) {
			throw new Error('Já existe uma empresa cadastrada com este CNPJ.');
		}
		let enderecoId: Id<'enderecos'> | undefined;
		if (args.endereco) {
			enderecoId = await ctx.db.insert('enderecos', {
				cep: args.endereco.cep,
				logradouro: args.endereco.logradouro,
				numero: args.endereco.numero,
				complemento: args.endereco.complemento,
				bairro: args.endereco.bairro,
				cidade: args.endereco.cidade,
				uf: args.endereco.uf,
				criadoPor: usuarioAtual._id,
				atualizadoPor: usuarioAtual._id
			});
		}

		const empresaDoc: {
			razao_social: string;
			nome_fantasia?: string;
			cnpj: string;
			telefone: string;
			email: string;
			descricao?: string;
			enderecoId?: Id<'enderecos'>;
			criadoPor: Id<'usuarios'>;
		} = {
			razao_social: args.razao_social,
			cnpj: args.cnpj,
			telefone: args.telefone,
			email: args.email,
			criadoPor: usuarioAtual._id
		};

		if (args.nome_fantasia !== undefined) {
			empresaDoc.nome_fantasia = args.nome_fantasia;
		}
		if (args.descricao !== undefined) {
			empresaDoc.descricao = args.descricao;
		}
		if (enderecoId) {
			empresaDoc.enderecoId = enderecoId;
		}

		const empresaId = await ctx.db.insert('empresas', empresaDoc);

		if (args.contatos && args.contatos.length > 0) {
			for (const contato of args.contatos) {
				await ctx.db.insert('contatosEmpresa', {
					empresaId,
					nome: contato.nome,
					funcao: contato.funcao,
					email: contato.email,
					telefone: contato.telefone,
					adicionadoPor: usuarioAtual._id,
					descricao: contato.descricao
				});
			}
		}

		return empresaId;
	}
});

export const update = mutation({
	args: {
		id: v.id('empresas'),
		razao_social: v.string(),
		nome_fantasia: v.optional(v.string()),
		cnpj: v.string(),
		telefone: v.string(),
		email: v.string(),
		descricao: v.optional(v.string()),
		endereco: v.optional(enderecoInput),
		contatos: v.optional(v.array(contatoInput))
	},
	returns: v.null(),
	handler: async (ctx, args) => {
		await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
			recurso: 'empresas',
			acao: 'editar'
		});

		const cnpjExistente = await ctx.db
			.query('empresas')
			.withIndex('by_cnpj', (q) => q.eq('cnpj', args.cnpj))
			.unique();

		if (cnpjExistente && cnpjExistente._id !== args.id) {
			throw new Error('Já existe uma empresa cadastrada com este CNPJ.');
		}
		const empresa = await ctx.db.get(args.id);
		if (!empresa) {
			throw new Error('Empresa não encontrada.');
		}

		if (args.endereco) {
			if (empresa.enderecoId) {
				const usuarioAtual = await getCurrentUserFunction(ctx);
				await ctx.db.patch(empresa.enderecoId as Id<'enderecos'>, {
					cep: args.endereco.cep,
					logradouro: args.endereco.logradouro,
					numero: args.endereco.numero,
					complemento: args.endereco.complemento,
					bairro: args.endereco.bairro,
					cidade: args.endereco.cidade,
					uf: args.endereco.uf,
					atualizadoPor: usuarioAtual?._id
				});
			} else {
				const usuarioAtual = await getCurrentUserFunction(ctx);

				if (!usuarioAtual) {
					throw new Error('Usuário não autenticado.');
				}

				const novoEnderecoId: Id<'enderecos'> = await ctx.db.insert('enderecos', {
					cep: args.endereco.cep,
					logradouro: args.endereco.logradouro,
					numero: args.endereco.numero,
					complemento: args.endereco.complemento,
					bairro: args.endereco.bairro,
					cidade: args.endereco.cidade,
					uf: args.endereco.uf,
					criadoPor: usuarioAtual._id,
					atualizadoPor: usuarioAtual._id
				});

				await ctx.db.patch(args.id, {
					enderecoId: novoEnderecoId
				});
			}
		}

		const patchDoc: {
			razao_social: string;
			nome_fantasia?: string;
			cnpj: string;
			telefone: string;
			email: string;
			descricao?: string;
		} = {
			razao_social: args.razao_social,
			cnpj: args.cnpj,
			telefone: args.telefone,
			email: args.email
		};

		if (args.nome_fantasia !== undefined) {
			patchDoc.nome_fantasia = args.nome_fantasia;
		}
		if (args.descricao !== undefined) {
			patchDoc.descricao = args.descricao;
		}

		await ctx.db.patch(args.id, patchDoc);

		if (!args.contatos) {
			return null;
		}

		for (const contato of args.contatos) {
			if (contato._id && contato._deleted) {
				await ctx.db.delete(contato._id);
			} else if (contato._id) {
				await ctx.db.patch(contato._id, {
					nome: contato.nome,
					funcao: contato.funcao,
					email: contato.email,
					telefone: contato.telefone,
					descricao: contato.descricao
				});
			} else if (!contato._deleted) {
				const usuarioAtual = await getCurrentUserFunction(ctx);

				if (!usuarioAtual) {
					throw new Error('Usuário não autenticado.');
				}

				await ctx.db.insert('contatosEmpresa', {
					empresaId: args.id,
					nome: contato.nome,
					funcao: contato.funcao,
					email: contato.email,
					telefone: contato.telefone,
					adicionadoPor: usuarioAtual._id,
					descricao: contato.descricao
				});
			}
		}

		return null;
	}
});
