"""Adds config flow for Mail and Packages."""

import contextlib
import logging
import ssl
from collections.abc import Mapping
from pathlib import Path
from typing import Any

import homeassistant.helpers.config_validation as cv
import voluptuous as vol
from aioimaplib import AioImapException
from homeassistant import config_entries
from homeassistant.config_entries import ConfigFlowResult
from homeassistant.const import (
    CONF_HOST,
    CONF_PASSWORD,
    CONF_PORT,
    CONF_RESOURCES,
    CONF_USERNAME,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_entry_oauth2_flow, selector

from .const import (
    AUTH_TYPE_OAUTH_GOOGLE,
    AUTH_TYPE_OAUTH_MICROSOFT,
    AUTH_TYPE_PASSWORD,
    CONF_ALLOW_EXTERNAL,
    CONF_ALLOW_FORWARDED_EMAILS,
    CONF_AMAZON_CUSTOM_IMG,
    CONF_AMAZON_CUSTOM_IMG_FILE,
    CONF_AMAZON_DAYS,
    CONF_AMAZON_DOMAIN,
    CONF_AMAZON_FWDS,
    CONF_AUTH_TYPE,
    CONF_CUSTOM_DAYS,
    CONF_CUSTOM_IMG,
    CONF_CUSTOM_IMG_FILE,
    CONF_DURATION,
    CONF_FEDEX_CUSTOM_IMG,
    CONF_FEDEX_CUSTOM_IMG_FILE,
    CONF_FOLDER,
    CONF_FORWARDED_EMAILS,
    CONF_FORWARDING_HEADER,
    CONF_GENERATE_GRID,
    CONF_GENERATE_MP4,
    CONF_GENERIC_CUSTOM_IMG,
    CONF_GENERIC_CUSTOM_IMG_FILE,
    CONF_IMAGE_SECURITY,
    CONF_IMAP_SECURITY,
    CONF_IMAP_TIMEOUT,
    CONF_PATH,
    CONF_POST_DE_CUSTOM_IMG,
    CONF_POST_DE_CUSTOM_IMG_FILE,
    CONF_SCAN_INTERVAL,
    CONF_STORAGE,
    CONF_UPS_CUSTOM_IMG,
    CONF_UPS_CUSTOM_IMG_FILE,
    CONF_USPS_PLACEHOLDER,
    CONF_VERIFY_SSL,
    CONF_WALMART_CUSTOM_IMG,
    CONF_WALMART_CUSTOM_IMG_FILE,
    CONFIG_VER,
    DEFAULT_ALLOW_EXTERNAL,
    DEFAULT_ALLOW_FORWARDED_EMAILS,
    DEFAULT_AMAZON_CUSTOM_IMG,
    DEFAULT_AMAZON_CUSTOM_IMG_FILE,
    DEFAULT_AMAZON_DAYS,
    DEFAULT_AMAZON_DOMAIN,
    DEFAULT_AMAZON_FWDS,
    DEFAULT_CUSTOM_DAYS,
    DEFAULT_CUSTOM_IMG,
    DEFAULT_CUSTOM_IMG_FILE,
    DEFAULT_FEDEX_CUSTOM_IMG,
    DEFAULT_FEDEX_CUSTOM_IMG_FILE,
    DEFAULT_FOLDER,
    DEFAULT_FORWARDED_EMAILS,
    DEFAULT_FORWARDING_HEADER,
    DEFAULT_GENERIC_CUSTOM_IMG,
    DEFAULT_GENERIC_CUSTOM_IMG_FILE,
    DEFAULT_GIF_DURATION,
    DEFAULT_IMAGE_SECURITY,
    DEFAULT_IMAP_TIMEOUT,
    DEFAULT_PATH,
    DEFAULT_PORT,
    DEFAULT_POST_DE_CUSTOM_IMG,
    DEFAULT_POST_DE_CUSTOM_IMG_FILE,
    DEFAULT_SCAN_INTERVAL,
    DEFAULT_STORAGE,
    DEFAULT_UPS_CUSTOM_IMG,
    DEFAULT_UPS_CUSTOM_IMG_FILE,
    DEFAULT_USPS_PLACEHOLDER,
    DEFAULT_WALMART_CUSTOM_IMG,
    DEFAULT_WALMART_CUSTOM_IMG_FILE,
    DOMAIN,
    OAUTH_IMAP_DEFAULTS,
    OAUTH_SCOPES,
)
from .helpers import get_resources
from .utils.email import generate_service_email_domains, validate_email_address
from .utils.image import _check_ffmpeg
from .utils.imap import InvalidAuth, decode_imap_utf7, login, logout

ERROR_MAILBOX_FAIL = "Problem getting mailbox listing using 'INBOX' message"
IMAP_SECURITY = ["none", "SSL"]
AMAZON_SENSORS = ["amazon_packages", "amazon_delivered", "amazon_exception"]
_LOGGER = logging.getLogger(__name__)
AMAZON_EMAIL_ERROR = (
    "Amazon domain found in email: %s, this may cause errors when searching emails."
)
FORWARDED_EMAIL_ERROR = "A service domain was found in email: %s, this may cause errors when searching emails."  # pylint: disable=line-too-long


async def _check_amazon_forwards(forwards: str, domain: str) -> tuple:
    """Validate and format amazon forward emails for user input.

    Returns tuple: dict of errors, list of email addresses
    """
    emails = forwards.split(",")
    errors = []

    # Validate each email address
    for email in emails:
        email = email.strip()

        if "@" in email:
            # Check for amazon domains
            if f"@{domain}" in email:
                _LOGGER.error(
                    AMAZON_EMAIL_ERROR,
                    email,
                )

        # No forwards
        elif forwards in ["", "(none)", '""']:
            forwards = []

        else:
            _LOGGER.error("Missing '@' in email address: %s", email)
            errors.append("invalid_email_format")

    if len(errors) == 0:
        errors.append("ok")

    return errors, forwards


async def _check_forwarded_emails(user_input: dict[str, Any]) -> list[str]:
    """Validate forwarded email addresses provided by the user.

    Use Voluptuous to make sure that none of the forwarded email addresses use domains
    that match any of the Mail service domains, as this was known to cause issues when
    searching for Amazon emails.

    Args:
        user_input (dict[str, Any]): The user input dictionary.

    Returns:
        list[str]: A list of error codes (e.g. "missing_forwarded_emails") or "ok" if the
                   email addresses are valid

    """
    forwarded_emails = user_input[CONF_FORWARDED_EMAILS]

    _LOGGER.debug("checking forwarded emails: '%s'", forwarded_emails)

    # No forwards
    if not forwarded_emails:
        _LOGGER.error(
            "Allowed forwarded emails but no forwards or '(none)' were entered.",
        )
        return ["missing_forwarded_emails"]
    if forwarded_emails == "(none)":
        return ["ok"]

    errors = []

    service_email_domains = generate_service_email_domains(
        user_input.get(CONF_AMAZON_FWDS, []),
    )

    emails = [email.strip() for email in forwarded_emails.split(",")]
    for email in emails:
        _LOGGER.debug("validating email address %s", email)
        if not validate_email_address(email):
            _LOGGER.error("%s does not look like a valid email address", email)
            errors.append("invalid_email_format")
            continue

        domain = email.split("@")[1]
        if domain in service_email_domains:
            _LOGGER.error(
                FORWARDED_EMAIL_ERROR,
                email,
            )
    if len(errors) == 0:
        return ["ok"]

    return errors


def _validate_path_input(
    user_input: dict, errors: dict, hass: HomeAssistant | None = None
) -> None:
    """Validate path and file inputs."""
    # List of (Toggle Key, File Key, Error Key)
    file_checks = [
        (CONF_CUSTOM_IMG, CONF_CUSTOM_IMG_FILE, CONF_CUSTOM_IMG_FILE),
        (
            CONF_AMAZON_CUSTOM_IMG,
            CONF_AMAZON_CUSTOM_IMG_FILE,
            CONF_AMAZON_CUSTOM_IMG_FILE,
        ),
        (CONF_UPS_CUSTOM_IMG, CONF_UPS_CUSTOM_IMG_FILE, CONF_UPS_CUSTOM_IMG_FILE),
        (
            CONF_WALMART_CUSTOM_IMG,
            CONF_WALMART_CUSTOM_IMG_FILE,
            CONF_WALMART_CUSTOM_IMG_FILE,
        ),
        (
            CONF_FEDEX_CUSTOM_IMG,
            CONF_FEDEX_CUSTOM_IMG_FILE,
            CONF_FEDEX_CUSTOM_IMG_FILE,
        ),
        (
            CONF_GENERIC_CUSTOM_IMG,
            CONF_GENERIC_CUSTOM_IMG_FILE,
            CONF_GENERIC_CUSTOM_IMG_FILE,
        ),
        (
            CONF_POST_DE_CUSTOM_IMG,
            CONF_POST_DE_CUSTOM_IMG_FILE,
            CONF_POST_DE_CUSTOM_IMG_FILE,
        ),
    ]

    for toggle, file_key, error_key in file_checks:
        if user_input.get(toggle) and file_key in user_input:
            path = user_input[file_key]
            if hass:
                path = hass.config.path(path)
            if not Path(path).is_file():
                errors[error_key] = "file_not_found"

    if CONF_STORAGE in user_input:
        path = user_input[CONF_STORAGE]
        if hass:
            path = hass.config.path(path)
        if not Path(path).exists():
            errors[CONF_STORAGE] = "path_not_found"


async def _validate_amazon_fwds(user_input: dict, errors: dict) -> None:
    """Validate amazon forwarding email addresses in user_input."""
    if CONF_AMAZON_FWDS not in user_input:
        return
    if not isinstance(user_input[CONF_AMAZON_FWDS], str):
        return
    status, amazon_list = await _check_amazon_forwards(
        user_input[CONF_AMAZON_FWDS],
        user_input[CONF_AMAZON_DOMAIN],
    )
    user_input[CONF_AMAZON_FWDS] = amazon_list
    if status[0] != "ok":
        errors[CONF_AMAZON_FWDS] = status[0]


async def _validate_forwarded_emails(user_input: dict, errors: dict) -> None:
    """Validate forwarded email address list in user_input."""
    if CONF_FORWARDED_EMAILS not in user_input:
        return
    if not isinstance(user_input[CONF_FORWARDED_EMAILS], str):
        return
    status = await _check_forwarded_emails(user_input)
    if status[0] == "ok" and user_input[CONF_FORWARDED_EMAILS] == "(none)":
        # the user changed their mind, remove the flag and config entry
        user_input[CONF_ALLOW_FORWARDED_EMAILS] = False
        del user_input[CONF_FORWARDED_EMAILS]
    elif status[0] == "ok":
        user_input[CONF_FORWARDED_EMAILS] = [
            e.strip() for e in user_input[CONF_FORWARDED_EMAILS].split(",") if e.strip()
        ]
    else:
        errors[CONF_FORWARDED_EMAILS] = status[0]


async def _validate_user_input(
    user_input: dict, hass: HomeAssistant | None = None
) -> tuple:
    """Validate user input from config flow.

    Returns tuple with error messages and modified user_input
    """
    errors = {}

    await _validate_amazon_fwds(user_input, errors)

    # Check for forwarding header mode first — it takes precedence over address list
    forwarding_header = user_input.get(CONF_FORWARDING_HEADER, "")
    if isinstance(forwarding_header, str):
        forwarding_header = forwarding_header.strip()
    if forwarding_header and forwarding_header not in ("(none)", ""):
        # Header mode: store the header name, clear address list if present
        user_input[CONF_FORWARDING_HEADER] = forwarding_header
        user_input.pop(CONF_FORWARDED_EMAILS, None)
    else:
        # No header provided — clear the key and fall through to address list validation
        user_input.pop(CONF_FORWARDING_HEADER, None)
        await _validate_forwarded_emails(user_input, errors)

    # Check for ffmpeg if option enabled
    if user_input[CONF_GENERATE_MP4]:
        if not await _check_ffmpeg():
            errors[CONF_GENERATE_MP4] = "ffmpeg_not_found"

    # Validate file paths
    _validate_path_input(user_input, errors, hass)

    # Normalize CONF_FOLDER: if it has exactly 1 folder, store as string
    if CONF_FOLDER in user_input:
        folder_val = user_input[CONF_FOLDER]
        if isinstance(folder_val, (list, set, tuple)):
            folder_list = [f for f in folder_val if isinstance(f, str) and f]
            if not folder_list:
                user_input[CONF_FOLDER] = "INBOX"
            elif len(folder_list) == 1:
                user_input[CONF_FOLDER] = folder_list[0]
            else:
                user_input[CONF_FOLDER] = folder_list
        elif not isinstance(folder_val, str) or not folder_val:
            user_input[CONF_FOLDER] = "INBOX"

    return errors, user_input


async def _get_mailboxes(
    hass: HomeAssistant,
    host: str,
    port: int,
    user: str,
    pwd: str,
    security: str,
    verify: bool,
    oauth_token: str | None = None,
) -> list:
    """Get list of mailbox folders from mail server."""
    _LOGGER.debug("Getting mailboxes, login...")
    try:
        account = await login(
            hass,
            host,
            port,
            user,
            pwd,
            security,
            verify,
            oauth_token=oauth_token,
        )

    except (TimeoutError, AioImapException, ConnectionRefusedError) as err:
        _LOGGER.error("Unable to connect: %s", err)
        return []

    _LOGGER.debug("Attempting to get mailbox list...")
    try:
        result = await account.list('""', '"*"')
        status = result.result
        folderlist = result.lines
        _LOGGER.debug("Get mailbox status: %s folder list: %s", status, folderlist)
        mailboxes = []
        if status != "OK" or not isinstance(folderlist, list):
            _LOGGER.error("Error listing mailboxes ... using default")
            mailboxes.append(DEFAULT_FOLDER)
        else:
            mailboxes = await _parse_folder_list(folderlist)
    finally:
        await logout(account)

    return mailboxes


async def _parse_folder_list(folderlist: list) -> list:
    """Parse folder list from IMAP server response."""
    mailboxes = []
    with contextlib.suppress(IndexError):
        mailboxes.extend(
            decode_imap_utf7(i.decode().split(' "/" ')[1].strip('"'))
            for i in folderlist
        )

    with contextlib.suppress(IndexError):
        mailboxes.extend(
            decode_imap_utf7(i.decode().split(' "." ')[1].strip('"'))
            for i in folderlist
        )

    if len(mailboxes) == 0:
        _LOGGER.error("Problem reading mailbox folders, using default.")
        mailboxes.append(DEFAULT_FOLDER)

    return mailboxes


def _get_schema_auth(user_input: list, default_dict: list) -> Any:
    """Get the first schema for auth type."""
    if user_input is None:
        user_input = {}

    def _get_default(key: str, fallback_default: Any = None) -> None:
        return user_input.get(key, default_dict.get(key, fallback_default))

    return vol.Schema(
        {
            vol.Required(
                CONF_AUTH_TYPE,
                default=_get_default(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD),
            ): selector.SelectSelector(
                selector.SelectSelectorConfig(
                    options=[
                        selector.SelectOptionDict(
                            value=AUTH_TYPE_PASSWORD,
                            label="Password",
                        ),
                        selector.SelectOptionDict(
                            value=AUTH_TYPE_OAUTH_MICROSOFT,
                            label="OAuth2 - Microsoft (Outlook/Exchange)",
                        ),
                        selector.SelectOptionDict(
                            value=AUTH_TYPE_OAUTH_GOOGLE,
                            label="OAuth2 - Google (Gmail)",
                        ),
                    ],
                    mode=selector.SelectSelectorMode.DROPDOWN,
                    translation_key="auth_type",
                ),
            ),
        },
    )


def _get_schema_imap(user_input: list, default_dict: list) -> Any:
    """Get the secondary schema for IMAP configuration."""
    if user_input is None:
        user_input = {}

    def _get_default(key: str, fallback_default: Any = None) -> None:
        return user_input.get(key, default_dict.get(key, fallback_default))

    # Determine if password field should be shown based on chosen auth_type
    auth_type = _get_default(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD)
    schema = {
        vol.Required(CONF_HOST, default=_get_default(CONF_HOST)): cv.string,
        vol.Required(CONF_PORT, default=_get_default(CONF_PORT, 993)): cv.port,
        vol.Required(CONF_USERNAME, default=_get_default(CONF_USERNAME)): cv.string,
    }

    if auth_type == AUTH_TYPE_PASSWORD:
        schema[vol.Required(CONF_PASSWORD, default=_get_default(CONF_PASSWORD, ""))] = (
            cv.string
        )

    schema.update(
        {
            vol.Required(
                CONF_IMAP_SECURITY,
                default=_get_default(CONF_IMAP_SECURITY),
            ): vol.In(IMAP_SECURITY),
            vol.Optional(
                CONF_VERIFY_SSL,
                default=_get_default(CONF_VERIFY_SSL, False),
            ): cv.boolean,
        },
    )

    return vol.Schema(schema)


class multi_folder_select(cv.multi_select):
    """Multi select validator that allows a single string and converts it to a list, stripping quotes."""

    def __call__(self, value: Any) -> list[Any]:
        """Validate and format the folder selection value."""
        if isinstance(value, str):
            value = [value.strip('"')]
        elif isinstance(value, (list, tuple, set)):
            value = [v.strip('"') if isinstance(v, str) else v for v in value]
        return super().__call__(value)


async def _get_schema_step_2(
    data: list,
    user_input: list,
    default_dict: list,
    hass: HomeAssistant,
) -> Any:
    """Get a schema using the default_dict as a backup."""
    if user_input is None:
        user_input = {}

    def _get_default(key: str, fallback_default: Any = None) -> None:
        """Get default value for key."""
        return user_input.get(key, default_dict.get(key, fallback_default))

    mailboxes = await _get_mailboxes(
        hass,
        data[CONF_HOST],
        data[CONF_PORT],
        data[CONF_USERNAME],
        data.get(CONF_PASSWORD, ""),
        data[CONF_IMAP_SECURITY],
        data[CONF_VERIFY_SSL],
        data.get("token", {}).get("access_token"),
    )

    default_folder = _get_default(CONF_FOLDER)
    if isinstance(default_folder, str):
        default_folder = [default_folder.strip('"')]
    elif isinstance(default_folder, (list, tuple, set)):
        default_folder = [
            f.strip('"') for f in default_folder if isinstance(f, str) and f
        ]
    else:
        default_folder = []

    # Clean up legacy folders that don't exist anymore/prevent validation failures
    default_folder = [f for f in default_folder if f in mailboxes]

    # Pre-select "INBOX" if default_folder is empty and "INBOX" is available
    if not default_folder and "INBOX" in mailboxes:
        default_folder = ["INBOX"]

    return vol.Schema(
        {
            vol.Required(CONF_FOLDER, default=default_folder): multi_folder_select(
                {m: m for m in mailboxes}
            ),
            vol.Required(
                CONF_RESOURCES,
                default=_get_default(CONF_RESOURCES),
            ): cv.multi_select(get_resources()),
            vol.Optional(
                CONF_SCAN_INTERVAL,
                default=_get_default(CONF_SCAN_INTERVAL),
            ): vol.All(vol.Coerce(int), vol.Range(min=5)),
            vol.Optional(
                CONF_CUSTOM_DAYS,
                default=_get_default(CONF_CUSTOM_DAYS, DEFAULT_CUSTOM_DAYS),
            ): vol.All(vol.Coerce(int), vol.Range(min=1)),
            vol.Optional(
                CONF_IMAP_TIMEOUT,
                default=_get_default(CONF_IMAP_TIMEOUT),
            ): vol.All(vol.Coerce(int), vol.Range(min=10)),
            vol.Optional(
                CONF_DURATION,
                default=_get_default(CONF_DURATION),
            ): vol.Coerce(int),
            vol.Optional(
                CONF_ALLOW_FORWARDED_EMAILS,
                default=_get_default(CONF_ALLOW_FORWARDED_EMAILS, False),
            ): cv.boolean,
            vol.Optional(
                CONF_GENERATE_GRID,
                default=_get_default(CONF_GENERATE_GRID, False),
            ): cv.boolean,
            vol.Optional(
                CONF_GENERATE_MP4,
                default=_get_default(CONF_GENERATE_MP4, False),
            ): cv.boolean,
            vol.Optional(
                CONF_USPS_PLACEHOLDER,
                default=_get_default(CONF_USPS_PLACEHOLDER, DEFAULT_USPS_PLACEHOLDER),
            ): cv.boolean,
            vol.Optional(
                CONF_ALLOW_EXTERNAL,
                default=_get_default(CONF_ALLOW_EXTERNAL, False),
            ): cv.boolean,
            vol.Optional(
                CONF_CUSTOM_IMG,
                default=_get_default(CONF_CUSTOM_IMG, False),
            ): cv.boolean,
            vol.Optional(
                CONF_AMAZON_CUSTOM_IMG,
                default=_get_default(CONF_AMAZON_CUSTOM_IMG, False),
            ): cv.boolean,
            vol.Optional(
                CONF_UPS_CUSTOM_IMG,
                default=_get_default(CONF_UPS_CUSTOM_IMG, False),
            ): cv.boolean,
            vol.Optional(
                CONF_WALMART_CUSTOM_IMG,
                default=_get_default(CONF_WALMART_CUSTOM_IMG, False),
            ): cv.boolean,
            vol.Optional(
                CONF_FEDEX_CUSTOM_IMG,
                default=_get_default(CONF_FEDEX_CUSTOM_IMG, False),
            ): cv.boolean,
            vol.Optional(
                CONF_GENERIC_CUSTOM_IMG,
                default=_get_default(CONF_GENERIC_CUSTOM_IMG, False),
            ): cv.boolean,
            vol.Optional(
                CONF_POST_DE_CUSTOM_IMG,
                default=_get_default(CONF_POST_DE_CUSTOM_IMG, False),
            ): cv.boolean,
        },
    )


def _get_schema_step_3(user_input: dict, default_dict: dict) -> Any:
    """Get a schema using the default_dict as a backup."""
    if user_input is None:
        user_input = {}

    def _get_default(key: str, fallback_default: Any = None) -> str:
        """Get default value for key."""
        return user_input.get(key, default_dict.get(key, fallback_default))

    schema = {}

    # Only show custom image file field if custom image is enabled
    if user_input.get(CONF_CUSTOM_IMG):
        schema[
            vol.Optional(
                CONF_CUSTOM_IMG_FILE,
                default=_get_default(CONF_CUSTOM_IMG_FILE, DEFAULT_CUSTOM_IMG_FILE),
            )
        ] = cv.string

    # Only show Amazon custom image file field if Amazon custom image is enabled
    if user_input.get(CONF_AMAZON_CUSTOM_IMG):
        schema[
            vol.Optional(
                CONF_AMAZON_CUSTOM_IMG_FILE,
                default=_get_default(
                    CONF_AMAZON_CUSTOM_IMG_FILE,
                    DEFAULT_AMAZON_CUSTOM_IMG_FILE,
                ),
            )
        ] = cv.string

    # Only show UPS custom image file field if UPS custom image is enabled
    if user_input.get(CONF_UPS_CUSTOM_IMG):
        schema[
            vol.Optional(
                CONF_UPS_CUSTOM_IMG_FILE,
                default=_get_default(
                    CONF_UPS_CUSTOM_IMG_FILE,
                    DEFAULT_UPS_CUSTOM_IMG_FILE,
                ),
            )
        ] = cv.string

    # Only show Walmart custom image file field if Walmart custom image is enabled
    if user_input.get(CONF_WALMART_CUSTOM_IMG):
        schema[
            vol.Optional(
                CONF_WALMART_CUSTOM_IMG_FILE,
                default=_get_default(
                    CONF_WALMART_CUSTOM_IMG_FILE,
                    DEFAULT_WALMART_CUSTOM_IMG_FILE,
                ),
            )
        ] = cv.string

    # Only show FedEx custom image file field if FedEx custom image is enabled
    if user_input.get(CONF_FEDEX_CUSTOM_IMG):
        schema[
            vol.Optional(
                CONF_FEDEX_CUSTOM_IMG_FILE,
                default=_get_default(
                    CONF_FEDEX_CUSTOM_IMG_FILE,
                    DEFAULT_FEDEX_CUSTOM_IMG_FILE,
                ),
            )
        ] = cv.string

    # Only show Generic custom image file field if Generic custom image is enabled
    if user_input.get(CONF_GENERIC_CUSTOM_IMG):
        schema[
            vol.Optional(
                CONF_GENERIC_CUSTOM_IMG_FILE,
                default=_get_default(
                    CONF_GENERIC_CUSTOM_IMG_FILE,
                    DEFAULT_GENERIC_CUSTOM_IMG_FILE,
                ),
            )
        ] = cv.string

    # Only show Post DE custom image file field if Post DE custom image is enabled
    if user_input.get(CONF_POST_DE_CUSTOM_IMG):
        schema[
            vol.Optional(
                CONF_POST_DE_CUSTOM_IMG_FILE,
                default=_get_default(
                    CONF_POST_DE_CUSTOM_IMG_FILE,
                    DEFAULT_POST_DE_CUSTOM_IMG_FILE,
                ),
            )
        ] = cv.string

    return vol.Schema(schema)


def _get_schema_step_amazon(
    user_input: list,
    default_dict: list,
    forwarding_header: str = "",
) -> Any:
    """Get a schema using the default_dict as a backup."""
    if user_input is None:
        user_input = {}

    def _get_default(key: str, fallback_default: Any = None) -> None:
        """Get default value for key."""
        return user_input.get(key, default_dict.get(key, fallback_default))

    schema_dict: dict = {
        vol.Required(
            CONF_AMAZON_DOMAIN,
            default=_get_default(CONF_AMAZON_DOMAIN),
        ): cv.string,
    }

    if not forwarding_header or forwarding_header == "(none)":
        schema_dict[
            vol.Optional(
                CONF_AMAZON_FWDS,
                default=_get_default(CONF_AMAZON_FWDS),
            )
        ] = cv.string

    schema_dict[
        vol.Optional(CONF_AMAZON_DAYS, default=_get_default(CONF_AMAZON_DAYS))
    ] = int

    return vol.Schema(schema_dict)


def _get_schema_step_forwarded_emails(
    user_input: list,
    default_dict: list,
) -> vol.Schema:
    """Get a schema using the default_dict as a backup."""
    if user_input is None:
        user_input = {}

    def _get_default(key: str, fallback_default: Any = None) -> list:
        """Get default value for key."""
        value = user_input.get(key, default_dict.get(key, fallback_default))
        if isinstance(value, list):
            value = ", ".join(value)
        return value

    return vol.Schema(
        {
            vol.Optional(
                CONF_FORWARDING_HEADER,
                default=_get_default(CONF_FORWARDING_HEADER, DEFAULT_FORWARDING_HEADER),
            ): cv.string,
            vol.Optional(
                CONF_FORWARDED_EMAILS,
                default=_get_default(CONF_FORWARDED_EMAILS, DEFAULT_FORWARDED_EMAILS),
            ): cv.string,
        },
    )


def _get_schema_step_storage(user_input: dict, default_dict: dict) -> Any:
    """Get a schema using the default_dict as a backup."""
    if user_input is None:
        user_input = {}

    def _get_default(key: str, fallback_default: Any = None) -> None:
        """Get default value for key."""
        return user_input.get(key, default_dict.get(key, fallback_default))

    return vol.Schema(
        {
            vol.Required(
                CONF_STORAGE,
                default=_get_default(CONF_STORAGE, DEFAULT_STORAGE),
            ): cv.string,
        },
    )


async def _validate_login(
    hass: HomeAssistant,
    user_input: dict[str, Any],
) -> dict[str, str]:
    """Validate login credentials."""
    errors = {}
    _LOGGER.debug("Testing login...")

    auth_type = user_input.get(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD)

    # Skip login validation for OAuth2 — auth happens via the OAuth flow
    if auth_type != AUTH_TYPE_PASSWORD:
        return errors

    imap_client = None
    try:
        imap_client = await login(
            hass,
            host=user_input[CONF_HOST],
            port=user_input[CONF_PORT],
            user=user_input[CONF_USERNAME],
            pwd=user_input[CONF_PASSWORD],
            security=user_input[CONF_IMAP_SECURITY],
            verify=user_input[CONF_VERIFY_SSL],
        )
        result, data = await imap_client.select()

    except InvalidAuth:
        errors[CONF_USERNAME] = errors[CONF_PASSWORD] = "invalid_auth"
    except ssl.SSLError:
        errors["base"] = "ssl_error"
    except (TimeoutError, AioImapException, ConnectionRefusedError) as err:
        _LOGGER.error("Unable to connect: %s", err)
        errors["base"] = "cannot_connect"
    else:
        if result != "OK":
            errors["base"] = "missing_inbox"
    finally:
        if imap_client is not None:
            await logout(imap_client)

    return errors


@config_entries.HANDLERS.register(DOMAIN)
class MailAndPackagesFlowHandler(
    config_entry_oauth2_flow.AbstractOAuth2FlowHandler,
    domain=DOMAIN,
):
    """Config flow for Mail and Packages."""

    VERSION = CONFIG_VER
    CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
    DOMAIN = DOMAIN

    @property
    def logger(self) -> logging.Logger:
        """Return logger."""
        return _LOGGER

    @property
    def extra_authorize_data(self) -> dict:
        """Extra data that needs to be appended to the authorize url."""
        auth_type = self._data.get(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD)
        scopes = OAUTH_SCOPES.get(auth_type, "")
        data = {"scope": scopes}
        if auth_type == AUTH_TYPE_OAUTH_GOOGLE:
            data.update(
                {
                    "access_type": "offline",
                    "prompt": "consent",
                }
            )
        return data

    def __init__(self):
        """Initialize."""
        super().__init__()
        self._entry = None
        self._data = {}
        self._errors = {}

    async def async_step_user(self, user_input=None):
        """Handle a flow initialized by the user."""
        self._errors = {}

        if user_input is not None:
            self._data.update(user_input)
            return await self.async_step_imap_config()

        return await self._show_auth_form(user_input)

    async def async_step_reauth(
        self, entry_data: Mapping[str, Any]
    ) -> ConfigFlowResult:
        """Handle re-authentication."""
        self._entry = self.hass.config_entries.async_get_entry(self.context["entry_id"])
        self._data.update(entry_data)
        return await self.async_step_reauth_confirm()

    async def async_step_reauth_confirm(
        self, user_input: dict[str, Any] | None = None
    ) -> ConfigFlowResult:
        """Confirm re-authentication."""
        self._errors = {}
        auth_type = self._data.get(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD)

        if user_input is not None:
            self._data.update(user_input)
            if auth_type != AUTH_TYPE_PASSWORD:
                return await self.async_step_pick_implementation()

            self._errors = await _validate_login(self.hass, self._data)
            if not self._errors:
                return self.async_update_reload_and_abort(
                    self._entry,
                    data=self._data,
                )

        if auth_type != AUTH_TYPE_PASSWORD:
            return self.async_show_form(step_id="reauth_confirm")

        schema = vol.Schema(
            {
                vol.Required(CONF_USERNAME, default=self._data.get(CONF_USERNAME)): str,
                vol.Required(CONF_PASSWORD): str,
            }
        )
        return self.async_show_form(
            step_id="reauth_confirm", data_schema=schema, errors=self._errors
        )

    async def async_step_imap_config(self, user_input=None):
        """Handle IMAP config step after auth selection."""
        self._errors = {}

        if user_input is not None:
            self._data.update(user_input)
            auth_type = self._data.get(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD)

            if auth_type != AUTH_TYPE_PASSWORD:
                # OAuth2 flow: store provider and auto-fill IMAP defaults
                self._data[CONF_VERIFY_SSL] = True
                self.hass.data.setdefault(DOMAIN, {})
                self.hass.data[DOMAIN]["oauth_provider"] = auth_type
                return await self.async_step_pick_implementation()

            # Password flow: validate login
            self._errors = await _validate_login(
                self.hass,
                self._data,
            )
            if self._errors == {}:
                return await self.async_step_config_2()

            return await self._show_imap_form(user_input)

        return await self._show_imap_form(user_input)

    async def async_oauth_create_entry(
        self,
        data: dict,
    ) -> config_entries.ConfigFlowResult:
        """Handle OAuth2 completion — store token and continue to step 2."""
        self._data.update(data)
        if self._entry:
            if self.source == config_entries.SOURCE_REAUTH:
                return self.async_update_reload_and_abort(
                    self._entry,
                    data=self._data,
                )
            return await self.async_step_reconfig_2()
        return await self.async_step_config_2()

    async def _show_auth_form(self, user_input):
        """Show the authentication form."""
        defaults = {CONF_AUTH_TYPE: AUTH_TYPE_PASSWORD}
        return self.async_show_form(
            step_id="user",
            data_schema=_get_schema_auth(user_input, defaults),
            errors=self._errors,
        )

    async def _show_imap_form(self, user_input):
        """Show the configuration form to edit IMAP configuration data."""
        auth_type = self._data.get(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD)
        defaults = OAUTH_IMAP_DEFAULTS.get(
            auth_type,
            {
                CONF_PORT: DEFAULT_PORT,
                CONF_IMAP_SECURITY: "SSL",
                CONF_VERIFY_SSL: False,
            },
        )
        defaults[CONF_AUTH_TYPE] = auth_type

        return self.async_show_form(
            step_id="imap_config",
            data_schema=_get_schema_imap(user_input, defaults),
            errors=self._errors,
        )

    async def async_step_config_2(self, user_input=None):
        """Configure form step 2."""
        self._errors = {}
        if user_input is not None:
            self._errors, user_input = await _validate_user_input(user_input, self.hass)
            self._data.update(user_input)
            _LOGGER.debug("RESOURCES: %s", self._data[CONF_RESOURCES])
            if len(self._errors) == 0:
                if self._data[CONF_ALLOW_FORWARDED_EMAILS]:
                    return await self.async_step_config_forwarded_emails()
                if any(
                    sensor in self._data[CONF_RESOURCES] for sensor in AMAZON_SENSORS
                ):
                    return await self.async_step_config_amazon()
                has_custom_image = (
                    self._data.get(CONF_CUSTOM_IMG)
                    or self._data.get(CONF_AMAZON_CUSTOM_IMG)
                    or self._data.get(CONF_UPS_CUSTOM_IMG)
                    or self._data.get(CONF_WALMART_CUSTOM_IMG)
                    or self._data.get(CONF_FEDEX_CUSTOM_IMG)
                    or self._data.get(CONF_GENERIC_CUSTOM_IMG)
                    or self._data.get(CONF_POST_DE_CUSTOM_IMG)
                )
                if has_custom_image:
                    return await self.async_step_config_3()

                return self.async_create_entry(
                    title=f"Mail and Packages ({self._data[CONF_HOST]})",
                    data=self._data,
                )
            return await self._show_config_2(user_input)

        return await self._show_config_2(user_input)

    async def _show_config_2(self, user_input):
        """Step 2 setup."""
        # Defaults
        defaults = {
            CONF_FOLDER: DEFAULT_FOLDER,
            CONF_SCAN_INTERVAL: DEFAULT_SCAN_INTERVAL,
            CONF_CUSTOM_DAYS: DEFAULT_CUSTOM_DAYS,
            CONF_PATH: self.hass.config.path(DEFAULT_PATH),
            CONF_DURATION: DEFAULT_GIF_DURATION,
            CONF_IMAGE_SECURITY: DEFAULT_IMAGE_SECURITY,
            CONF_IMAP_TIMEOUT: DEFAULT_IMAP_TIMEOUT,
            CONF_GENERATE_GRID: False,
            CONF_GENERATE_MP4: False,
            CONF_USPS_PLACEHOLDER: DEFAULT_USPS_PLACEHOLDER,
            CONF_ALLOW_EXTERNAL: DEFAULT_ALLOW_EXTERNAL,
            CONF_CUSTOM_IMG: DEFAULT_CUSTOM_IMG,
            CONF_AMAZON_CUSTOM_IMG: DEFAULT_AMAZON_CUSTOM_IMG,
            CONF_UPS_CUSTOM_IMG: DEFAULT_UPS_CUSTOM_IMG,
            CONF_WALMART_CUSTOM_IMG: DEFAULT_WALMART_CUSTOM_IMG,
            CONF_FEDEX_CUSTOM_IMG: DEFAULT_FEDEX_CUSTOM_IMG,
            CONF_GENERIC_CUSTOM_IMG: DEFAULT_GENERIC_CUSTOM_IMG,
            CONF_POST_DE_CUSTOM_IMG: DEFAULT_POST_DE_CUSTOM_IMG,
            CONF_ALLOW_FORWARDED_EMAILS: DEFAULT_ALLOW_FORWARDED_EMAILS,
        }

        return self.async_show_form(
            step_id="config_2",
            data_schema=await _get_schema_step_2(
                self._data,
                user_input,
                defaults,
                self.hass,
            ),
            errors=self._errors,
        )

    async def async_step_config_3(self, user_input=None):
        """Configure form step 2."""
        self._errors = {}
        if user_input is not None:
            self._data.update(user_input)
            self._errors, user_input = await _validate_user_input(self._data, self.hass)
            if len(self._errors) == 0:
                return await self.async_step_config_storage()
            return await self._show_config_3(user_input)

        return await self._show_config_3(user_input)

    async def _show_config_3(self, user_input=None):  # pylint: disable=unused-argument
        """Step 3 setup."""
        # Defaults
        defaults = {
            CONF_CUSTOM_IMG_FILE: DEFAULT_CUSTOM_IMG_FILE,
            CONF_AMAZON_CUSTOM_IMG_FILE: DEFAULT_AMAZON_CUSTOM_IMG_FILE,
            CONF_UPS_CUSTOM_IMG_FILE: DEFAULT_UPS_CUSTOM_IMG_FILE,
            CONF_WALMART_CUSTOM_IMG_FILE: DEFAULT_WALMART_CUSTOM_IMG_FILE,
            CONF_FEDEX_CUSTOM_IMG_FILE: DEFAULT_FEDEX_CUSTOM_IMG_FILE,
            CONF_GENERIC_CUSTOM_IMG_FILE: DEFAULT_GENERIC_CUSTOM_IMG_FILE,
            CONF_POST_DE_CUSTOM_IMG_FILE: DEFAULT_POST_DE_CUSTOM_IMG_FILE,
        }

        return self.async_show_form(
            step_id="config_3",
            data_schema=_get_schema_step_3(self._data, defaults),
            errors=self._errors,
        )

    async def async_step_config_amazon(self, user_input=None):
        """Configure form step amazon."""
        self._errors = {}
        if user_input is not None:
            self._data.update(user_input)
            self._errors, user_input = await _validate_user_input(self._data, self.hass)
            if len(self._errors) == 0:
                if (
                    self._data.get(CONF_CUSTOM_IMG)
                    or self._data.get(CONF_AMAZON_CUSTOM_IMG)
                    or self._data.get(CONF_UPS_CUSTOM_IMG)
                    or self._data.get(CONF_WALMART_CUSTOM_IMG)
                    or self._data.get(CONF_GENERIC_CUSTOM_IMG)
                    or self._data.get(CONF_POST_DE_CUSTOM_IMG)
                ):
                    return await self.async_step_config_3()
                return await self.async_step_config_storage()

            return await self._show_config_amazon(user_input)

        return await self._show_config_amazon(user_input)

    async def _show_config_amazon(self, user_input):
        """Step 3 setup."""
        # Defaults
        defaults = {
            CONF_AMAZON_DOMAIN: DEFAULT_AMAZON_DOMAIN,
            CONF_AMAZON_FWDS: DEFAULT_AMAZON_FWDS,
            CONF_AMAZON_DAYS: DEFAULT_AMAZON_DAYS,
        }

        return self.async_show_form(
            step_id="config_amazon",
            data_schema=_get_schema_step_amazon(
                user_input,
                defaults,
                forwarding_header=self._data.get(CONF_FORWARDING_HEADER, ""),
            ),
            errors=self._errors,
        )

    async def async_step_config_forwarded_emails(self, user_input=None):
        """Configure form step forwarded emails."""
        self._errors = {}
        if user_input is not None:
            self._data.update(user_input)
            self._errors, user_input = await _validate_user_input(self._data, self.hass)
            if len(self._errors) == 0:
                if any(
                    sensor in self._data[CONF_RESOURCES] for sensor in AMAZON_SENSORS
                ):
                    return await self.async_step_config_amazon()
                if self._data[CONF_CUSTOM_IMG]:
                    return await self.async_step_config_3()

                return await self.async_step_config_storage()

            return await self._show_config_forwarded_emails(user_input)

        return await self._show_config_forwarded_emails(user_input)

    async def _show_config_forwarded_emails(self, user_input):
        """Configure forwarded emails setup."""
        # Defaults
        defaults = {
            CONF_FORWARDED_EMAILS: DEFAULT_FORWARDED_EMAILS,
        }

        return self.async_show_form(
            step_id="config_forwarded_emails",
            data_schema=_get_schema_step_forwarded_emails(user_input, defaults),
            errors=self._errors,
        )

    async def async_step_config_storage(self, user_input=None):
        """Configure form step storage."""
        self._errors = {}
        if user_input is not None:
            self._data.update(user_input)
            self._errors, user_input = await _validate_user_input(self._data, self.hass)
            if len(self._errors) == 0:
                return self.async_create_entry(
                    title=f"Mail and Packages ({self._data[CONF_HOST]})",
                    data=self._data,
                )
            return await self._show_config_storage(user_input)

        return await self._show_config_storage(user_input)

    async def _show_config_storage(self, user_input):
        """Step 3 setup."""
        # Defaults
        defaults = {
            CONF_STORAGE: DEFAULT_STORAGE,
        }

        return self.async_show_form(
            step_id="config_storage",
            data_schema=_get_schema_step_storage(user_input, defaults),
            errors=self._errors,
        )

    async def async_step_reconfigure(self, user_input: dict[str, Any] | None = None):
        """Add reconfigure step to allow to reconfigure a config entry."""
        self._entry = self.hass.config_entries.async_get_entry(self.context["entry_id"])
        assert self._entry
        self._data = dict(self._entry.data)
        self._errors = {}

        if user_input is not None:
            self._data.update(user_input)
            return await self.async_step_reconfig_imap()

        return await self._show_reconfig_auth_form(user_input)

    async def async_step_reconfig_imap(self, user_input=None):
        """Handle IMAP step for reconfigure."""
        self._errors = {}

        if user_input is not None:
            self._data.update(user_input)
            auth_type = self._data.get(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD)

            if auth_type != AUTH_TYPE_PASSWORD:
                self._data[CONF_VERIFY_SSL] = True
                self.hass.data.setdefault(DOMAIN, {})
                self.hass.data[DOMAIN]["oauth_provider"] = auth_type
                return await self.async_step_pick_implementation()

            self._errors = await _validate_login(self.hass, self._data)
            if self._errors == {}:
                return await self.async_step_reconfig_2()

            return await self._show_reconfig_imap_form(user_input)

        return await self._show_reconfig_imap_form(user_input)

    async def _show_reconfig_auth_form(self, user_input):
        """Show the auth form for reconfigure."""
        defaults = {
            CONF_AUTH_TYPE: self._entry.data.get(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD),
        }
        return self.async_show_form(
            step_id="reconfigure",
            data_schema=_get_schema_auth(user_input, defaults),
            errors=self._errors,
        )

    async def _show_reconfig_imap_form(self, user_input):
        """Show the configuration form to edit IMAP data."""
        auth_type = self._data.get(CONF_AUTH_TYPE, AUTH_TYPE_PASSWORD)
        defaults = OAUTH_IMAP_DEFAULTS.get(
            auth_type,
            {
                CONF_PORT: self._entry.data.get(CONF_PORT, DEFAULT_PORT),
                CONF_IMAP_SECURITY: self._entry.data.get(CONF_IMAP_SECURITY, "SSL"),
                CONF_VERIFY_SSL: self._entry.data.get(CONF_VERIFY_SSL, False),
            },
        )

        defaults[CONF_HOST] = self._entry.data.get(CONF_HOST, defaults.get(CONF_HOST))
        defaults[CONF_USERNAME] = self._entry.data.get(
            CONF_USERNAME,
            defaults.get(CONF_USERNAME),
        )
        defaults[CONF_PASSWORD] = self._entry.data.get(
            CONF_PASSWORD,
            defaults.get(CONF_PASSWORD),
        )
        defaults[CONF_AUTH_TYPE] = auth_type

        return self.async_show_form(
            step_id="reconfig_imap",
            data_schema=_get_schema_imap(user_input, defaults),
            errors=self._errors,
        )

    async def async_step_reconfig_2(self, user_input=None):
        """Configure form step 2."""
        self._errors = {}
        _LOGGER.debug("Loading step 2...")
        if user_input is not None:
            self._data.update(user_input)
            self._errors, user_input = await _validate_user_input(user_input, self.hass)
            if len(self._errors) == 0:
                if self._data.get(CONF_ALLOW_FORWARDED_EMAILS, False):
                    return await self.async_step_reconfig_forwarded_emails()

                if any(
                    sensor in self._data.get(CONF_RESOURCES, [])
                    for sensor in AMAZON_SENSORS
                ):
                    return await self.async_step_reconfig_amazon()
                has_custom_image = (
                    self._data.get(CONF_CUSTOM_IMG)
                    or self._data.get(CONF_AMAZON_CUSTOM_IMG)
                    or self._data.get(CONF_UPS_CUSTOM_IMG)
                    or self._data.get(CONF_WALMART_CUSTOM_IMG)
                    or self._data.get(CONF_FEDEX_CUSTOM_IMG)
                    or self._data.get(CONF_GENERIC_CUSTOM_IMG)
                    or self._data.get(CONF_POST_DE_CUSTOM_IMG)
                )
                if has_custom_image:
                    return await self.async_step_reconfig_3()

                return await self.async_step_reconfig_storage()

            return await self._show_reconfig_2(user_input)

        return await self._show_reconfig_2(user_input)

    async def _show_reconfig_2(self, user_input):
        """Step 2 setup."""
        return self.async_show_form(
            step_id="reconfig_2",
            data_schema=await _get_schema_step_2(
                self._data,
                user_input,
                self._data,
                self.hass,
            ),
            errors=self._errors,
        )

    async def async_step_reconfig_3(self, user_input=None):
        """Configure form step 2."""
        self._errors = {}
        if user_input is not None:
            self._data.update(user_input)
            self._errors, user_input = await _validate_user_input(self._data, self.hass)
            if len(self._errors) == 0:
                return await self.async_step_reconfig_storage()

            return await self._show_reconfig_3(user_input)

        return await self._show_reconfig_3(user_input)

    async def _show_reconfig_3(self, user_input=None):  # pylint: disable=unused-argument
        """Step 3 setup."""
        # Defaults
        defaults = {
            CONF_CUSTOM_IMG_FILE: DEFAULT_CUSTOM_IMG_FILE,
            CONF_AMAZON_CUSTOM_IMG_FILE: DEFAULT_AMAZON_CUSTOM_IMG_FILE,
            CONF_UPS_CUSTOM_IMG_FILE: DEFAULT_UPS_CUSTOM_IMG_FILE,
            CONF_WALMART_CUSTOM_IMG_FILE: DEFAULT_WALMART_CUSTOM_IMG_FILE,
            CONF_FEDEX_CUSTOM_IMG_FILE: DEFAULT_FEDEX_CUSTOM_IMG_FILE,
            CONF_GENERIC_CUSTOM_IMG_FILE: DEFAULT_GENERIC_CUSTOM_IMG_FILE,
            CONF_POST_DE_CUSTOM_IMG_FILE: DEFAULT_POST_DE_CUSTOM_IMG_FILE,
        }

        return self.async_show_form(
            step_id="reconfig_3",
            data_schema=_get_schema_step_3(self._data, defaults),
            errors=self._errors,
        )

    async def async_step_reconfig_amazon(self, user_input=None):
        """Configure form step amazon."""
        self._errors = {}
        if user_input is not None:
            self._data.update(user_input)
            self._errors, user_input = await _validate_user_input(self._data, self.hass)
            if len(self._errors) == 0:
                has_custom_image = (
                    self._data.get(CONF_CUSTOM_IMG)
                    or self._data.get(CONF_AMAZON_CUSTOM_IMG)
                    or self._data.get(CONF_UPS_CUSTOM_IMG)
                    or self._data.get(CONF_WALMART_CUSTOM_IMG)
                    or self._data.get(CONF_FEDEX_CUSTOM_IMG)
                    or self._data.get(CONF_GENERIC_CUSTOM_IMG)
                    or self._data.get(CONF_POST_DE_CUSTOM_IMG)
                )
                if has_custom_image:
                    return await self.async_step_reconfig_3()

                return await self.async_step_reconfig_storage()

            return await self._show_reconfig_amazon(user_input)

        return await self._show_reconfig_amazon(user_input)

    async def _show_reconfig_amazon(self, user_input):
        """Step 3 setup."""
        if self._data.get(CONF_AMAZON_FWDS, []) == []:
            self._data[CONF_AMAZON_FWDS] = "(none)"

        return self.async_show_form(
            step_id="reconfig_amazon",
            data_schema=_get_schema_step_amazon(
                user_input,
                self._data,
                forwarding_header=self._data.get(CONF_FORWARDING_HEADER, ""),
            ),
            errors=self._errors,
        )

    async def async_step_reconfig_forwarded_emails(
        self,
        user_input: dict[str, Any] | None = None,
    ):
        """Configure form step forwarded emails."""
        self._errors = {}
        if user_input is not None:
            self._data.update(user_input)
            self._errors, user_input = await _validate_user_input(self._data, self.hass)
            if len(self._errors) == 0:
                if any(
                    sensor in self._data.get(CONF_RESOURCES, [])
                    for sensor in AMAZON_SENSORS
                ):
                    return await self.async_step_reconfig_amazon()
                if self._data.get(CONF_CUSTOM_IMG, False):
                    return await self.async_step_reconfig_3()
                return await self.async_step_reconfig_storage()
            return await self._show_reconfig_forwarded_emails(user_input)
        return await self._show_reconfig_forwarded_emails(user_input)

    async def _show_reconfig_forwarded_emails(self, user_input=None):
        """Step forwarded emails."""
        if self._data.get(CONF_FORWARDED_EMAILS, []) == []:
            self._data[CONF_FORWARDED_EMAILS] = "(none)"

        return self.async_show_form(
            step_id="reconfig_forwarded_emails",
            data_schema=_get_schema_step_forwarded_emails(user_input, self._data),
            errors=self._errors,
        )

    async def async_step_reconfig_storage(self, user_input=None):
        """Configure form step storage."""
        self._errors = {}
        if user_input is not None:
            self._data.update(user_input)
            self._errors, user_input = await _validate_user_input(self._data, self.hass)
            if len(self._errors) == 0:
                self.hass.config_entries.async_update_entry(
                    self._entry,
                    data=self._data,
                )
                await self.hass.config_entries.async_reload(self._entry.entry_id)
                _LOGGER.debug("%s reconfigured.", DOMAIN)
                return self.async_abort(reason="reconfigure_successful")

            return await self._show_reconfig_storage(user_input)

        return await self._show_reconfig_storage(user_input)

    async def _show_reconfig_storage(self, user_input):
        """Step 3 setup."""
        return self.async_show_form(
            step_id="reconfig_storage",
            data_schema=_get_schema_step_storage(user_input, self._data),
            errors=self._errors,
        )
