"""Internal Certbot display utilities."""
import sys
import textwrap
from typing import Optional

from acme import messages as acme_messages
from certbot._internal import san
from certbot.compat import misc


def wrap_lines(msg: str) -> str:
    """Format lines nicely to 80 chars.

    :param str msg: Original message

    :returns: Formatted message respecting newlines in message
    :rtype: str

    """
    lines = msg.splitlines()
    fixed_l = []

    for line in lines:
        fixed_l.append(textwrap.fill(
            line,
            80,
            break_long_words=False,
            break_on_hyphens=False))

    return '\n'.join(fixed_l)


def parens_around_char(label: str) -> str:
    """Place parens around first character of label.

    :param str label: Must contain at least one character

    """
    return "({first}){rest}".format(first=label[0], rest=label[1:])


def input_with_timeout(prompt: Optional[str] = None, timeout: float = 36000.0) -> str:
    """Get user input with a timeout.

    Behaves the same as the builtin input, however, an error is raised if
    a user doesn't answer after timeout seconds. The default timeout
    value was chosen to place it just under 12 hours for users following
    our advice and running Certbot twice a day.

    :param str prompt: prompt to provide for input
    :param float timeout: maximum number of seconds to wait for input

    :returns: user response
    :rtype: str

    :raises errors.Error if no answer is given before the timeout

    """
    # use of sys.stdin and sys.stdout to mimic the builtin input based on
    # https://github.com/python/cpython/blob/baf7bb30a02aabde260143136bdf5b3738a1d409/Lib/getpass.py#L129
    if prompt:
        sys.stdout.write(prompt)
        sys.stdout.flush()

    line = misc.readline_with_timeout(timeout, prompt)

    if not line:
        raise EOFError
    return line.rstrip('\n')


def separate_list_input(input_: str) -> list[str]:
    """Separate a comma or space separated list.

    :param str input_: input from the user

    :returns: strings
    :rtype: list

    """
    no_commas = input_.replace(",", " ")
    # Each string is naturally unicode, this causes problems with M2Crypto SANs
    # TODO: check if above is still true when M2Crypto is gone ^
    return [str(string) for string in no_commas.split()]


def summarize_sans(sans: list[san.SAN]) -> str:
    """Summarizes a list of identifiers in the format of:
        example.com.com and N more
    or if there are only two identifiers:
        example.com and 192.0.2.77
    or if there is only one identifier:
        example.com

    :param list sans: `san.SAN` list of domains and/or IP addresses
    :returns: a summary
    :rtype: str
    """
    if not sans:
        return ""

    length = len(sans)
    if length == 1:
        return str(sans[0])
    elif length == 2:
        return f"{sans[0]} and {sans[1]}"
    else:
        return f"{sans[0]} and {length - 1} more"


def describe_acme_error(error: acme_messages.Error) -> str:
    """Returns a human-readable description of an RFC7807 error.

    :param error: The ACME error
    :returns: a string describing the error, suitable for human consumption.
    :rtype: str
    """
    parts = (error.title, error.detail)
    if any(parts):
        return ' :: '.join(part for part in parts if part is not None)
    if error.description:
        return error.description
    return error.typ
