"""
This utility serves the purpose to serialize 2FA responses from the gateway to the control process.
"""
import base64
import os
import re
from typing import Optional, Tuple

TWO_FACTOR_RE = re.compile(r"^2FA\|(\d+)\|([^|]*)\|([^|]*)$")


class TwoFactorError(Exception):
    def __init__(self, msg: str):
        Exception.__init__(self, msg)

    @property
    def message(self) -> str:
        return self.args[0]


def gateway_to_control(two_factor_payload) -> str:
    return "|".join(
        [
            "2FA",
            str(two_factor_payload.get("mode")),
            str(two_factor_payload.get("error")),
            str(two_factor_payload.get("detail") or ""),
        ]
    )


class AbstractTwoFactorErrorHandler(object):
    @staticmethod
    def get_two_factor(result: Optional[str]) -> Optional[Tuple[int, str, str]]:
        if isinstance(result, str):
            m = TWO_FACTOR_RE.match(result)
            if m:
                return int(m.group(1)), m.group(2), m.group(3)

    def handle_two_factor_response(self, mode_2fa, error_2fa, detail_2fa) -> dict:
        if error_2fa == "denied":
            if detail_2fa == "lockout":
                raise TwoFactorError(
                    gettext("Two-factor authentication has been locked because of unsuccessful attempts.")
                )
            raise TwoFactorError(gettext("Two-factor authentication is denied or invalid."))
        elif mode_2fa == 10:
            if error_2fa == "prerequisite":
                if detail_2fa == "otpSalt":
                    # add salt & resubmit
                    return {"otpSalt": base64.b64encode(os.urandom(16)).decode()}
                else:
                    raise TwoFactorError(
                        "%s %s (%s)"
                        % (gettext("Unexpected two-factor authentication prerequisite for mode:"), detail_2fa, mode_2fa)
                    )
            elif error_2fa == "required":
                # query user the code & resubmit
                code = self._query_user_2fa_code()
                if code:
                    return {"2faCode": code}
                raise TwoFactorError(gettext("Two-factor authentication is cancelled."))
            else:
                raise TwoFactorError("%s %s" % (gettext("Unexpected two-factor authentication error:"), error_2fa))
        elif mode_2fa == 30:
            # add empty code & resubmit
            return {"2faCode": ""}
        else:
            raise TwoFactorError("%s %s" % (gettext("Unsupported two-factor authentication mode:"), mode_2fa))

    def _query_user_2fa_code(self) -> Optional[str]:
        raise NotImplementedError()
