# -*- coding: utf-8 -*-
"""
Excepciones personalizadas para basskit.

Define la jerarquía de excepciones utilizada en toda la biblioteca
para reportar errores de forma clara y en español.
"""


class BassKitError(Exception):
	"""Excepción base para todos los errores de basskit."""

	def __init__(self, mensaje: str = "Error desconocido en basskit") -> None:
		self.mensaje = mensaje
		super().__init__(self.mensaje)

	def __str__(self) -> str:
		return self.mensaje


class ErrorDLLNoEncontrada(BassKitError):
	"""Se lanza cuando no se encuentra una DLL requerida."""

	def __init__(self, nombre_dll: str, ruta_buscada: str = "") -> None:
		self.nombre_dll = nombre_dll
		self.ruta_buscada = ruta_buscada
		detalle = f"No se encontró la DLL '{nombre_dll}'"
		if ruta_buscada:
			detalle += f" en '{ruta_buscada}'"
		super().__init__(detalle)


class ErrorArquitecturaInvalida(BassKitError):
	"""Se lanza cuando la arquitectura del sistema no es compatible."""

	def __init__(self, arquitectura_detectada: str = "") -> None:
		detalle = "basskit solo soporta Windows 64 bits"
		if arquitectura_detectada:
			detalle += f" (se detectó: {arquitectura_detectada})"
		super().__init__(detalle)


class ErrorVersionPython(BassKitError):
	"""Se lanza cuando la versión de Python no es compatible."""

	def __init__(self, version_actual: str = "") -> None:
		detalle = "basskit requiere Python 3.13 o superior"
		if version_actual:
			detalle += f" (se detectó: {version_actual})"
		super().__init__(detalle)


class ErrorFuncionNoEncontrada(BassKitError):
	"""Se lanza cuando una función exportada no se encuentra en la DLL."""

	def __init__(self, nombre_funcion: str, nombre_dll: str = "") -> None:
		self.nombre_funcion = nombre_funcion
		self.nombre_dll = nombre_dll
		detalle = f"No se encontró la función '{nombre_funcion}'"
		if nombre_dll:
			detalle += f" en la DLL '{nombre_dll}'"
		super().__init__(detalle)


class ErrorBassNativo(BassKitError):
	"""Se lanza cuando una función nativa de BASS retorna un error."""

	# Tabla de códigos de error con descripciones en español
	CODIGOS_ERROR: dict[int, str] = {
		0: "Sin error (BASS_OK)",
		1: "Error de memoria (BASS_ERROR_MEM)",
		2: "No se puede abrir el archivo (BASS_ERROR_FILEOPEN)",
		3: "No se encuentra un controlador libre/válido (BASS_ERROR_DRIVER)",
		4: "El buffer de muestra se perdió (BASS_ERROR_BUFLOST)",
		5: "Handle inválido (BASS_ERROR_HANDLE)",
		6: "Formato de muestra no soportado (BASS_ERROR_FORMAT)",
		7: "Posición inválida (BASS_ERROR_POSITION)",
		8: "BASS_Init no se ha llamado correctamente (BASS_ERROR_INIT)",
		9: "BASS_Start no se ha llamado correctamente (BASS_ERROR_START)",
		10: "SSL/HTTPS no disponible (BASS_ERROR_SSL)",
		11: "Error de reinicialización (BASS_ERROR_REINIT)",
		13: "Pista inválida (BASS_ERROR_TRACK)",
		14: "Ya inicializado/pausado (BASS_ERROR_ALREADY)",
		17: "No es un canal de audio (BASS_ERROR_NOTAUDIO)",
		18: "No se puede obtener un canal libre (BASS_ERROR_NOCHAN)",
		19: "Tipo ilegal especificado (BASS_ERROR_ILLTYPE)",
		20: "Parámetro ilegal especificado (BASS_ERROR_ILLPARAM)",
		21: "Sin soporte 3D (BASS_ERROR_NO3D)",
		22: "Sin soporte EAX (BASS_ERROR_NOEAX)",
		23: "Número de dispositivo ilegal (BASS_ERROR_DEVICE)",
		24: "No se está reproduciendo (BASS_ERROR_NOPLAY)",
		25: "Frecuencia de muestreo ilegal (BASS_ERROR_FREQ)",
		27: "El stream no es un stream de archivo (BASS_ERROR_NOTFILE)",
		29: "Sin voces de hardware disponibles (BASS_ERROR_NOHW)",
		31: "La música MOD no tiene datos de secuencia (BASS_ERROR_EMPTY)",
		32: "No se pudo abrir conexión de internet (BASS_ERROR_NONET)",
		33: "No se pudo crear el archivo (BASS_ERROR_CREATE)",
		34: "Los efectos no están disponibles (BASS_ERROR_NOFX)",
		37: "Los datos solicitados no están disponibles (BASS_ERROR_NOTAVAIL)",
		38: "El canal es un canal de decodificación (BASS_ERROR_DECODE)",
		39: "Versión de DirectX insuficiente (BASS_ERROR_DX)",
		40: "Tiempo de espera agotado (BASS_ERROR_TIMEOUT)",
		41: "Formato de archivo no soportado (BASS_ERROR_FILEFORM)",
		42: "Altavoz no disponible (BASS_ERROR_SPEAKER)",
		43: "Versión de BASS inválida (BASS_ERROR_VERSION)",
		44: "Códec no disponible/no soportado (BASS_ERROR_CODEC)",
		45: "El canal/archivo ha terminado (BASS_ERROR_ENDED)",
		46: "El dispositivo está ocupado (BASS_ERROR_BUSY)",
		47: "No se puede transmitir como stream (BASS_ERROR_UNSTREAMABLE)",
		48: "Protocolo no soportado (BASS_ERROR_PROTOCOL)",
		49: "Acceso denegado (BASS_ERROR_DENIED)",
		50: "Liberándose (BASS_ERROR_FREEING)",
		51: "Cancelado (BASS_ERROR_CANCEL)",
		-1: "Error desconocido (BASS_ERROR_UNKNOWN)",
	}

	def __init__(
		self,
		codigo: int,
		nombre_funcion: str = "",
		detalle_extra: str = "",
	) -> None:
		self.codigo = codigo
		self.nombre_funcion = nombre_funcion
		descripcion = self.CODIGOS_ERROR.get(
			codigo,
			f"Código de error no reconocido ({codigo})",
		)
		partes = [f"Error BASS código {codigo}: {descripcion}"]
		if nombre_funcion:
			partes.append(f"Función: {nombre_funcion}")
		if detalle_extra:
			partes.append(detalle_extra)
		super().__init__(" | ".join(partes))

	@classmethod
	def descripcion_para_codigo(cls, codigo: int) -> str:
		"""Devuelve la descripción en español para un código de error."""
		return cls.CODIGOS_ERROR.get(
			codigo,
			f"Código de error no reconocido ({codigo})",
		)
