# -*- coding: utf-8 -*-
"""Authorization class - refactored version"""

# pylint: disable=R0913, R1705
from __future__ import print_function
import json
from typing import List, Tuple, Dict, Optional, Any
from dataclasses import dataclass
from acme2certifier.acme_srv.db_handler import DBstore
from acme2certifier.acme_srv.challenge import Challenge
from acme2certifier.acme_srv.challenge_validators.dns_persist_validator import (
    DnsPersistChallengeValidator,
    ChallengeContext,
)
from acme2certifier.acme_srv.helper import (
    generate_random_string,
    uts_now,
    uts_to_date_utc,
    string_sanitize,
)
from acme2certifier.acme_srv.helpers.config import (
    load_config,
    config_eab_profile_load,
    config_dns_server_list_load,
)
from acme2certifier.acme_srv.helpers.domain_utils import (
    is_domain_whitelisted,
    is_ip_whitelisted,
    is_email_whitelisted,
)
from acme2certifier.acme_srv.message import Message
from acme2certifier.acme_srv.nonce import Nonce

NO_ORDER_INFO_LOG = "No order information found for authorization %s"


# Custom Exceptions
class AuthorizationError(Exception):
    """Base exception for authorization operations"""

    # pylint: disable=unnecessary-pass
    pass


class AuthorizationNotFoundError(AuthorizationError):
    """Raised when authorization is not found"""

    # pylint: disable=unnecessary-pass
    pass


class AuthorizationExpiredError(AuthorizationError):
    """Raised when authorization has expired"""

    # pylint: disable=unnecessary-pass
    pass


class ConfigurationError(AuthorizationError):
    """Raised when configuration is invalid"""

    # pylint: disable=unnecessary-pass
    pass


@dataclass
class AuthorizationConfiguration:
    """Configuration for Authorization operations"""

    validity: int = 86400
    expiry_check_disable: bool = False
    authz_path: str = "/acme/authz/"
    caaidentities: Optional[list] = None
    dns_persist_01_support: bool = False
    dns_persist_allow_policy_wildcard: bool = False
    dns_persist_jit_validation: bool = False
    dns_server_list: Optional[List[str]] = None
    dns_validation_pause_timer: Optional[float] = 0.5
    eab_profiling: bool = False
    eab_handler: Optional[Any] = None
    email_identifier_rewrite: bool = False
    prevalidated_domainlist: Optional[List[str]] = None
    prevalidated_iplist: Optional[List[str]] = None
    prevalidated_emaillist: Optional[List[str]] = None


@dataclass
class AuthorizationData:
    """Authorization data structure"""

    name: str
    status: str
    expires: int
    token: str
    identifier: Optional[Dict[str, str]] = None
    challenges: Optional[List[Dict[str, str]]] = None
    wildcard: bool = False

    def to_dict(self) -> Dict[str, str]:
        """Convert to dictionary for response"""
        result = {
            "status": self.status,
            "expires": uts_to_date_utc(self.expires),
        }

        if self.identifier:
            result["identifier"] = self.identifier

        if self.wildcard:
            result["wildcard"] = self.wildcard

        if self.challenges:
            result["challenges"] = self.challenges

        return result


class AuthorizationRepository:
    """Repository class for authorization database operations"""

    def __init__(self, dbstore: DBstore, logger):
        self.dbstore = dbstore
        self.logger = logger

    def find_authorization_by_name(
        self, authz_name: str, field_list: List[str] = None
    ) -> Optional[Dict[str, str]]:
        """Find authorization by name in database"""
        self.logger.debug(
            "AuthorizationRepository.find_authorization_by_name(%s)", authz_name
        )

        try:
            if field_list:
                authz_list = self.dbstore.authorization_lookup(
                    "name", authz_name, field_list
                )
            else:
                authz_list = self.dbstore.authorization_lookup("name", authz_name)

            # authorization_lookup returns a list, we want the first item if it exists
            if authz_list and len(authz_list) > 0:
                return authz_list[0]
            else:
                return None

        except Exception as err:
            self.logger.critical(
                "Database error: failed to lookup authorization '%s': %s",
                authz_name,
                err,
            )
            raise AuthorizationError(
                f"Failed to find authorization '{authz_name}': {err}"
            ) from err

    def update_authorization_expiry(
        self, authz_name: str, token: str, expires: int
    ) -> None:
        """Update authorization expiry date and token"""
        self.logger.debug(
            "AuthorizationRepository.update_authorization_expiry(%s)", authz_name
        )

        try:
            self.dbstore.authorization_update(
                {"name": authz_name, "token": token, "expires": expires}
            )
        except Exception as err:
            self.logger.error(
                "Database error during authorization update (%s): %s", authz_name, err
            )
            raise AuthorizationError(
                f"Failed to update authorization '{authz_name}': {err}"
            ) from err

    def search_expired_authorizations(
        self, timestamp: int, field_list: List[str]
    ) -> List[Dict[str, str]]:
        """Search for expired authorizations"""
        self.logger.debug(
            "AuthorizationRepository.search_expired_authorizations(%s)", timestamp
        )

        try:
            return self.dbstore.authorizations_expired_search(
                "expires", timestamp, vlist=field_list, operant="<="
            )
        except Exception as err:
            self.logger.critical(
                "Database error: failed to search for expired authorizations: %s", err
            )
            raise AuthorizationError(
                f"Failed to search expired authorizations: {err}"
            ) from err

    def mark_authorization_as_expired(self, authz_name: str) -> None:
        """Mark authorization as expired"""
        self.logger.debug(
            "AuthorizationRepository.mark_authorization_as_expired(%s)", authz_name
        )

        try:
            self.dbstore.authorization_update({"name": authz_name, "status": "expired"})
        except Exception as err:
            self.logger.critical(
                "Database error: failed to update authorization '%s' as expired: %s",
                authz_name,
                err,
            )
            raise AuthorizationError(
                f"Failed to expire authorization '{authz_name}': {err}"
            ) from err

    def mark_authorization_as_valid(self, authz_name: str) -> None:
        """Mark authorization as valid"""
        self.logger.debug(
            "AuthorizationRepository.mark_authorization_as_valid(%s)", authz_name
        )

        try:
            self.dbstore.authorization_update({"name": authz_name, "status": "valid"})
        except Exception as err:
            self.logger.critical(
                "Database error: failed to update authorization '%s' as valid: %s",
                authz_name,
                err,
            )
            raise AuthorizationError(
                f"Failed to mark authorization '{authz_name}' as valid: {err}"
            ) from err

    def mark_order_as_ready(self, order_name: str) -> None:
        """Mark order as ready"""
        self.logger.debug("AuthorizationRepository.mark_order_as_ready(%s)", order_name)

        try:
            self.dbstore.order_update({"name": order_name, "status": "ready"})
        except Exception as err:
            self.logger.critical(
                "Database error: failed to update order '%s' as valid: %s",
                order_name,
                err,
            )
            raise AuthorizationError(
                f"Failed to mark order '{order_name}' as valid: {err}"
            ) from err


class AuthorizationBusinessLogic:
    """Business logic for authorization operations"""

    def __init__(
        self,
        config: AuthorizationConfiguration,
        repository: AuthorizationRepository,
        logger,
    ):
        self.config = config
        self.repository = repository
        self.logger = logger

    def extract_authorization_name_from_url(self, url: str, server_name: str) -> str:
        """Extract authorization name from URL"""
        self.logger.debug(
            "AuthorizationBusinessLogic.extract_authorization_name_from_url()"
        )

        authz_name = string_sanitize(
            self.logger, url.replace(f"{server_name}{self.config.authz_path}", "")
        )
        return authz_name

    def generate_authorization_token_and_expiry(self) -> Tuple[str, int]:
        """Generate new token and expiry time"""
        self.logger.debug(
            "AuthorizationBusinessLogic.generate_authorization_token_and_expiry()"
        )

        expires = uts_now() + self.config.validity
        token = generate_random_string(self.logger, 32)
        self.logger.debug(
            "AuthorizationBusinessLogic.generate_authorization_token_and_expiry() ended: Generated token expires at: %s",
            expires,
        )
        return token, expires

    def resolve_authorization_token_and_expiry(
        self, auth_record: Optional[Dict[str, str]]
    ) -> Tuple[str, int, bool]:
        """Return token and expiry, persisting to DB only when no token exists yet."""
        self.logger.debug(
            "AuthorizationBusinessLogic.resolve_authorization_token_and_expiry()"
        )

        existing_token = auth_record.get("token") if auth_record else None
        if isinstance(existing_token, str):
            existing_token = existing_token.strip() or None

        if existing_token:
            expires = auth_record.get("expires")
            if not expires:
                expires = uts_now() + self.config.validity
            self.logger.debug(
                "AuthorizationBusinessLogic.resolve_authorization_token_and_expiry() reusing existing token"
            )
            return existing_token, expires, False

        token, expires = self.generate_authorization_token_and_expiry()
        self.logger.debug(
            "AuthorizationBusinessLogic.resolve_authorization_token_and_expiry() generated new token"
        )
        return token, expires, True

    def enrich_authorization_with_identifier_info(
        self, auth_db_info: Dict[str, str]
    ) -> Tuple[Dict[str, str], bool]:
        """Extract and enrich authorization with identifier information"""
        self.logger.debug(
            "AuthorizationBusinessLogic.enrich_authorization_with_identifier_info()"
        )

        if not auth_db_info:
            return {}, False

        auth_info = auth_db_info[0] if isinstance(auth_db_info, list) else auth_db_info
        identifier_info = {}
        is_tnauth = False

        # Extract status
        status = auth_info.get("status__name", "pending")
        identifier_info["status"] = status

        # Extract identifier
        if "type" in auth_info and "value" in auth_info:
            identifier_info["identifier"] = {
                "type": auth_info["type"],
                "value": auth_info["value"],
            }

            # Check for TNAuthList
            if auth_info["type"] == "TNAuthList":
                is_tnauth = True

            # Handle wildcard domains
            if auth_info["value"].startswith("*."):
                self.logger.debug("Adding wildcard flag to authorization")
                identifier_info["identifier"]["value"] = auth_info["value"][2:]
                identifier_info["wildcard"] = True

        return identifier_info, is_tnauth

    def extract_identifier_info_for_challenge(
        self, authz_info_dict: Dict[str, str]
    ) -> Tuple[str, str, bool]:
        """Extract identifier type/value and wildcard marker for challenge operations."""
        self.logger.debug(
            "AuthorizationBusinessLogic.extract_identifier_info_for_challenge()"
        )

        is_wildcard = bool(authz_info_dict.get("wildcard"))

        if "identifier" not in authz_info_dict:
            return None, None, is_wildcard

        identifier = authz_info_dict["identifier"]
        id_type = identifier.get("type")
        id_value = identifier.get("value")

        return id_type, id_value, is_wildcard

    def is_authorization_eligible_for_expiry(self, auth_record: Dict[str, str]) -> bool:
        """Check if authorization should be expired"""
        self.logger.debug(
            "AuthorizationBusinessLogic.is_authorization_eligible_for_expiry()"
        )

        # Must have name and status
        if "name" not in auth_record or "status__name" not in auth_record:
            return False

        # Skip if already expired
        if auth_record["status__name"] == "expired":
            return False

        # Skip corner cases where expiry is set to 0
        if "expires" in auth_record and auth_record["expires"] == 0:
            return False

        return True


class ChallengeSetManager:
    """Manager for challenge set operations"""

    def __init__(self, debug: bool, server_name: str, logger):
        self.debug = debug
        self.server_name = server_name
        self.logger = logger

    def get_challenge_set_for_authorization(
        self,
        authz_name: str,
        status: str,
        token: str,
        is_tnauth: bool,
        expires: int,
        id_type: str = None,
        id_value: str = None,
        is_wildcard: bool = False,
    ) -> List[Dict[str, str]]:
        """Get challenge set for authorization"""
        self.logger.debug(
            "ChallengeSetManager.get_challenge_set_for_authorization(%s)", authz_name
        )

        with Challenge(
            debug=self.debug,
            srv_name=self.server_name,
            logger=self.logger,
            expiry=expires,
        ) as challenge:
            return challenge.challengeset_get(
                authz_name,
                status,
                token,
                is_tnauth,
                id_type,
                id_value,
                is_wildcard,
            )


class Authorization(object):
    """Refactored Authorization class with clear separation of concerns"""

    def __init__(
        self, debug: bool = False, srv_name: str = None, logger: object = None
    ):
        self.server_name = srv_name
        self.debug = debug
        self.logger = logger

        # Initialize dependencies
        self.dbstore = DBstore(debug, self.logger)
        self.message = Message(debug, self.server_name, self.logger)
        self.nonce = Nonce(debug, self.logger)

        # Initialize components immediately
        self.config = AuthorizationConfiguration()
        self.repository = AuthorizationRepository(self.dbstore, self.logger)
        self.business_logic = AuthorizationBusinessLogic(
            self.config, self.repository, self.logger
        )
        self.challenge_manager = ChallengeSetManager(
            self.debug, self.server_name, self.logger
        )

    def __enter__(self):
        """Makes Authorization a Context Manager"""
        self._load_configuration()
        # Re-initialize business logic with updated config
        self.business_logic = AuthorizationBusinessLogic(
            self.config, self.repository, self.logger
        )
        return self

    def __exit__(self, *args):
        """Close the connection at the end of the context"""
        # pylint: disable=unnecessary-pass
        pass

    def _load_json_config_param(
        self, config_dic, parameter_name: str
    ) -> Optional[List[str]]:
        """Helper method to load JSON config parameters"""
        self.logger.debug(
            f"Authorization._load_json_config_param() - Loading parameter: {parameter_name}"
        )
        value = None
        try:
            # load  parameter as JSON, if it exists. If it doesn't exist, fallback to None
            value = json.loads(
                config_dic.get("Authorization", parameter_name, fallback="null")
            )
            if value:
                self.logger.warning(
                    f"{parameter_name} loaded globally. Such configuration is NOT recommended as this is a severe security risk!"
                )
        except json.JSONDecodeError as err:
            value = None
            raise ConfigurationError(f"Invalid {parameter_name} parameter") from err

        self.logger.debug(
            f"Authorization._load_json_config_param() - Loaded {parameter_name} ended with : {bool(value)}"
        )
        return value

    def _load_configuration(self):
        """Load configuration from file"""
        self.logger.debug("Authorization._load_configuration()")

        config_dic = load_config()

        if config_dic:

            try:
                self.config.validity = int(
                    config_dic.get("Authorization", "validity", fallback=86400)
                )
            except ValueError as err:
                raise ConfigurationError(
                    f"Invalid validity parameter: {config_dic.get('Authorization', 'validity')}"
                ) from err

            self.config.expiry_check_disable = config_dic.getboolean(
                "Authorization", "expiry_check_disable", fallback=False
            )
            self.config.email_identifier_rewrite = config_dic.getboolean(
                "Order", "email_identifier_rewrite", fallback=False
            )
            self.config.dns_persist_01_support = config_dic.getboolean(
                "Challenge", "dns_persist_01_support", fallback=False
            )
            self.config.dns_persist_allow_policy_wildcard = config_dic.getboolean(
                "Challenge", "dns_persist_allow_policy_wildcard", fallback=False
            )
            self.config.dns_persist_jit_validation = config_dic.getboolean(
                "Challenge", "dns_persist_jit_validation", fallback=False
            )
            self.config.dns_server_list, self.config.dns_validation_pause_timer = (
                config_dns_server_list_load(self.logger, config_dic)
            )

            # Load caaidentities from Directory section as JSON array or comma-separated string
            caaidentities_raw = config_dic.get(
                "Directory", "caaidentities", fallback=None
            )
            caaidentities: Optional[List[str]] = None

            if caaidentities_raw:
                try:
                    parsed = json.loads(caaidentities_raw)
                    if isinstance(parsed, list):
                        caaidentities = parsed
                    else:
                        self.logger.warning(
                            "Failed to parse caaidentities from configuration, expected JSON array. Got: %s",
                            caaidentities_raw,
                        )
                        caaidentities = [caaidentities_raw]
                except Exception:
                    # fallback: try comma-separated string
                    caaidentities = [
                        x.strip() for x in caaidentities_raw.split(",") if x.strip()
                    ]
            self.config.caaidentities = caaidentities

            url_prefix = config_dic.get("Directory", "url_prefix", fallback=None)
            if url_prefix:
                self.config.authz_path = f"{url_prefix}{self.config.authz_path}"

            self.config.prevalidated_domainlist = self._load_json_config_param(
                config_dic, "prevalidated_domainlist"
            )
            self.config.prevalidated_iplist = self._load_json_config_param(
                config_dic, "prevalidated_iplist"
            )
            self.config.prevalidated_emaillist = self._load_json_config_param(
                config_dic, "prevalidated_emaillist"
            )
            # load profiling
            (
                self.config.eab_profiling,
                self.config.eab_handler,
            ) = config_eab_profile_load(self.logger, config_dic)

        self.logger.debug("Authorization._load_configuration() ended:")

    def _build_jit_validation_context(
        self,
        authz_name: str,
        token: str,
        id_value: Optional[str],
        auth_details: Optional[Dict[str, str]],
    ) -> ChallengeContext:
        """Build context object used by the dns-persist JIT validator."""
        account_name = (
            auth_details.get("order__account__name") if auth_details else None
        )

        acct_path = self.config.authz_path.replace("/acme/authz/", "/acme/acct/")
        accounturi = (
            f"{self.server_name}{acct_path}{account_name}" if account_name else None
        )

        caaidentities = getattr(self.config, "caaidentities", None)

        return ChallengeContext(
            challenge_name=authz_name,
            token=token,
            jwk_thumbprint=None,
            authorization_type="dns",
            authorization_value=id_value,
            options={
                "accounturi": accounturi,
                "issuer_domain_names": caaidentities or [],
                "allow_policy_wildcard": getattr(
                    self.config, "dns_persist_allow_policy_wildcard", False
                ),
            },
            dns_servers=self.config.dns_server_list,
            proxy_servers=None,
            timeout=10,
        )

    def _run_jit_dns_validation(
        self,
        authz_name: str,
        token: str,
        id_value: Optional[str],
        auth_details: Optional[Dict[str, str]],
    ) -> bool:
        """Run dns-persist JIT validation and return True on successful validation."""
        try:
            validator_context = self._build_jit_validation_context(
                authz_name, token, id_value, auth_details
            )
            self.logger.debug("JIT validator context: %s", validator_context)

            validator = DnsPersistChallengeValidator(self.logger)
            result = validator.perform_validation(validator_context)
            self.logger.debug("JIT validator result: %s", result)

            if result.success and not result.invalid:
                self.logger.debug(
                    "Authorization.get_authorization_details(): JIT dns-persist-01 validation result: %s",
                    result,
                )
                return True

        except Exception as err:
            self.logger.error("JIT dns-persist-01 validation failed: %s", err)

        return False

    def _apply_jit_validation_success(
        self,
        authz_name: str,
        auth_details: Optional[Dict[str, str]],
        authz_info: Dict[str, Any],
    ) -> None:
        """Apply state updates when JIT validation succeeds."""
        authz_info["status"] = "valid"
        self.repository.mark_authorization_as_valid(authz_name)
        if auth_details is not None:
            self.repository.mark_order_as_ready(auth_details.get("order__name"))
        authz_info["challenges"] = []
        self.logger.info("JIT dns-persist-01 validation succeeded for %s", authz_name)

    def get_authorization_details(self, url: str) -> Optional[Dict[str, str]]:
        """Get detailed authorization information"""
        self.logger.debug("Authorization.get_authorization_details()")

        # Extract authorization name from URL
        authz_name = self.business_logic.extract_authorization_name_from_url(
            url, self.server_name
        )
        self.logger.debug("Authorization name: %s", authz_name)

        # Check if authorization exists
        authz = self.repository.find_authorization_by_name(
            authz_name, ["name", "token", "expires"]
        )
        if not authz:
            self.logger.debug("Authorization not found: %s", authz_name)
            return {}

        token, expires, persist_token = (
            self.business_logic.resolve_authorization_token_and_expiry(authz)
        )
        if persist_token:
            self.repository.update_authorization_expiry(authz_name, token, expires)

        # Create base authorization info
        authz_info = {
            "expires": uts_to_date_utc(expires),
        }

        # Get detailed authorization information
        auth_details = self.repository.find_authorization_by_name(
            authz_name,
            [
                "status__name",
                "type",
                "value",
                "order__name",
                "order__account__name",
                "order__account__eab_kid",
            ],
        )

        if auth_details:
            (
                identifier_info,
                is_tnauth,
            ) = self.business_logic.enrich_authorization_with_identifier_info(
                auth_details
            )
            authz_info.update(identifier_info)
        else:
            authz_info["status"] = "pending"
            is_tnauth = False

        # Extract identifier type and value
        id_type, id_value, is_wildcard = (
            self.business_logic.extract_identifier_info_for_challenge(authz_info)
        )

        # JIT dns-persist-01 validation if enabled and applicable
        jit_valid = False
        if (
            id_type == "dns"
            and getattr(self.config, "dns_persist_01_support", False)
            and getattr(self.config, "dns_persist_jit_validation", False)
        ):
            jit_valid = self._run_jit_dns_validation(
                authz_name, token, id_value, auth_details
            )

        if jit_valid:
            self._apply_jit_validation_success(authz_name, auth_details, authz_info)
        else:
            if auth_details:
                # Apply EAB profile and prevalidation whitelist logic
                self._apply_eab_and_prevalidation_whitelist(
                    authz_name, auth_details, id_type, id_value, authz_info
                )
            # Get challenge set
            try:
                authz_info["challenges"] = (
                    self.challenge_manager.get_challenge_set_for_authorization(
                        authz_name,
                        authz_info["status"],
                        token,
                        is_tnauth,
                        expires,
                        id_type,
                        id_value,
                        is_wildcard,
                    )
                )
            except Exception as err:
                self.logger.error(
                    "Failed to create challenge set for authorization %s: %s",
                    authz_name,
                    err,
                )
                return None

        self.logger.debug(
            "Authorization.get_authorization_details() returns: %s",
            json.dumps(authz_info),
        )
        return authz_info

    def _apply_eab_and_prevalidation_whitelist(
        self, authz_name, auth_details, id_type, id_value, authz_info
    ):
        """Apply EAB profile settings and prevalidation whitelist logic to authorization info."""
        self._apply_eab_profile(authz_name, auth_details)
        self._apply_prevalidation_whitelist(
            authz_name, auth_details, id_type, id_value, authz_info
        )

    def _apply_eab_profile(self, authz_name, auth_details):
        if not self.config.eab_profiling:
            return
        self.logger.debug(
            "Authorization._apply_eab_and_domain_whitelist() - apply eab profile setting"
        )
        eab_kid = auth_details.get("order__account__eab_kid") if auth_details else None
        if not eab_kid:
            return
        try:
            with self.config.eab_handler(self.logger) as eab_handler:
                profile_dic = eab_handler.key_file_load()
                for key, attr in [
                    ("prevalidated_domainlist", "prevalidated_domainlist"),
                    ("prevalidated_iplist", "prevalidated_iplist"),
                    ("prevalidated_emaillist", "prevalidated_emaillist"),
                ]:
                    value = (
                        profile_dic.get(eab_kid, {}).get("authorization", {}).get(key)
                    )
                    if value:
                        self.logger.debug(
                            f"Authorization._apply_eab_and_domain_whitelist() - apply {key} from eab profile."
                        )
                        setattr(self.config, attr, value)
        except Exception as err:
            self.logger.error(
                "Failed to process EAB profile for challenge %s (kid: %s): %s",
                authz_name,
                eab_kid,
                err,
            )

    def _apply_prevalidation_whitelist(
        self, authz_name, auth_details, id_type, id_value, authz_info
    ):
        self.logger.debug(
            "Checking prevalidation whitelist for identifier type: %s, value: %s",
            id_type,
            id_value,
        )
        if not id_value:
            return
        if (
            (id_type == "dns" and self.config.email_identifier_rewrite)
            or id_type == "email"
        ) and "@" in id_value:
            self._handle_email_prevalidation(
                authz_name, auth_details, id_value, authz_info
            )
        elif id_type == "dns":
            self._handle_domain_prevalidation(
                authz_name, auth_details, id_value, authz_info
            )
        elif id_type == "ip":
            self._handle_ip_prevalidation(
                authz_name, auth_details, id_value, authz_info
            )

    def _handle_email_prevalidation(
        self, authz_name, auth_details, id_value, authz_info
    ):
        """Handle email identifier prevalidation based on whitelist configuration. If the email is whitelisted, mark the authorization as valid and the order as ready."""
        self.logger.debug(
            "Authorization._handle_email_prevalidation() - Checking email identifier rewrite setting for email prevalidation"
        )
        emaillist = getattr(self.config, "prevalidated_emaillist", None)
        if not emaillist:
            return
        self.logger.debug(
            "Authorization.get_authorization_details() - Checking preauthorized email list for email identifier"
        )
        if is_email_whitelisted(self.logger, id_value, emaillist):
            self.logger.debug(
                "Email %s is preauthorized, setting authorization status to 'valid'",
                id_value,
            )
            authz_info["status"] = "valid"
            self.repository.mark_authorization_as_valid(authz_name)
            if auth_details is not None:
                self.repository.mark_order_as_ready(auth_details.get("order__name"))
            else:
                self.logger.debug(NO_ORDER_INFO_LOG, authz_name)

    def _handle_domain_prevalidation(
        self, authz_name, auth_details, id_value, authz_info
    ):
        """Handle domain identifier prevalidation based on whitelist configuration. If the domain is whitelisted, mark the authorization as valid and the order as ready."""
        self.logger.debug(
            "Authorization._handle_domain_prevalidation() - Checking email identifier rewrite setting for domain prevalidation"
        )
        domainlist = getattr(self.config, "prevalidated_domainlist", None)
        if not domainlist:
            return

        wildcard_all = (
            len(self.config.prevalidated_domainlist) == 1
            and isinstance(self.config.prevalidated_domainlist[0], str)
            and self.config.prevalidated_domainlist[0].strip() == "*"
        )

        if wildcard_all:
            self.logger.warning(
                "Global wildcard prevalidation is active (prevalidated_domainlist contains '*'). Marking authorization %s as valid without challenge validation.",
                authz_name,
            )
            authz_info["status"] = "valid"
            self.repository.mark_authorization_as_valid(authz_name)
            if auth_details is not None:
                self.repository.mark_order_as_ready(auth_details.get("order__name"))
            else:
                self.logger.debug(
                    "No order information found for authorization %s", authz_name
                )
            return

        self.logger.debug(
            "Authorization._handle_domain_prevalidation() - Evaluating domain whitelist match for id_value='%s' (wildcard flag: %s)",
            id_value,
            bool(authz_info.get("wildcard")),
        )

        is_whitelisted = is_domain_whitelisted(self.logger, id_value, domainlist)
        self.logger.debug(
            "Authorization._handle_domain_prevalidation() - Whitelist check result for id_value='%s': %s",
            id_value,
            is_whitelisted,
        )

        # Wildcard identifiers are normalized earlier ('*.example.org' -> 'example.org').
        # Reconstruct wildcard form for whitelist checks like '*.example.org'.
        if (
            not is_whitelisted
            and authz_info.get("wildcard")
            and id_value
            and not id_value.startswith("*.")
        ):
            wildcard_value = f"*.{id_value}"
            self.logger.debug(
                "Authorization._handle_domain_prevalidation() - Reconstructed wildcard id_value for whitelist check: '%s' -> '%s'",
                id_value,
                wildcard_value,
            )
            is_whitelisted = is_domain_whitelisted(
                self.logger, wildcard_value, domainlist
            )
            self.logger.debug(
                "Authorization._handle_domain_prevalidation() - Whitelist check result for reconstructed wildcard id_value='%s': %s",
                wildcard_value,
                is_whitelisted,
            )
        else:
            self.logger.debug(
                "Authorization._handle_domain_prevalidation() - Skipping reconstructed wildcard whitelist check (is_whitelisted=%s, wildcard=%s, id_value_present=%s, id_value_has_wildcard_prefix=%s)",
                is_whitelisted,
                bool(authz_info.get("wildcard")),
                bool(id_value),
                bool(id_value and id_value.startswith("*.")),
            )

        if is_whitelisted:
            self.logger.debug(
                "Domain %s is preauthorized, setting authorization status to 'valid'",
                id_value,
            )
            authz_info["status"] = "valid"
            self.repository.mark_authorization_as_valid(authz_name)
            if auth_details is not None:
                self.repository.mark_order_as_ready(auth_details.get("order__name"))
            else:
                self.logger.debug(NO_ORDER_INFO_LOG, authz_name)

    def _handle_ip_prevalidation(self, authz_name, auth_details, id_value, authz_info):
        """Handle IP identifier prevalidation based on whitelist configuration. If the IP is whitelisted, mark the authorization as valid and the order as ready."""
        self.logger.debug(
            "Authorization._handle_ip_prevalidation() - Checking email identifier rewrite setting for IP prevalidation"
        )
        iplist = getattr(self.config, "prevalidated_iplist", None)
        if not iplist:
            return
        self.logger.debug(
            "Authorization._handle_ip_prevalidation() - Checking preauthorized IP list for IP identifier"
        )
        if is_ip_whitelisted(self.logger, id_value, iplist):
            self.logger.debug(
                "IP %s is preauthorized, setting authorization status to 'valid'",
                id_value,
            )
            authz_info["status"] = "valid"
            self.repository.mark_authorization_as_valid(authz_name)
            if auth_details is not None:
                self.repository.mark_order_as_ready(auth_details.get("order__name"))
            else:
                self.logger.debug(NO_ORDER_INFO_LOG, authz_name)
        else:
            self.logger.debug(
                "IP %s is not preauthorized, leaving authorization status unchanged",
                id_value,
            )

    def expire_invalid_authorizations(
        self, timestamp: int = None
    ) -> Tuple[List[str], List[str]]:
        """Expire invalid authorizations"""
        self.logger.debug("Authorization.expire_invalid_authorizations(%s)", timestamp)

        if timestamp is None:
            timestamp = uts_now()
            self.logger.debug("Set timestamp to current time: %s", timestamp)

        field_list = [
            "id",
            "name",
            "expires",
            "value",
            "created_at",
            "token",
            "status__id",
            "status__name",
            "order__id",
            "order__name",
        ]

        try:
            # Search for expired authorizations
            expired_authz_list = self.repository.search_expired_authorizations(
                timestamp, field_list
            )
        except AuthorizationError as err:
            self.logger.warning("Failed to search for expired authorizations: %s", err)
            return [], []

        # Process expired authorizations
        expired_output = []
        for authz_record in expired_authz_list:
            try:
                if self.business_logic.is_authorization_eligible_for_expiry(
                    authz_record
                ):
                    expired_output.append(authz_record)
                    self.repository.mark_authorization_as_expired(authz_record["name"])
            except AuthorizationError as err:
                self.logger.warning(
                    "Failed to expire authorization %s: %s",
                    authz_record.get("name"),
                    err,
                )
                # Continue processing other authorizations
                continue

        self.logger.debug(
            "Authorization.expire_invalid_authorizations() ended: %s authorizations expired",
            len(expired_output),
        )
        return field_list, expired_output

    def handle_get_request(self, url: str) -> Dict[str, str]:
        """Handle GET request for authorization"""
        self.logger.debug("Authorization.handle_get_request()")

        try:
            authorization_data = self.get_authorization_details(url)
            if authorization_data:
                return {"code": 200, "header": {}, "data": authorization_data}
            else:
                return {
                    "code": 404,
                    "header": {},
                    "data": {"error": "Authorization not found"},
                }
        except AuthorizationError as err:
            self.logger.error("Authorization error: %s", err)
            return {"code": 404, "header": {}, "data": {"error": str(err)}}

    def handle_post_request(self, content: str) -> Dict[str, str]:
        """Handle POST request for authorization"""
        self.logger.debug("Authorization.handle_post_request()")

        # Expire invalid authorizations if not disabled
        if not self.config.expiry_check_disable:
            try:
                self.invalidate()  # Call public method for backward compatibility
            except Exception as err:
                self.logger.warning("Failed to expire authorizations: %s", err)
                # Continue with processing - don't fail the request

        # Validate message
        code, message, detail, protected, _payload, _account_name = self.message.check(
            content
        )

        response_dic = {}
        if code == 200:
            if "url" not in protected:
                code = 400
                message = "urn:ietf:params:acme:error:malformed"
                detail = "url is missing in protected"
            else:
                try:
                    auth_info = self.get_authorization_details(protected["url"])
                    if auth_info:
                        response_dic["data"] = auth_info
                    else:
                        code = 403
                        message = "urn:ietf:params:acme:error:unauthorized"
                        detail = "authorization lookup failed"
                except AuthorizationError as err:
                    self.logger.error("Authorization error: %s", err)
                    code = 403
                    message = "urn:ietf:params:acme:error:unauthorized"
                    detail = "authorization error"

        # Prepare response
        status_dic = {"code": code, "type": message, "detail": detail}
        response_dic = self.message.prepare_response(response_dic, status_dic)

        self.logger.debug(
            "Authorization.handle_post_request() returns: %s", json.dumps(response_dic)
        )
        return response_dic

    # Backward compatibility methods (delegating to new methods)
    def new_get(self, url: str) -> Dict[str, str]:
        """Backward compatibility: handle GET request"""
        self.logger.debug("Authorization.new_get()")
        return self.handle_get_request(url)

    def new_post(self, content: str) -> Dict[str, str]:
        """Backward compatibility: handle POST request"""
        self.logger.debug("Authorization.new_post()")
        return self.handle_post_request(content)

    def invalidate(self, timestamp: int = None) -> Tuple[List[str], List[str]]:
        """Backward compatibility: expire invalid authorizations"""
        self.logger.debug("Authorization.invalidate()")
        return self.expire_invalid_authorizations(timestamp)
