"""
Error codes, log levels, and error-related definitions for nexa-sdk.
This module contains C-level definitions (error codes, log levels, callbacks).
"""

import ctypes
from typing import Optional

from ._lib import _lib

# Error codes from ml.h
ML_SUCCESS = 0
ML_ERROR_COMMON_UNKNOWN = -100000
ML_ERROR_COMMON_INVALID_INPUT = -100001
ML_ERROR_COMMON_MEMORY_ALLOCATION = -100003
ML_ERROR_COMMON_FILE_NOT_FOUND = -100004
ML_ERROR_COMMON_NOT_INITIALIZED = -100007
ML_ERROR_COMMON_NOT_SUPPORTED = -100013
ML_ERROR_COMMON_MODEL_LOAD = -100201
ML_ERROR_COMMON_MODEL_INVALID = -100203
ML_ERROR_COMMON_LICENSE_INVALID = -100601
ML_ERROR_COMMON_LICENSE_EXPIRED = -100602
ML_ERROR_LLM_TOKENIZATION_FAILED = -200001
ML_ERROR_LLM_TOKENIZATION_CONTEXT_LENGTH = -200004
ML_ERROR_LLM_GENERATION_FAILED = -200101
ML_ERROR_LLM_GENERATION_PROMPT_TOO_LONG = -200103

# Log levels
ML_LOG_LEVEL_TRACE = 0
ML_LOG_LEVEL_DEBUG = 1
ML_LOG_LEVEL_INFO = 2
ML_LOG_LEVEL_WARN = 3
ML_LOG_LEVEL_ERROR = 4

# Callback types
ml_log_callback = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p)

# Function signatures
_lib.ml_get_error_message.argtypes = [ctypes.c_int32]
_lib.ml_get_error_message.restype = ctypes.c_char_p


# ============================================================================
# Python Exception Classes
# ============================================================================

# ============================================================================
# Error Handling Functions
# ============================================================================


def ml_get_error_message(error_code: int) -> str:
    """
    Get error message string for error code.

    Args:
        error_code: The error code.

    Returns:
        Error message string.
    """
    result = _lib.ml_get_error_message(error_code)
    if result:
        return result.decode('utf-8')
    return f'Unknown error code: {error_code}'


def check_error(error_code: int) -> None:
    """
    Check error code and raise appropriate exception if not success.

    Args:
        error_code: The error code returned from a C function.

    Raises:
        NexaError: Appropriate exception based on error code.
    """
    if error_code == ML_SUCCESS:
        return

    # Get the exception class from the map, or use base NexaError
    exception_class = _ERROR_MAP.get(error_code, NexaError)
    raise exception_class(error_code)


# ============================================================================
# Python Exception Classes
# ============================================================================


class NexaError(Exception):
    """Base exception for all nexa-sdk errors."""

    def __init__(self, error_code: int, message: Optional[str] = None):
        self.error_code = error_code
        if message is None:
            message = ml_get_error_message(error_code)
        self.message = message
        super().__init__(self.message)

    def __str__(self) -> str:
        return f'[{self.error_code}] {self.message}'


class NexaCommonError(NexaError):
    """Base class for common errors."""

    pass


class NexaUnknownError(NexaCommonError):
    """Unknown error."""

    pass


class NexaInvalidInputError(NexaCommonError):
    """Invalid input parameters or handle."""

    pass


class NexaMemoryAllocationError(NexaCommonError):
    """Memory allocation failed."""

    pass


class NexaFileNotFoundError(NexaCommonError):
    """File not found or inaccessible."""

    pass


class NexaNotInitializedError(NexaCommonError):
    """Library not initialized."""

    pass


class NexaNotSupportedError(NexaCommonError):
    """Operation not supported."""

    pass


class NexaModelLoadError(NexaCommonError):
    """Model loading failed."""

    pass


class NexaModelInvalidError(NexaCommonError):
    """Invalid model format."""

    pass


class NexaLicenseInvalidError(NexaCommonError):
    """Invalid license."""

    pass


class NexaLicenseExpiredError(NexaCommonError):
    """License expired."""

    pass


class NexaLLMError(NexaError):
    """Base class for LLM errors."""

    pass


class NexaLLMTokenizationError(NexaLLMError):
    """Tokenization failed."""

    pass


class NexaLLMTokenizationContextLengthError(NexaLLMError):
    """Context length exceeded."""

    pass


class NexaLLMGenerationError(NexaLLMError):
    """Text generation failed."""

    pass


class NexaLLMGenerationPromptTooLongError(NexaLLMError):
    """Input prompt too long."""

    pass


# Error code to exception class mapping
_ERROR_MAP = {
    ML_ERROR_COMMON_UNKNOWN: NexaUnknownError,
    ML_ERROR_COMMON_INVALID_INPUT: NexaInvalidInputError,
    ML_ERROR_COMMON_MEMORY_ALLOCATION: NexaMemoryAllocationError,
    ML_ERROR_COMMON_FILE_NOT_FOUND: NexaFileNotFoundError,
    ML_ERROR_COMMON_NOT_INITIALIZED: NexaNotInitializedError,
    ML_ERROR_COMMON_NOT_SUPPORTED: NexaNotSupportedError,
    ML_ERROR_COMMON_MODEL_LOAD: NexaModelLoadError,
    ML_ERROR_COMMON_MODEL_INVALID: NexaModelInvalidError,
    ML_ERROR_COMMON_LICENSE_INVALID: NexaLicenseInvalidError,
    ML_ERROR_COMMON_LICENSE_EXPIRED: NexaLicenseExpiredError,
    ML_ERROR_LLM_TOKENIZATION_FAILED: NexaLLMTokenizationError,
    ML_ERROR_LLM_TOKENIZATION_CONTEXT_LENGTH: NexaLLMTokenizationContextLengthError,
    ML_ERROR_LLM_GENERATION_FAILED: NexaLLMGenerationError,
    ML_ERROR_LLM_GENERATION_PROMPT_TOO_LONG: NexaLLMGenerationPromptTooLongError,
}
