import re

from ..common.models import IdType


def check_digit_10(isbn):
    assert len(isbn) == 9
    sum = 0
    for i in range(len(isbn)):
        c = int(isbn[i])
        w = i + 1
        sum += w * c
    r = sum % 11
    return "X" if r == 10 else str(r)


def check_digit_13(isbn):
    assert len(isbn) == 12
    sum = 0
    for i in range(len(isbn)):
        c = int(isbn[i])
        w = 3 if i % 2 else 1
        sum += w * c
    r = 10 - (sum % 10)
    return "0" if r == 10 else str(r)


def isbn_10_to_13(isbn):
    if not isbn or len(isbn) != 10:
        return None
    return "978" + isbn[:-1] + check_digit_13("978" + isbn[:-1])


def isbn_13_to_10(isbn):
    if not isbn or len(isbn) != 13 or isbn[:3] != "978":
        return None
    else:
        return isbn[3:12] + check_digit_10(isbn[3:12])


def is_isbn_13(isbn):
    return re.match(r"^\d{13}$", isbn) is not None


def is_isbn_10(isbn):
    return re.match(r"^\d{9}[X0-9]$", isbn) is not None


def is_asin(asin):
    return re.match(r"^B[A-Z0-9]{9}$", asin) is not None


def detect_isbn_asin(s: str) -> tuple[IdType, str] | tuple[None, None]:
    if not s:
        return None, None
    n = re.sub(r"[^0-9A-Z]", "", s.upper())
    if is_isbn_13(n):
        return IdType.ISBN, n
    if is_isbn_10(n):
        v = isbn_10_to_13(n)
        return (IdType.ISBN, v) if v else (None, None)
    if is_asin(n):
        return IdType.ASIN, n
    return None, None


def binding_to_format(binding: str | None):
    from .models import Edition

    if not binding:
        return None
    if re.search(r"(Audio|Audible|音频)", binding, flags=re.IGNORECASE):
        return Edition.BookFormat.AUDIOBOOK
    if re.search(
        r"(pub|ebook|e-book|kindle|electronic|电子)", binding, flags=re.IGNORECASE
    ):
        return Edition.BookFormat.HARDCOVER
    if re.search(r"(web|网)", binding, flags=re.IGNORECASE):
        return Edition.BookFormat.WEB
    if re.search(r"(精|Hard)", binding, flags=re.IGNORECASE):
        return Edition.BookFormat.HARDCOVER
    if re.search(r"(平|Paper|Soft)", binding, flags=re.IGNORECASE):
        return Edition.BookFormat.PAPERBACK
    return None
