import datetime
import logging

from django.db.utils import (
    IntegrityError,
    OperationalError,
    ProgrammingError,
    InterfaceError,
    DataError,
)
from django.http import Http404
from django.shortcuts import render

logger = logging.getLogger(__name__)

class ServiceUnavailableException(Exception):
    """Custom exception for service unavailability."""
    pass

class HandleAllErrorsMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # Extract roles from session
        admin = request.session.get('admin')
        gate_keeper = request.session.get('gate_keeper')
        employee = request.session.get('employee')

        try:
            response = self.get_response(request)
            return response

        except IntegrityError as e:
            self._log_error(request, "IntegrityError", e)
            return self._handle_error(request, "Database Error: Integrity constraint violation.", 500, admin, gate_keeper, employee)

        except OperationalError as e:
            self._log_error(request, "OperationalError", e)
            return self._handle_error(request, "Operational Database Error: Please contact support.", 500, admin, gate_keeper, employee)

        except ProgrammingError as e:
            self._log_error(request, "ProgrammingError", e)
            return self._handle_error(request, "Programming Error: An issue occurred with the database query.", 500, admin, gate_keeper, employee)

        except InterfaceError as e:
            self._log_error(request, "InterfaceError", e)
            return self._handle_error(request, "Database Interface Error: Connection issue detected.", 500, admin, gate_keeper, employee)

        except DataError as e:
            self._log_error(request, "DataError", e)
            return self._handle_error(request, "Data Error: Invalid or corrupted data encountered.", 500, admin, gate_keeper, employee)

        except Http404 as e:
            self._log_error(request, "Http404", e)
            return self._handle_error(request, "Page not found", 404, admin, gate_keeper, employee)

        except ServiceUnavailableException as e:
            self._log_error(request, "ServiceUnavailableException", e)
            return self._handle_error(request, "Service is temporarily unavailable. Please try again later.", 503, admin, gate_keeper, employee)

        except Exception as e:
            self._log_error(request, "UnhandledException", e)
            return self._handle_error(request, f"An unexpected error occurred: {str(e)}", 500, admin, gate_keeper, employee)

    def process_exception(self, request, exception):
        admin = request.session.get('admin')
        gate_keeper = request.session.get('gate_keeper')
        employee = request.session.get('employee')

        exc_type = type(exception).__name__
        self._log_error(request, exc_type, exception)

        if isinstance(exception, Http404):
            return self._handle_error(request, "Page not found", 404, admin, gate_keeper, employee)
        elif isinstance(exception, IntegrityError):
            return self._handle_error(request, "Database Error: Integrity constraint violation.", 500, admin, gate_keeper, employee)
        elif isinstance(exception, OperationalError):
            return self._handle_error(request, "Operational Database Error: Please contact support.", 500, admin, gate_keeper, employee)
        elif isinstance(exception, ProgrammingError):
            return self._handle_error(request, "Programming Error: An issue occurred with the database query.", 500, admin, gate_keeper, employee)
        elif isinstance(exception, InterfaceError):
            return self._handle_error(request, "Database Interface Error: Connection issue detected.", 500, admin, gate_keeper, employee)
        elif isinstance(exception, DataError):
            return self._handle_error(request, "Data Error: Invalid or corrupted data encountered.", 500, admin, gate_keeper, employee)
        elif isinstance(exception, ServiceUnavailableException):
            return self._handle_error(request, "Service is temporarily unavailable. Please try again later.", 503, admin, gate_keeper, employee)
        else:
            return self._handle_error(request, f"An unexpected error occurred: {str(exception)}", 500, admin, gate_keeper, employee)

    def _handle_error(self, request, error_message, status, admin, gate_keeper, employee):
        return render(request, 'error.html', {
            'error_message': error_message,
            'status': str(status),
            'admin': admin,
            'gate_keeper': gate_keeper,
            'employee': employee
        }, status=status)

    def _log_error(self, request, exc_type, exception):
        now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        url = request.get_full_path()
        logger.error(f"[{now}] URL: {url} | {exc_type} occurred: {exception}")
