import { v } from "convex/values";
import { mutation, query, action, internalMutation } from "./_generated/server";
import { registrarAtividade } from "./logsAtividades";
import { api, internal } from "./_generated/api";
import { encryptSMTPPassword } from "./auth/utils";

/**
 * Obter configuração de Jitsi ativa
 */
export const obterConfigJitsi = query({
	args: {},
	handler: async (ctx) => {
		const config = await ctx.db
			.query("configuracaoJitsi")
			.withIndex("by_ativo", (q) => q.eq("ativo", true))
			.first();

		if (!config) {
			return null;
		}

		return {
			_id: config._id,
			domain: config.domain,
			appId: config.appId,
			roomPrefix: config.roomPrefix,
			useHttps: config.useHttps,
			acceptSelfSignedCert: config.acceptSelfSignedCert ?? false, // Default para false se não existir
			ativo: config.ativo,
			testadoEm: config.testadoEm,
			atualizadoEm: config.atualizadoEm,
		};
	},
});

/**
 * Obter configuração completa de Jitsi (incluindo SSH, mas sem senha)
 */
export const obterConfigJitsiCompleta = query({
	args: {
		configId: v.id("configuracaoJitsi"),
	},
	handler: async (ctx, args) => {
		const config = await ctx.db.get(args.configId);

		if (!config) {
			return null;
		}

		return {
			_id: config._id,
			domain: config.domain,
			appId: config.appId,
			roomPrefix: config.roomPrefix,
			useHttps: config.useHttps,
			acceptSelfSignedCert: config.acceptSelfSignedCert ?? false,
			ativo: config.ativo,
			testadoEm: config.testadoEm,
			atualizadoEm: config.atualizadoEm,
			configuradoEm: config.configuradoEm,
			// Configurações SSH (sem senha)
			sshHost: config.sshHost,
			sshPort: config.sshPort,
			sshUsername: config.sshUsername,
			sshPasswordHash: config.sshPasswordHash ? "********" : undefined, // Mascarar
			sshKeyPath: config.sshKeyPath,
			dockerComposePath: config.dockerComposePath,
			jitsiConfigPath: config.jitsiConfigPath,
			configuradoNoServidor: config.configuradoNoServidor ?? false,
			configuradoNoServidorEm: config.configuradoNoServidorEm,
		};
	},
});

/**
 * Salvar configuração de Jitsi (apenas TI_MASTER)
 */
export const salvarConfigJitsi = mutation({
	args: {
		domain: v.string(),
		appId: v.string(),
		roomPrefix: v.string(),
		useHttps: v.boolean(),
		acceptSelfSignedCert: v.boolean(),
		configuradoPorId: v.id("usuarios"),
		// Opcionais: configurações SSH/Docker
		sshHost: v.optional(v.string()),
		sshPort: v.optional(v.number()),
		sshUsername: v.optional(v.string()),
		sshPassword: v.optional(v.string()), // Senha nova (será criptografada)
		sshKeyPath: v.optional(v.string()),
		dockerComposePath: v.optional(v.string()),
		jitsiConfigPath: v.optional(v.string()),
	},
	returns: v.union(
		v.object({ sucesso: v.literal(true), configId: v.id("configuracaoJitsi") }),
		v.object({ sucesso: v.literal(false), erro: v.string() })
	),
	handler: async (ctx, args) => {
		// Validar domínio (deve ser não vazio)
		if (!args.domain || args.domain.trim().length === 0) {
			return { sucesso: false as const, erro: "Domínio não pode estar vazio" };
		}

		// Validar appId (deve ser não vazio)
		if (!args.appId || args.appId.trim().length === 0) {
			return { sucesso: false as const, erro: "App ID não pode estar vazio" };
		}

		// Validar roomPrefix (deve ser não vazio e alfanumérico)
		if (!args.roomPrefix || args.roomPrefix.trim().length === 0) {
			return { sucesso: false as const, erro: "Prefixo de sala não pode estar vazio" };
		}

		// Validar formato do roomPrefix (apenas letras, números e hífens)
		const roomPrefixRegex = /^[a-zA-Z0-9-]+$/;
		if (!roomPrefixRegex.test(args.roomPrefix.trim())) {
			return {
				sucesso: false as const,
				erro: "Prefixo de sala deve conter apenas letras, números e hífens",
			};
		}

		// Buscar config ativa anterior para manter senha SSH se não fornecida
		const configAtiva = await ctx.db
			.query("configuracaoJitsi")
			.withIndex("by_ativo", (q) => q.eq("ativo", true))
			.first();

		// Desativar config anterior
		const configsAntigas = await ctx.db
			.query("configuracaoJitsi")
			.withIndex("by_ativo", (q) => q.eq("ativo", true))
			.collect();

		for (const config of configsAntigas) {
			await ctx.db.patch(config._id, { ativo: false });
		}

		// Determinar senha SSH: usar nova senha se fornecida, senão manter a atual
		let sshPasswordHash: string | undefined = undefined;
		if (args.sshPassword && args.sshPassword.trim().length > 0) {
			// Nova senha fornecida, criptografar
			sshPasswordHash = await encryptSMTPPassword(args.sshPassword);
		} else if (configAtiva && configAtiva.sshPasswordHash) {
			// Senha não fornecida, manter a atual (já criptografada)
			sshPasswordHash = configAtiva.sshPasswordHash;
		}

		// Criar nova config
		const configId = await ctx.db.insert("configuracaoJitsi", {
			domain: args.domain.trim(),
			appId: args.appId.trim(),
			roomPrefix: args.roomPrefix.trim(),
			useHttps: args.useHttps,
			acceptSelfSignedCert: args.acceptSelfSignedCert,
			ativo: true,
			configuradoPor: args.configuradoPorId,
			atualizadoEm: Date.now(),
			// Configurações SSH/Docker
			sshHost: args.sshHost?.trim() || undefined,
			sshPort: args.sshPort || undefined,
			sshUsername: args.sshUsername?.trim() || undefined,
			sshPasswordHash: sshPasswordHash,
			sshKeyPath: args.sshKeyPath?.trim() || undefined,
			dockerComposePath: args.dockerComposePath?.trim() || undefined,
			jitsiConfigPath: args.jitsiConfigPath?.trim() || undefined,
		});

		// Log de atividade
		await registrarAtividade(
			ctx,
			args.configuradoPorId,
			"configurar",
			"jitsi",
			JSON.stringify({ domain: args.domain, appId: args.appId }),
			configId
		);

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

/**
 * Mutation interna para atualizar testadoEm
 */
export const atualizarTestadoEm = internalMutation({
	args: {
		configId: v.id("configuracaoJitsi"),
	},
	returns: v.null(),
	handler: async (ctx, args) => {
		await ctx.db.patch(args.configId, {
			testadoEm: Date.now(),
		});
		return null;
	},
});

/**
 * Testar conexão com servidor Jitsi
 */
export const testarConexaoJitsi = action({
	args: {
		domain: v.string(),
		useHttps: v.boolean(),
		acceptSelfSignedCert: v.optional(v.boolean()),
	},
	returns: v.union(
		v.object({ sucesso: v.literal(true), aviso: v.optional(v.string()) }),
		v.object({ sucesso: v.literal(false), erro: v.string() })
	),
	handler: async (ctx, args): Promise<{ sucesso: true; aviso?: string } | { sucesso: false; erro: string }> => {
		// Validações básicas
		if (!args.domain || args.domain.trim().length === 0) {
			return { sucesso: false as const, erro: "Domínio não pode estar vazio" };
		}

		try {
			const protocol = args.useHttps ? "https" : "http";
			// Extrair host e porta do domain
			const [host, portStr] = args.domain.split(":");
			const port = portStr ? parseInt(portStr, 10) : args.useHttps ? 443 : 80;
			const url = `${protocol}://${host}:${port}/http-bind`;

			// Tentar fazer uma requisição HTTP para verificar se o servidor está acessível
			// Nota: No ambiente Node.js do Convex, podemos usar fetch
			const controller = new AbortController();
			const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 segundos de timeout

			try {
				const response = await fetch(url, {
					method: "GET",
					signal: controller.signal,
					headers: {
						"Content-Type": "application/xml",
					},
				});

				clearTimeout(timeoutId);

				// Qualquer resposta indica que o servidor está acessível
				// Não precisamos verificar o status code exato, apenas se há resposta
				if (response.status >= 200 && response.status < 600) {
					// Se o teste foi bem-sucedido e há uma config ativa, atualizar testadoEm
					const configAtiva = await ctx.runQuery(api.configuracaoJitsi.obterConfigJitsi, {});

					if (configAtiva) {
						await ctx.runMutation(internal.configuracaoJitsi.atualizarTestadoEm, {
							configId: configAtiva._id,
						});
					}

					return { sucesso: true as const, aviso: undefined };
				} else {
					return {
						sucesso: false as const,
						erro: `Servidor retornou status ${response.status}`,
					};
				}
			} catch (fetchError: unknown) {
				clearTimeout(timeoutId);
				const errorMessage =
					fetchError instanceof Error ? fetchError.message : String(fetchError);

				// Se for erro de timeout
				if (errorMessage.includes("aborted") || errorMessage.includes("timeout")) {
					return {
						sucesso: false as const,
						erro: "Timeout: Servidor não respondeu em 5 segundos",
					};
				}

				// Verificar se é erro de certificado SSL autoassinado
				const isSSLError = 
					errorMessage.includes("CERTIFICATE_VERIFY_FAILED") ||
					errorMessage.includes("self signed certificate") ||
					errorMessage.includes("self-signed certificate") ||
					errorMessage.includes("certificate") ||
					errorMessage.includes("SSL") ||
					errorMessage.includes("certificate verify failed");

				// Se for erro de certificado e aceitar autoassinado está configurado
				if (isSSLError && args.acceptSelfSignedCert) {
					// Aceitar como sucesso se configurado para aceitar certificados autoassinados
					// (o servidor está acessível, apenas o certificado não é confiável)
					// Nota: No cliente (navegador), o usuário ainda precisará aceitar o certificado manualmente
					const configAtiva = await ctx.runQuery(api.configuracaoJitsi.obterConfigJitsi, {});

					if (configAtiva) {
						await ctx.runMutation(internal.configuracaoJitsi.atualizarTestadoEm, {
							configId: configAtiva._id,
						});
					}

					return { 
						sucesso: true as const,
						aviso: "Servidor acessível com certificado autoassinado. No navegador, você precisará aceitar o certificado manualmente na primeira conexão."
					};
				}

				// Para servidores Jitsi, pode ser normal receber erro 405 (Method Not Allowed)
				// para GET em /http-bind, pois esse endpoint espera POST (BOSH)
				// Isso indica que o servidor está acessível, apenas não aceita GET
				if (errorMessage.includes("405") || errorMessage.includes("Method Not Allowed")) {
					// Se o teste foi bem-sucedido e há uma config ativa, atualizar testadoEm
					const configAtiva = await ctx.runQuery(api.configuracaoJitsi.obterConfigJitsi, {});

					if (configAtiva) {
						await ctx.runMutation(internal.configuracaoJitsi.atualizarTestadoEm, {
							configId: configAtiva._id,
						});
					}

					return { sucesso: true as const, aviso: undefined };
				}

				// Se for erro de certificado SSL e não está configurado para aceitar
				if (isSSLError) {
					return {
						sucesso: false as const,
						erro: `Erro de certificado SSL: O servidor está usando um certificado não confiável (provavelmente autoassinado). Para desenvolvimento local, habilite "Aceitar Certificados Autoassinados" nas configurações de segurança. Em produção, use um certificado válido (ex: Let's Encrypt).`,
					};
				}

				return {
					sucesso: false as const,
					erro: `Erro ao conectar: ${errorMessage}`,
				};
			}
		} catch (error: unknown) {
			const errorMessage = error instanceof Error ? error.message : String(error);
			return {
				sucesso: false as const,
				erro: errorMessage || "Erro ao conectar com o servidor Jitsi",
			};
		}
	},
});

/**
 * Marcar que a configuração foi testada com sucesso
 */
export const marcarConfigTestada = mutation({
	args: {
		configId: v.id("configuracaoJitsi"),
	},
	handler: async (ctx, args) => {
		await ctx.db.patch(args.configId, {
			testadoEm: Date.now(),
		});
	},
});

/**
 * Mutation interna para marcar que a configuração foi aplicada no servidor
 */
export const marcarConfiguradoNoServidor = internalMutation({
	args: {
		configId: v.id("configuracaoJitsi"),
	},
	returns: v.null(),
	handler: async (ctx, args) => {
		await ctx.db.patch(args.configId, {
			configuradoNoServidor: true,
			configuradoNoServidorEm: Date.now(),
			configuradoEm: Date.now(),
		});
		return null;
	},
});

