class EveAIException(Exception):
    """Base exception class for EveAI API"""

    def __init__(self, message, status_code=400, payload=None):
        super().__init__()
        self.message = message
        self.status_code = status_code
        self.payload = payload

    def to_dict(self):
        rv = dict(self.payload or ())
        rv['message'] = self.message
        rv['error'] = self.__class__.__name__
        return rv

    def __str__(self):
        return self.message  # Return the message when the exception is converted to a string


class EveAIInvalidLanguageException(EveAIException):
    """Raised when an invalid language is provided"""

    def __init__(self, message="Langage is required", status_code=400, payload=None):
        super().__init__(message, status_code, payload)


class EveAIDoubleURLException(EveAIException):
    """Raised when an existing url is provided"""

    def __init__(self, message="URL already exists", status_code=400, payload=None):
        super().__init__(message, status_code, payload)


class EveAIUnsupportedFileType(EveAIException):
    """Raised when an invalid file type is provided"""

    def __init__(self, message="Filetype is not supported by current active processors", status_code=400, payload=None):
        super().__init__(message, status_code, payload)


class EveAINoProcessorFound(EveAIException):
    """Raised when no processor is found for a given file type"""

    def __init__(self, catalog_id, file_type, file_subtype, status_code=400, payload=None):
        message = f"No active processor found for catalog {catalog_id} with file type {file_type} and subtype {file_subtype}"
        super().__init__(message, status_code, payload)


class EveAINoContentFound(EveAIException):
    """Raised when no content is found for a given document"""

    def __init__(self, document_id, document_version_id, status_code=400, payload=None):
        self.document_id = document_id
        self.document_version_id = document_version_id
        message = f"No content found while processing Document with ID {document_id} and version {document_version_id}."
        super().__init__(message, status_code, payload)


class EveAINoLicenseForTenant(EveAIException):
    """Raised when no active license for a tenant is provided"""

    def __init__(self, message="No license for tenant found", status_code=400, payload=None):
        super().__init__(message, status_code, payload)


class EveAITenantNotFound(EveAIException):
    """Raised when a tenant is not found"""

    def __init__(self, tenant_id, status_code=400, payload=None):
        self.tenant_id = tenant_id
        message = f"Tenant {tenant_id} not found"
        super().__init__(message, status_code, payload)


class EveAITenantInvalid(EveAIException):
    """Raised when a tenant is invalid"""

    def __init__(self, tenant_id, status_code=400, payload=None):
        self.tenant_id = tenant_id
        # Construct the message dynamically
        message = f"Tenant with ID '{tenant_id}' is not valid. Please contact the System Administrator."
        super().__init__(message, status_code, payload)


class EveAINoActiveLicense(EveAIException):
    """Raised when a tenant has no active licenses"""

    def __init__(self, tenant_id, status_code=400, payload=None):
        self.tenant_id = tenant_id
        # Construct the message dynamically
        message = f"Tenant with ID '{tenant_id}' has no active licenses. Please contact the System Administrator."
        super().__init__(message, status_code, payload)


class EveAIInvalidCatalog(EveAIException):
    """Raised when a catalog cannot be found"""

    def __init__(self, tenant_id, catalog_id, status_code=400, payload=None):
        self.tenant_id = tenant_id
        self.catalog_id = catalog_id
        # Construct the message dynamically
        message = f"Tenant with ID '{tenant_id}' has no valid catalog with ID {catalog_id}. Please contact the System Administrator."
        super().__init__(message, status_code, payload)


class EveAIInvalidProcessor(EveAIException):
    """Raised when no valid processor can be found for a given Catalog ID"""

    def __init__(self, tenant_id, catalog_id, file_type, status_code=400, payload=None):
        self.tenant_id = tenant_id
        self.catalog_id = catalog_id
        self.file_type = file_type
        # Construct the message dynamically
        message = (f"Tenant with ID '{tenant_id}' has no valid {file_type} processor for catalog with ID {catalog_id}. "
                   f"Please contact the System Administrator.")
        super().__init__(message, status_code, payload)


class EveAIInvalidDocument(EveAIException):
    """Raised when a tenant has no document with given ID"""

    def __init__(self, tenant_id, document_id, status_code=400, payload=None):
        self.tenant_id = tenant_id
        self.document_id = document_id
        # Construct the message dynamically
        message = f"Tenant with ID '{tenant_id}' has no document with ID {document_id}."
        super().__init__(message, status_code, payload)


class EveAIInvalidDocumentVersion(EveAIException):
    """Raised when a tenant has no document version with given ID"""

    def __init__(self, tenant_id, document_version_id, status_code=400, payload=None):
        self.tenant_id = tenant_id
        self.document_version_id = document_version_id
        # Construct the message dynamically
        message = f"Tenant with ID '{tenant_id}' has no document version with ID {document_version_id}."
        super().__init__(message, status_code, payload)


class EveAISocketInputException(EveAIException):
    """Raised when a socket call receives an invalid payload"""

    def __init__(self, message, status_code=400, payload=None):
        super.__init__(message, status_code, payload)


class EveAIInvalidEmbeddingModel(EveAIException):
    """Raised when no or an invalid embedding model is provided in the catalog"""

    def __init__(self, tenant_id, catalog_id, status_code=400, payload=None):
        self.tenant_id = tenant_id
        self.catalog_id = catalog_id
        # Construct the message dynamically
        message = f"Tenant with ID '{tenant_id}' has no or an invalid embedding model in Catalog {catalog_id}."
        super().__init__(message, status_code, payload)


class EveAIDoublePartner(EveAIException):
    """Raised when there is already a partner defined for a given tenant (while registering a partner)"""

    def __init__(self, tenant_id, status_code=400, payload=None):
        self.tenant_id = tenant_id
        # Construct the message dynamically
        message = f"Tenant with ID '{tenant_id}' is already defined as a Partner."
        super().__init__(message, status_code, payload)


class EveAIRoleAssignmentException(EveAIException):
    """Exception raised when a role cannot be assigned due to business rules"""

    def __init__(self, message, status_code=403, payload=None):
        super().__init__(message, status_code, payload)


class EveAINoManagementPartnerService(EveAIException):
    """Exception raised when the operation requires the logged in partner (or selected parter by Super User)
    does not have a MANAGEMENT_SERVICE"""

    def __init__(self, message="No Management Service defined for partner", status_code=403, payload=None):
        super().__init__(message, status_code, payload)


class EveAINoSessionTenant(EveAIException):
    """Exception raised when no session tenant is set"""

    def __init__(self, message="No Session Tenant selected. Cannot perform requested action.", status_code=403,
                 payload=None):
        super().__init__(message, status_code, payload)


class EveAINoSessionPartner(EveAIException):
    """Exception raised when no session partner is set"""

    def __init__(self, message="No Session Partner selected. Cannot perform requested action.", status_code=403,
                 payload=None):
        super().__init__(message, status_code, payload)


class EveAINoManagementPartnerForTenant(EveAIException):
    """Exception raised when the selected partner is no management partner for tenant"""

    def __init__(self, message="No Management Partner for Tenant", status_code=403, payload=None):
        super().__init__(message, status_code, payload)


class EveAIQuotaExceeded(EveAIException):
    """Base exception for quota-related errors"""

    def __init__(self, message, quota_type, current_usage, limit, additional=0, status_code=400, payload=None):
        super().__init__(message, status_code, payload)
        self.quota_type = quota_type
        self.current_usage = current_usage
        self.limit = limit
        self.additional = additional


class EveAIStorageQuotaExceeded(EveAIQuotaExceeded):
    """Raised when storage quota is exceeded"""

    def __init__(self, current_usage, limit, additional, status_code=400, payload=None):
        message = (f"Storage quota exceeded. Current: {current_usage:.1f}MB, "
                   f"Additional: {additional:.1f}MB, Limit: {limit}MB")
        super().__init__(message, "storage", current_usage, limit, additional, status_code, payload)


class EveAIEmbeddingQuotaExceeded(EveAIQuotaExceeded):
    """Raised when embedding quota is exceeded"""

    def __init__(self, current_usage, limit, additional, status_code=400, payload=None):
        message = (f"Embedding quota exceeded. Current: {current_usage:.1f}MB, "
                   f"Additional: {additional:.1f}MB, Limit: {limit}MB")
        super().__init__(message, "embedding", current_usage, limit, additional, status_code, payload)


class EveAIInteractionQuotaExceeded(EveAIQuotaExceeded):
    """Raised when the interaction token quota is exceeded"""

    def __init__(self, current_usage, limit, status_code=400, payload=None):
        message = (f"Interaction token quota exceeded. Current: {current_usage:.2f}M tokens, "
                   f"Limit: {limit:.2f}M tokens")
        super().__init__(message, "interaction", current_usage, limit, 0, status_code, payload)


class EveAIQuotaWarning(EveAIException):
    """Warning when approaching quota limits (not blocking)"""

    def __init__(self, message, quota_type, usage_percentage, status_code=200, payload=None):
        super().__init__(message, status_code, payload)
        self.quota_type = quota_type
        self.usage_percentage = usage_percentage


class EveAILicensePeriodsExceeded(EveAIException):
    """Raised when no more license periods can be created for a given license"""

    def __init__(self, license_id, status_code=400, payload=None):
        message = f"No more license periods can be created for license with ID {license_id}. "
        super().__init__(message, status_code, payload)


class EveAIPendingLicensePeriod(EveAIException):
    """Raised when a license period is pending"""

    def __init__(self, status_code=400, payload=None):
        message = f"Basic Fee Payment has not been received yet. Please ensure payment has been made, and please wait for payment to be processed."
        super().__init__(message, status_code, payload)


class EveAISpecialistExecutionError(EveAIException):
    """Raised when an error occurs during specialist execution"""

    def __init__(self, tenant_id, specialist_id, session_id, details, status_code=400, payload=None):
        message = (f"Error during specialist {specialist_id} execution \n"
                   f"with Session ID {session_id} \n"
                   f"for Tenant {tenant_id}. \n"
                   f"Details: {details} \n"
                   f"The System Administrator has been notified. Please try again later.")
        super().__init__(message, status_code, payload)
