"""Classes for handling exceptions."""
import lambdalogging
from clients.s3_client import S3Client
from config import UNSUPPORTED_FILE_HANDLING
from constants import UNSUPPORTED_FILE_HANDLING_VALID_VALUES, S3_STATUS_CODES, S3_ERROR_CODES, error_code_to_enums
from exceptions import UnsupportedFileException, FileSizeLimitExceededException, S3DownloadException, InvalidConfigurationException, \
    InvalidRequestException, RestrictedDocumentException, TimeoutException

LOG = lambdalogging.getLogger(__name__)


class ExceptionHandler:
    """Handler enclosing an action to be taken in case of an error occurred while processing files."""

    def __init__(self, s3_client: S3Client):
        self.s3_client = s3_client

    def handle_exception(self, exception: BaseException, request_route: str, request_token: str):
        """Handle exception and take appropriate actions."""
        try:
            raise exception
        except UnsupportedFileException as e:
            self._handle_unsupported_file_exception(e, request_route, request_token)
        except InvalidConfigurationException as e:
            LOG.error(f"Encountered an invalid configuration setup. {e}", exc_info=True)
            self.s3_client.respond_back_with_error(S3_STATUS_CODES.BAD_REQUEST_400,
                                                   S3_ERROR_CODES.InvalidRequest,
                                                   "Lambda function has been incorrectly setup", request_route,
                                                   request_token)
        except FileSizeLimitExceededException:
            LOG.info(
                f"File size of the requested object exceeds maximum file size supported. Responding back with"
                f"error: {S3_STATUS_CODES.BAD_REQUEST_400.name} ")
            self.s3_client.respond_back_with_error(S3_STATUS_CODES.BAD_REQUEST_400, S3_ERROR_CODES.EntityTooLarge,
                                                   "Size of the requested object exceeds maximum file size supported", request_route,
                                                   request_token)
        except InvalidRequestException as e:
            LOG.info(f"Encountered an invalid request {e}", exc_info=True)
            self.s3_client.respond_back_with_error(S3_STATUS_CODES.BAD_REQUEST_400, S3_ERROR_CODES.InvalidRequest,
                                                   e.message, request_route, request_token)
        except S3DownloadException as e:
            LOG.error(f"Error downloading from presigned url. {e}", exc_info=True)
            status_code, error_code = error_code_to_enums(e.s3_error_code)
            self.s3_client.respond_back_with_error(status_code, error_code, e.s3_message,
                                                   request_route, request_token)
        except RestrictedDocumentException as e:
            LOG.error(f"Document contains pii. {e}", exc_info=True)
            self.s3_client.respond_back_with_error(S3_STATUS_CODES.FORBIDDEN_403,
                                                   S3_ERROR_CODES.AccessDenied,
                                                   "Document Contains PII",
                                                   request_route, request_token)
        except TimeoutException as e:
            LOG.error(f"Couldn't complete processing within the time limit. {e}", exc_info=True)
            self.s3_client.respond_back_with_error(S3_STATUS_CODES.BAD_REQUEST_400,
                                                   S3_ERROR_CODES.RequestTimeout,
                                                   "Failed to complete document processing within time limit",
                                                   request_route, request_token)
        except Exception as e:
            LOG.error(f"Internal error {e} occurred while processing the file", exc_info=True)
            self.s3_client.respond_back_with_error(S3_STATUS_CODES.INTERNAL_SERVER_ERROR_500, S3_ERROR_CODES.InternalError,
                                                   "An internal error occurred while processing the file", request_route,
                                                   request_token)

    def _handle_unsupported_file_exception(self, exception: UnsupportedFileException, request_route: str, request_token: str):
        """Handle the action to be taken in case we encounter a file which is not supported by Lambda's core functionality."""
        LOG.debug("File is not supported for determining and redacting pii data.")

        if UNSUPPORTED_FILE_HANDLING == UNSUPPORTED_FILE_HANDLING_VALID_VALUES.PASS:
            LOG.debug("Unsupported file handling strategy is set to PASS. Responding back with the file content to the caller")
            self.s3_client.respond_back_with_data(exception.file_content, exception.http_headers, request_route, request_token)

        elif UNSUPPORTED_FILE_HANDLING == UNSUPPORTED_FILE_HANDLING_VALID_VALUES.FAIL:
            LOG.debug(
                f"Unsupported file handling strategy is set to FAIL. Responding back with error: "
                f"{S3_ERROR_CODES.UnexpectedContent.name} to the caller")
            self.s3_client.respond_back_with_error(S3_STATUS_CODES.BAD_REQUEST_400,
                                                   S3_ERROR_CODES.UnexpectedContent,
                                                   "Unsupported file encountered for determining Pii", request_route, request_token)
        else:
            raise Exception("Unknown exception handling strategy found for UnsupportedFileException.")