# at_02.py - functions for handling AT-02 (SEPA Creditor identifier)
#
# Copyright (C) 2014-2016 Sergi Almacellas Abellana
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA

"""SEPA Identifier of the Creditor (AT-02).

This identifier is indicated in the ISO 20022 data element `Creditor Scheme
Identification`. The creditor can be a legal entity, or an association that
is not a legal entity, or a person.

The first two digits contain the ISO country code, the next two are check
digits for the ISO 7064 Mod 97, 10 checksum, the next tree contain the
Creditor Business Code (or `ZZZ` if no business code used) and the remainder
contains the country-specific identifier.

>>> validate('ES 23 ZZZ 47690558N')
'ES23ZZZ47690558N'
>>> validate('ES2300047690558N')
'ES2300047690558N'
>>> compact('ES++()+23ZZZ4//7690558N')
'ES23ZZZ47690558N'
>>> calc_check_digits('ESXXZZZ47690558N')
'23'
"""

from __future__ import annotations

from stdnum.exceptions import *
from stdnum.iso7064 import mod_97_10
from stdnum.util import clean


# the valid characters we have
_alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'


def compact(number: str) -> str:
    """Convert the AT-02 number to the minimal representation. This strips
    the number of any valid separators and removes invalid characters."""
    return clean(number, ' -/?:().m\'+"').strip().upper()


def _to_base10(number: str) -> str:
    """Prepare the number to its base10 representation so it can be checked
    with the ISO 7064 Mod 97, 10 algorithm. That means excluding positions 5
    to 7 and moving the first four digits to the end."""
    return ''.join(str(_alphabet.index(x)) for x in number[7:] + number[:4])


def validate(number: str) -> str:
    """Check if the number provided is a valid AT-02."""
    number = compact(number)
    try:
        test_number = _to_base10(number)
    except Exception:  # noqa: B902
        raise InvalidFormat()
    # ensure that checksum is valid
    mod_97_10.validate(test_number)
    return number


def is_valid(number: str) -> bool:
    """Check if the number provided is a valid AT-02."""
    try:
        return bool(validate(number))
    except ValidationError:
        return False


def calc_check_digits(number: str) -> str:
    """Calculate the check digits that should be put in the number to make it
    valid. Check digits in the supplied number are ignored."""
    number = compact(number)
    # replace check digits with placeholders
    number = ''.join((number[:2], '00', number[4:]))
    return mod_97_10.calc_check_digits(_to_base10(number)[:-2])
