import { getLocalIP } from './browserInfo';

export interface DadosAcelerometro {
	x: number;
	y: number;
	z: number;
	movimentoDetectado: boolean;
	magnitude: number;
	variacao: number; // Variância entre leituras
	timestamp: number;
}

export interface DadosGiroscopio {
	alpha: number;
	beta: number;
	gamma: number;
}

export interface InformacoesDispositivo {
	ipAddress?: string;
	ipPublico?: string;
	ipLocal?: string;
	userAgent?: string;
	browser?: string;
	browserVersion?: string;
	engine?: string;
	sistemaOperacional?: string;
	osVersion?: string;
	arquitetura?: string;
	plataforma?: string;
	latitude?: number;
	longitude?: number;
	precisao?: number;
	altitude?: number | null;
	altitudeAccuracy?: number | null;
	heading?: number | null;
	speed?: number | null;
	confiabilidadeGPS?: number; // 0-1
	suspeitaSpoofing?: boolean;
	motivoSuspeita?: string;
	endereco?: string;
	cidade?: string;
	estado?: string;
	pais?: string;
	timezone?: string;
	deviceType?: string;
	deviceModel?: string;
	screenResolution?: string;
	coresTela?: number;
	idioma?: string;
	isMobile?: boolean;
	isTablet?: boolean;
	isDesktop?: boolean;
	connectionType?: string;
	memoryInfo?: string;
	acelerometro?: DadosAcelerometro;
	giroscopio?: DadosGiroscopio;
	sensorDisponivel?: boolean; // Indica se o sensor está disponível no dispositivo
	permissaoNegada?: boolean; // Indica se a permissão foi negada pelo usuário
}

/**
 * Detecta informações do navegador
 */
function detectarNavegador(): { browser: string; browserVersion: string; engine: string } {
	if (typeof navigator === 'undefined') {
		return { browser: 'Desconhecido', browserVersion: '', engine: '' };
	}

	const ua = navigator.userAgent;
	let browser = 'Desconhecido';
	let browserVersion = '';
	let engine = '';

	// Detectar engine
	if (ua.includes('Edg/')) {
		engine = 'EdgeHTML';
	} else if (ua.includes('Chrome/')) {
		engine = 'Blink';
	} else if (ua.includes('Firefox/')) {
		engine = 'Gecko';
	} else if (ua.includes('Safari/') && !ua.includes('Chrome/')) {
		engine = 'WebKit';
	}

	// Detectar navegador
	if (ua.includes('Edg/')) {
		browser = 'Edge';
		const match = ua.match(/Edg\/(\d+)/);
		browserVersion = match ? match[1]! : '';
	} else if (ua.includes('Chrome/') && !ua.includes('Edg/')) {
		browser = 'Chrome';
		const match = ua.match(/Chrome\/(\d+)/);
		browserVersion = match ? match[1]! : '';
	} else if (ua.includes('Firefox/')) {
		browser = 'Firefox';
		const match = ua.match(/Firefox\/(\d+)/);
		browserVersion = match ? match[1]! : '';
	} else if (ua.includes('Safari/') && !ua.includes('Chrome/')) {
		browser = 'Safari';
		const match = ua.match(/Version\/(\d+)/);
		browserVersion = match ? match[1]! : '';
	} else if (ua.includes('Opera/') || ua.includes('OPR/')) {
		browser = 'Opera';
		const match = ua.match(/(?:Opera|OPR)\/(\d+)/);
		browserVersion = match ? match[1]! : '';
	}

	return { browser, browserVersion, engine };
}

/**
 * Detecta informações do sistema operacional
 */
function detectarSistemaOperacional(): {
	sistemaOperacional: string;
	osVersion: string;
	arquitetura: string;
	plataforma: string;
} {
	if (typeof navigator === 'undefined') {
		return {
			sistemaOperacional: 'Desconhecido',
			osVersion: '',
			arquitetura: '',
			plataforma: ''
		};
	}

	const ua = navigator.userAgent;
	const platform = navigator.platform || '';
	let sistemaOperacional = 'Desconhecido';
	let osVersion = '';
	let arquitetura = '';
	const plataforma = platform;

	// Detectar OS
	if (ua.includes('Windows NT')) {
		sistemaOperacional = 'Windows';
		const match = ua.match(/Windows NT (\d+\.\d+)/);
		if (match) {
			const version = match[1]!;
			const versions: Record<string, string> = {
				'10.0': '10/11',
				'6.3': '8.1',
				'6.2': '8',
				'6.1': '7'
			};
			osVersion = versions[version] || version;
		}
	} else if (ua.includes('Mac OS X') || ua.includes('Macintosh')) {
		sistemaOperacional = 'macOS';
		const match = ua.match(/Mac OS X (\d+[._]\d+)/);
		if (match) {
			osVersion = match[1]!.replace('_', '.');
		}
	} else if (ua.includes('Linux')) {
		sistemaOperacional = 'Linux';
		osVersion = 'Linux';
	} else if (ua.includes('Android')) {
		sistemaOperacional = 'Android';
		const match = ua.match(/Android (\d+(?:\.\d+)?)/);
		osVersion = match ? match[1]! : '';
	} else if (ua.includes('iPhone') || ua.includes('iPad')) {
		sistemaOperacional = 'iOS';
		const match = ua.match(/OS (\d+[._]\d+)/);
		if (match) {
			osVersion = match[1]!.replace('_', '.');
		}
	}

	// Detectar arquitetura (se disponível)
	if ('cpuClass' in navigator) {
		arquitetura = (navigator as unknown as { cpuClass: string }).cpuClass;
	}

	return { sistemaOperacional, osVersion, arquitetura, plataforma };
}

/**
 * Detecta tipo de dispositivo
 */
function detectarTipoDispositivo(): {
	deviceType: string;
	isMobile: boolean;
	isTablet: boolean;
	isDesktop: boolean;
} {
	if (typeof navigator === 'undefined') {
		return {
			deviceType: 'Desconhecido',
			isMobile: false,
			isTablet: false,
			isDesktop: true
		};
	}

	const ua = navigator.userAgent;
	const isMobile = /Mobile|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua);
	const isTablet = /iPad|Android(?!.*Mobile)|Tablet/i.test(ua);
	const isDesktop = !isMobile && !isTablet;

	let deviceType = 'Desktop';
	if (isTablet) {
		deviceType = 'Tablet';
	} else if (isMobile) {
		deviceType = 'Mobile';
	}

	return { deviceType, isMobile, isTablet, isDesktop };
}

/**
 * Obtém informações da tela
 */
function obterInformacoesTela(): { screenResolution: string; coresTela: number } {
	if (typeof screen === 'undefined') {
		return { screenResolution: 'Desconhecido', coresTela: 0 };
	}

	const screenResolution = `${screen.width}x${screen.height}`;
	const coresTela = screen.colorDepth || 24;

	return { screenResolution, coresTela };
}

/**
 * Obtém informações de conexão
 */
async function obterInformacoesConexao(): Promise<string> {
	if (typeof navigator === 'undefined' || !('connection' in navigator)) {
		return 'Desconhecido';
	}

	const connection = (navigator as unknown as { connection?: { effectiveType?: string } })
		.connection;
	if (connection?.effectiveType) {
		return connection.effectiveType;
	}

	return 'Desconhecido';
}

/**
 * Obtém informações de memória (se disponível)
 */
function obterInformacoesMemoria(): string {
	if (typeof navigator === 'undefined' || !('deviceMemory' in navigator)) {
		return 'Desconhecido';
	}

	const deviceMemory = (navigator as unknown as { deviceMemory?: number }).deviceMemory;
	if (deviceMemory) {
		return `${deviceMemory} GB`;
	}

	return 'Desconhecido';
}

/**
 * Calcula distância entre duas coordenadas (fórmula de Haversine)
 * Retorna distância em metros
 */
function calcularDistancia(lat1: number, lon1: number, lat2: number, lon2: number): number {
	const R = 6371000; // Raio da Terra em metros
	const dLat = ((lat2 - lat1) * Math.PI) / 180;
	const dLon = ((lon2 - lon1) * Math.PI) / 180;
	const a =
		Math.sin(dLat / 2) * Math.sin(dLat / 2) +
		Math.cos((lat1 * Math.PI) / 180) *
			Math.cos((lat2 * Math.PI) / 180) *
			Math.sin(dLon / 2) *
			Math.sin(dLon / 2);
	const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
	return R * c;
}

/**
 * Obtém timezone aproximado por coordenadas
 */
function obterTimezonePorCoordenadas(latitude: number, longitude: number): string {
	// Pernambuco está em UTC-3 (America/Recife)
	if (longitude >= -45 && longitude <= -30 && latitude >= -10 && latitude <= 5) {
		return 'America/Recife'; // UTC-3
	}

	// Fallback: usar timezone do sistema
	try {
		return Intl.DateTimeFormat().resolvedOptions().timeZone;
	} catch {
		return 'America/Recife'; // Default
	}
}

/**
 * Captura uma única leitura de localização com todas as propriedades disponíveis
 */
async function capturarLocalizacaoUnica(
	enableHighAccuracy: boolean = true,
	timeout: number = 10000
): Promise<{
	latitude?: number;
	longitude?: number;
	precisao?: number;
	altitude?: number | null;
	altitudeAccuracy?: number | null;
	heading?: number | null;
	speed?: number | null;
	timestamp?: number;
	confiabilidade: number; // 0-1
}> {
	return new Promise((resolve) => {
		if (typeof navigator === 'undefined' || !navigator.geolocation) {
			resolve({ confiabilidade: 0 });
			return;
		}

		const timeoutId = setTimeout(() => {
			resolve({ confiabilidade: 0 });
		}, timeout + 1000);

		navigator.geolocation.getCurrentPosition(
			(position) => {
				clearTimeout(timeoutId);
				const coords = position.coords;
				const { latitude, longitude, accuracy } = coords;

				// Validar coordenadas básicas
				if (
					isNaN(latitude) ||
					isNaN(longitude) ||
					latitude === 0 ||
					longitude === 0 ||
					latitude < -90 ||
					latitude > 90 ||
					longitude < -180 ||
					longitude > 180
				) {
					resolve({ confiabilidade: 0 });
					return;
				}

				// Calcular score de confiabilidade baseado em propriedades do GPS real
				const sinaisGPSReal = {
					temAltitude: coords.altitude !== null && coords.altitude !== 0,
					temAltitudeAccuracy: coords.altitudeAccuracy !== null && coords.altitudeAccuracy > 0,
					temHeading: coords.heading !== null && !isNaN(coords.heading),
					temSpeed: coords.speed !== null && !isNaN(coords.speed),
					precisaoBoa: accuracy < 20, // GPS real geralmente < 20m
					precisaoMedia: accuracy >= 20 && accuracy < 100,
					timestampPreciso: position.timestamp > 0
				};

				// Calcular confiabilidade: cada sinal adiciona pontos
				let pontos = 0;
				const maxPontos = 7;

				if (sinaisGPSReal.temAltitude) pontos += 1;
				if (sinaisGPSReal.temAltitudeAccuracy) pontos += 1;
				if (sinaisGPSReal.temHeading) pontos += 0.5;
				if (sinaisGPSReal.temSpeed) pontos += 0.5;
				if (sinaisGPSReal.precisaoBoa) pontos += 2;
				if (sinaisGPSReal.precisaoMedia) pontos += 1;
				if (sinaisGPSReal.timestampPreciso) pontos += 1;

				const confiabilidade = Math.min(pontos / maxPontos, 1);

				resolve({
					latitude,
					longitude,
					precisao: accuracy,
					altitude: coords.altitude ?? null,
					altitudeAccuracy: coords.altitudeAccuracy ?? null,
					heading: coords.heading ?? null,
					speed: coords.speed ?? null,
					timestamp: position.timestamp,
					confiabilidade
				});
			},
			(error) => {
				clearTimeout(timeoutId);
				console.warn('Erro ao obter localização:', error.code, error.message);
				resolve({ confiabilidade: 0 });
			},
			{
				enableHighAccuracy,
				timeout,
				maximumAge: 0 // Sempre obter nova leitura
			}
		);
	});
}

/**
 * Obtém localização via GPS com múltiplas leituras para detectar spoofing
 * Apps de spoofing geralmente retornam valores idênticos em todas as leituras
 */
async function obterLocalizacaoMultipla(): Promise<{
	latitude?: number;
	longitude?: number;
	precisao?: number;
	altitude?: number | null;
	altitudeAccuracy?: number | null;
	heading?: number | null;
	speed?: number | null;
	confiabilidade: number; // 0-1
	suspeitaSpoofing: boolean;
	motivoSuspeita?: string;
}> {
	if (typeof navigator === 'undefined' || !navigator.geolocation) {
		return {
			confiabilidade: 0,
			suspeitaSpoofing: true,
			motivoSuspeita: 'Geolocalização não suportada'
		};
	}

	// Capturar 3 leituras com intervalo de 2 segundos entre elas
	const leituras: Array<{
		lat: number;
		lon: number;
		precisao: number;
		altitude: number | null;
		confiabilidade: number;
	}> = [];

	for (let i = 0; i < 3; i++) {
		const leitura = await capturarLocalizacaoUnica(true, 8000);

		if (leitura.latitude && leitura.longitude && leitura.confiabilidade > 0) {
			leituras.push({
				lat: leitura.latitude,
				lon: leitura.longitude,
				precisao: leitura.precisao || 999,
				altitude: leitura.altitude ?? null,
				confiabilidade: leitura.confiabilidade
			});
		}

		// Aguardar 2 segundos entre leituras (exceto na última)
		if (i < 2) {
			await new Promise((resolve) => setTimeout(resolve, 2000));
		}
	}

	if (leituras.length === 0) {
		return {
			confiabilidade: 0,
			suspeitaSpoofing: true,
			motivoSuspeita: 'Não foi possível obter localização'
		};
	}

	// Se tivermos menos de 2 leituras, usar única leitura com baixa confiança
	if (leituras.length < 2) {
		const unica = leituras[0];
		return {
			latitude: unica.lat,
			longitude: unica.lon,
			precisao: unica.precisao,
			altitude: unica.altitude,
			altitudeAccuracy: null,
			heading: null,
			speed: null,
			confiabilidade: unica.confiabilidade * 0.5, // Reduzir confiança por ter apenas 1 leitura
			suspeitaSpoofing: true,
			motivoSuspeita: 'Apenas uma leitura obtida'
		};
	}

	// Verificar se todas as leituras são idênticas (suspeito de spoofing)
	const primeiraLeitura = leituras[0];
	const todasIguais = leituras.every(
		(l) =>
			Math.abs(l.lat - primeiraLeitura.lat) < 0.00001 && // ~1 metro
			Math.abs(l.lon - primeiraLeitura.lon) < 0.00001
	);

	if (todasIguais && leituras.length === 3) {
		// GPS real varia alguns metros, se todas são idênticas pode ser spoofing
		return {
			latitude: primeiraLeitura.lat,
			longitude: primeiraLeitura.lon,
			precisao: primeiraLeitura.precisao,
			altitude: primeiraLeitura.altitude,
			altitudeAccuracy: null,
			heading: null,
			speed: null,
			confiabilidade: primeiraLeitura.confiabilidade * 0.4, // Reduzir drasticamente confiança
			suspeitaSpoofing: true,
			motivoSuspeita: 'Todas as leituras são idênticas (GPS real varia alguns metros)'
		};
	}

	// Calcular média das leituras e variância
	const mediaLat = leituras.reduce((sum, l) => sum + l.lat, 0) / leituras.length;
	const mediaLon = leituras.reduce((sum, l) => sum + l.lon, 0) / leituras.length;
	const mediaConfianca = leituras.reduce((sum, l) => sum + l.confiabilidade, 0) / leituras.length;

	// Calcular distância máxima entre leituras
	let distanciaMaxima = 0;
	for (let i = 0; i < leituras.length; i++) {
		for (let j = i + 1; j < leituras.length; j++) {
			const dist = calcularDistancia(
				leituras[i].lat,
				leituras[i].lon,
				leituras[j].lat,
				leituras[j].lon
			);
			distanciaMaxima = Math.max(distanciaMaxima, dist);
		}
	}

	// Se distância máxima for muito grande (> 100m), pode indicar problemas
	const suspeitoPorDistancia = distanciaMaxima > 100;

	return {
		latitude: mediaLat,
		longitude: mediaLon,
		precisao: primeiraLeitura.precisao,
		altitude: primeiraLeitura.altitude,
		altitudeAccuracy: null,
		heading: null,
		speed: null,
		confiabilidade: suspeitoPorDistancia ? mediaConfianca * 0.6 : mediaConfianca,
		suspeitaSpoofing: suspeitoPorDistancia,
		motivoSuspeita: suspeitoPorDistancia
			? `Variação muito grande entre leituras (${Math.round(distanciaMaxima)}m)`
			: undefined
	};
}

/**
 * Obtém localização via GPS de forma rápida (uma única leitura, sem reverse geocoding)
 * Usado para login - não bloqueia o fluxo
 */
export async function obterLocalizacaoRapida(): Promise<{
	latitude?: number;
	longitude?: number;
	precisao?: number;
	endereco?: string;
	cidade?: string;
	estado?: string;
	pais?: string;
}> {
	if (typeof navigator === 'undefined' || !navigator.geolocation) {
		return {};
	}

	try {
		// Uma única leitura rápida com timeout curto
		const leitura = await capturarLocalizacaoUnica(true, 3000); // 3 segundos máximo

		if (!leitura.latitude || !leitura.longitude || leitura.confiabilidade === 0) {
			return {};
		}

		// Tentar obter endereço via reverse geocoding (com timeout curto)
		let endereco = '';
		let cidade = '';
		let estado = '';
		let pais = '';

		try {
			const geocodePromise = fetch(
				`https://nominatim.openstreetmap.org/reverse?format=json&lat=${leitura.latitude}&lon=${leitura.longitude}&zoom=18&addressdetails=1`,
				{
					headers: {
						'User-Agent': 'SGSE-App/1.0'
					}
				}
			);
			const geocodeTimeout = new Promise<Response>((_, reject) =>
				setTimeout(() => reject(new Error('Timeout')), 2000)
			);

			const response = await Promise.race([geocodePromise, geocodeTimeout]);

			if (response.ok) {
				const data = (await response.json()) as {
					address?: {
						road?: string;
						house_number?: string;
						city?: string;
						town?: string;
						state?: string;
						country?: string;
					};
				};
				if (data.address) {
					const addr = data.address;
					if (addr.road) {
						endereco = `${addr.road}${addr.house_number ? `, ${addr.house_number}` : ''}`;
					}
					cidade = addr.city || addr.town || '';
					estado = addr.state || '';
					pais = addr.country || '';
				}
			}
		} catch (error) {
			// Ignorar erro de geocoding - não é crítico
			console.warn('Erro ao obter endereço (não crítico):', error);
		}

		return {
			latitude: leitura.latitude,
			longitude: leitura.longitude,
			precisao: leitura.precisao,
			endereco,
			cidade,
			estado,
			pais
		};
	} catch (error) {
		console.warn('Erro ao obter localização rápida:', error);
		return {};
	}
}

/**
 * Obtém localização via GPS com múltiplas tentativas e validações anti-spoofing
 */
export async function obterLocalizacao(): Promise<{
	latitude?: number;
	longitude?: number;
	precisao?: number;
	altitude?: number | null;
	altitudeAccuracy?: number | null;
	heading?: number | null;
	speed?: number | null;
	confiabilidadeGPS?: number;
	suspeitaSpoofing?: boolean;
	motivoSuspeita?: string;
	endereco?: string;
	cidade?: string;
	estado?: string;
	pais?: string;
}> {
	if (typeof navigator === 'undefined' || !navigator.geolocation) {
		console.warn('Geolocalização não suportada');
		return {};
	}

	// Usar múltiplas leituras para detectar spoofing
	const localizacaoMultipla = await obterLocalizacaoMultipla();

	if (!localizacaoMultipla.latitude || !localizacaoMultipla.longitude) {
		console.warn('Não foi possível obter localização');
		return {
			confiabilidadeGPS: 0,
			suspeitaSpoofing: true,
			motivoSuspeita: 'Não foi possível obter localização'
		};
	}

	const {
		latitude,
		longitude,
		precisao,
		altitude,
		altitudeAccuracy,
		heading,
		speed,
		confiabilidade,
		suspeitaSpoofing,
		motivoSuspeita
	} = localizacaoMultipla;

	// Tentar obter endereço via reverse geocoding
	let endereco = '';
	let cidade = '';
	let estado = '';
	let pais = '';

	try {
		const response = await fetch(
			`https://nominatim.openstreetmap.org/reverse?format=json&lat=${latitude}&lon=${longitude}&zoom=18&addressdetails=1`,
			{
				headers: {
					'User-Agent': 'SGSE-App/1.0'
				}
			}
		);
		if (response.ok) {
			const data = (await response.json()) as {
				address?: {
					road?: string;
					house_number?: string;
					city?: string;
					town?: string;
					state?: string;
					country?: string;
				};
			};
			if (data.address) {
				const addr = data.address;
				if (addr.road) {
					endereco = `${addr.road}${addr.house_number ? `, ${addr.house_number}` : ''}`;
				}
				cidade = addr.city || addr.town || '';
				estado = addr.state || '';
				pais = addr.country || '';
			}
		}
	} catch (error) {
		console.warn('Erro ao obter endereço:', error);
	}

	// Validar timezone vs localização
	if (typeof navigator !== 'undefined') {
		const timezoneAtual = Intl.DateTimeFormat().resolvedOptions().timeZone;
		const timezoneEsperado = obterTimezonePorCoordenadas(latitude, longitude);

		// Se timezone é muito diferente, pode ser suspeito
		if (
			timezoneAtual !== timezoneEsperado &&
			timezoneAtual !== 'America/Recife' &&
			timezoneEsperado !== 'America/Recife'
		) {
			console.warn(`Timezone inconsistente: esperado ${timezoneEsperado}, atual ${timezoneAtual}`);
		}
	}

	console.log('Localização obtida com validações:', {
		latitude,
		longitude,
		confiabilidade: confiabilidade.toFixed(2),
		suspeitaSpoofing,
		motivoSuspeita
	});

	return {
		latitude,
		longitude,
		precisao,
		altitude,
		altitudeAccuracy,
		heading,
		speed,
		confiabilidadeGPS: confiabilidade,
		suspeitaSpoofing: suspeitaSpoofing || false,
		motivoSuspeita,
		endereco,
		cidade,
		estado,
		pais
	};
}

/**
 * Obtém IP público
 */
export async function obterIPPublico(): Promise<string | undefined> {
	try {
		const response = await fetch('https://api.ipify.org?format=json');
		if (response.ok) {
			const data = (await response.json()) as { ip: string };
			return data.ip;
		}
	} catch (error) {
		console.warn('Erro ao obter IP público:', error);
	}
	return undefined;
}

/**
 * Solicita permissão para acesso aos sensores de movimento (iOS 13+)
 */
async function solicitarPermissaoSensor(): Promise<PermissionState> {
	if (
		typeof DeviceMotionEvent === 'undefined' ||
		typeof (DeviceMotionEvent as { requestPermission?: () => Promise<PermissionState> })
			.requestPermission !== 'function'
	) {
		// Permissão não necessária ou já concedida (navegadores modernos)
		return 'granted';
	}

	try {
		const requestPermission = (
			DeviceMotionEvent as { requestPermission: () => Promise<PermissionState> }
		).requestPermission;
		const resultado = await requestPermission();
		return resultado;
	} catch (error) {
		console.warn('Erro ao solicitar permissão de sensor:', error);
		return 'denied';
	}
}

/**
 * Obtém dados de acelerômetro e giroscópio durante um período
 * @param duracaoMs Duração da coleta em milissegundos (padrão: 5000ms = 5 segundos)
 */
async function obterDadosAcelerometro(duracaoMs: number = 5000): Promise<{
	acelerometro?: DadosAcelerometro;
	giroscopio?: DadosGiroscopio;
	sensorDisponivel: boolean;
	permissaoNegada: boolean;
}> {
	// Verificar se DeviceMotionEvent está disponível
	if (typeof DeviceMotionEvent === 'undefined' || typeof DeviceOrientationEvent === 'undefined') {
		return {
			sensorDisponivel: false,
			permissaoNegada: false
		};
	}

	// Solicitar permissão (especialmente necessário no iOS 13+)
	const permissao = await solicitarPermissaoSensor();

	if (permissao === 'denied') {
		return {
			sensorDisponivel: true,
			permissaoNegada: true
		};
	}

	return new Promise((resolve) => {
		const leiturasAcelerometro: Array<{ x: number; y: number; z: number; timestamp: number }> = [];
		const leiturasGiroscopio: Array<{
			alpha: number;
			beta: number;
			gamma: number;
			timestamp: number;
		}> = [];

		const timeoutId = setTimeout(() => {
			window.removeEventListener('devicemotion', handleDeviceMotion);
			window.removeEventListener('deviceorientation', handleDeviceOrientation);

			// Processar dados de acelerômetro
			let acelerometro: DadosAcelerometro | undefined;
			if (leiturasAcelerometro.length > 0) {
				const ultimaLeitura = leiturasAcelerometro[leiturasAcelerometro.length - 1]!;

				// Calcular magnitude média
				const magnitudes = leiturasAcelerometro.map((l) =>
					Math.sqrt(l.x * l.x + l.y * l.y + l.z * l.z)
				);
				const magnitude = magnitudes.reduce((sum, m) => sum + m, 0) / magnitudes.length;

				// Calcular variância para detectar movimento
				const mediaX =
					leiturasAcelerometro.reduce((sum, l) => sum + l.x, 0) / leiturasAcelerometro.length;
				const mediaY =
					leiturasAcelerometro.reduce((sum, l) => sum + l.y, 0) / leiturasAcelerometro.length;
				const mediaZ =
					leiturasAcelerometro.reduce((sum, l) => sum + l.z, 0) / leiturasAcelerometro.length;

				const variacoes = leiturasAcelerometro.map(
					(l) => Math.pow(l.x - mediaX, 2) + Math.pow(l.y - mediaY, 2) + Math.pow(l.z - mediaZ, 2)
				);
				const variacao = variacoes.reduce((sum, v) => sum + v, 0) / variacoes.length;

				// Detectar movimento: se variância > 0.01, há movimento
				const movimentoDetectado = variacao > 0.01;

				acelerometro = {
					x: ultimaLeitura.x,
					y: ultimaLeitura.y,
					z: ultimaLeitura.z,
					movimentoDetectado,
					magnitude,
					variacao,
					timestamp: ultimaLeitura.timestamp
				};
			}

			// Processar dados de giroscópio
			let giroscopio: DadosGiroscopio | undefined;
			if (leiturasGiroscopio.length > 0) {
				const ultimaLeitura = leiturasGiroscopio[leiturasGiroscopio.length - 1]!;
				giroscopio = {
					alpha: ultimaLeitura.alpha || 0,
					beta: ultimaLeitura.beta || 0,
					gamma: ultimaLeitura.gamma || 0
				};
			}

			resolve({
				acelerometro,
				giroscopio,
				sensorDisponivel: true,
				permissaoNegada: false
			});
		}, duracaoMs);

		function handleDeviceMotion(event: DeviceMotionEvent) {
			if (event.accelerationIncludingGravity) {
				const acc = event.accelerationIncludingGravity;
				if (acc.x !== null && acc.y !== null && acc.z !== null) {
					leiturasAcelerometro.push({
						x: acc.x,
						y: acc.y,
						z: acc.z,
						timestamp: Date.now()
					});
				}
			}
		}

		function handleDeviceOrientation(event: DeviceOrientationEvent) {
			if (event.alpha !== null && event.beta !== null && event.gamma !== null) {
				leiturasGiroscopio.push({
					alpha: event.alpha,
					beta: event.beta,
					gamma: event.gamma,
					timestamp: Date.now()
				});
			}
		}

		window.addEventListener('devicemotion', handleDeviceMotion);
		window.addEventListener('deviceorientation', handleDeviceOrientation);
	});
}

/**
 * Obtém todas as informações do dispositivo
 */
export async function obterInformacoesDispositivo(): Promise<InformacoesDispositivo> {
	const informacoes: InformacoesDispositivo = {};

	// Informações básicas
	if (typeof navigator !== 'undefined') {
		informacoes.userAgent = navigator.userAgent;
		informacoes.idioma = navigator.language || navigator.languages?.[0];
		informacoes.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
	}

	// Informações do navegador
	const navegador = detectarNavegador();
	informacoes.browser = navegador.browser;
	informacoes.browserVersion = navegador.browserVersion;
	informacoes.engine = navegador.engine;

	// Informações do sistema
	const sistema = detectarSistemaOperacional();
	informacoes.sistemaOperacional = sistema.sistemaOperacional;
	informacoes.osVersion = sistema.osVersion;
	informacoes.arquitetura = sistema.arquitetura;
	informacoes.plataforma = sistema.plataforma;

	// Tipo de dispositivo
	const dispositivo = detectarTipoDispositivo();
	informacoes.deviceType = dispositivo.deviceType;
	informacoes.isMobile = dispositivo.isMobile;
	informacoes.isTablet = dispositivo.isTablet;
	informacoes.isDesktop = dispositivo.isDesktop;

	// Informações da tela
	const tela = obterInformacoesTela();
	informacoes.screenResolution = tela.screenResolution;
	informacoes.coresTela = tela.coresTela;

	// Informações de conexão, memória e localização (assíncronas)
	const [connectionType, memoryInfo, ipPublico, ipLocal, localizacao, dadosSensores] =
		await Promise.all([
			obterInformacoesConexao(),
			Promise.resolve(obterInformacoesMemoria()),
			obterIPPublico(),
			getLocalIP(),
			obterLocalizacao(),
			obterDadosAcelerometro(5000) // Coletar dados por 5 segundos
		]);

	informacoes.connectionType = connectionType;
	informacoes.memoryInfo = memoryInfo;
	informacoes.ipPublico = ipPublico;
	informacoes.ipLocal = ipLocal;
	informacoes.latitude = localizacao.latitude;
	informacoes.longitude = localizacao.longitude;
	informacoes.precisao = localizacao.precisao;
	informacoes.altitude = localizacao.altitude ?? null;
	informacoes.altitudeAccuracy = localizacao.altitudeAccuracy ?? null;
	informacoes.heading = localizacao.heading ?? null;
	informacoes.speed = localizacao.speed ?? null;
	informacoes.confiabilidadeGPS = localizacao.confiabilidadeGPS;
	informacoes.suspeitaSpoofing = localizacao.suspeitaSpoofing;
	informacoes.motivoSuspeita = localizacao.motivoSuspeita;
	informacoes.endereco = localizacao.endereco;
	informacoes.cidade = localizacao.cidade;
	informacoes.estado = localizacao.estado;
	informacoes.pais = localizacao.pais;

	// Dados de sensores
	informacoes.acelerometro = dadosSensores.acelerometro;
	informacoes.giroscopio = dadosSensores.giroscopio;
	informacoes.sensorDisponivel = dadosSensores.sensorDisponivel;
	informacoes.permissaoNegada = dadosSensores.permissaoNegada;

	// IP address (usar público se disponível, senão local)
	informacoes.ipAddress = ipPublico || ipLocal;

	return informacoes;
}
