/*
 * @Copyright 2018-2026 HardBackNutter
 * @License GNU General Public License
 *
 * This file is part of NeverTooManyBooks.
 *
 * NeverTooManyBooks is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * NeverTooManyBooks 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with NeverTooManyBooks. If not, see <http://www.gnu.org/licenses/>.
 */

package com.hardbacknutter.nevertoomanybooks.entities.codes;

import androidx.annotation.NonNull;

import java.util.stream.Stream;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;

class ISBNTest {

    @NonNull
    static Stream<Arguments> validIsbn13() {
        return Stream.of(
                Arguments.of("9782723481229"),
                Arguments.of("978-1-886778-17-7"),
                Arguments.of("9781886778177"),
                Arguments.of("978-0-684-18818-8"),
                Arguments.of("9780684188188"),

                Arguments.of("9791028102838")
        );
    }

    /**
     * Same as {@link #validIsbn13()}, but one digit changed so the checksum fails.
     */
    @NonNull
    static Stream<Arguments> invalidIsbn13() {
        return Stream.of(
                Arguments.of("9782823481229"),
                Arguments.of("978-1-986778-17-7"),
                Arguments.of("9781886878177"),
                Arguments.of("978-0-684-18818-7"),
                Arguments.of("9780684188178"),

                Arguments.of("9791028102848")
        );
    }

    @NonNull
    static Stream<Arguments> validIsbn1310() {
        return Stream.of(
                Arguments.of("9782723481229", "2723481220"),
                Arguments.of("978-1-886778-17-7", "1886778175"),
                Arguments.of("9781886778177", "1886778175"),
                Arguments.of("978-0-684-18818-8", "068418818X"),
                Arguments.of("9780684188188", "068418818X")
                );
    }

    @NonNull
    static Stream<Arguments> validIsbn1013() {
        return Stream.of(
                Arguments.of("272348-1220", "9782723481229"),
                Arguments.of("18-8677-8175", "9781886778177"),
                Arguments.of("1886-77-8175", "9781886778177"),
                Arguments.of("068-418818-X", "9780684188188"),
                Arguments.of("06841-88-18X", "9780684188188")
        );
    }


    /**
     * SBN codes which can be translated to ISBN-10.
     */
    @NonNull
    static Stream<Arguments> validSbn() {
        return Stream.of(
                Arguments.of("345-21908-2", "0345219082"),
                Arguments.of("345-21908-2-095", "0345219082")
        );
    }

    /**
     * UPC codes which can be translated to ISBN-10.
     */
    @NonNull
    static Stream<Arguments> validUpcIsbn() {
        return Stream.of(
                Arguments.of("0-70999-00225-5 30054", "0345300548")
        );
    }

    /**
     * Generic UPC_A codes
     */
    @NonNull
    static Stream<Arguments> valid_upc() {
        return Stream.of(
                Arguments.of("7 12345 67890 4", "712345678904")
        );
    }


    /**
     * Generic EAN-13 codes.
     */
    @NonNull
    static Stream<Arguments> valid_ean13() {
        return Stream.of(
                Arguments.of("5410983535003", "5410983535003")
        );
    }

    /**
     * pure 8 digit ISSN codes.
     */
    @NonNull
    static Stream<Arguments> valid_issn8() {
        return Stream.of(
                Arguments.of("0378-5955", "03785955"),
                Arguments.of("0024-984X", "0024984X")
        );
    }

    /**
     * pure 13 digit ISSN codes.
     */
    @NonNull
    static Stream<Arguments> valid_issn13() {
        return Stream.of(
                Arguments.of("977 0378-595 12 5", "9770378595125"),
                Arguments.of("9770024984648", "9770024984648")
        );
    }

    /**
     * mixed 13 digit input; 8 digit expected ISSN codes.
     */
    @NonNull
    static Stream<Arguments> valid_issn138() {
        return Stream.of(
                Arguments.of("977 0378-595 12 5", "03785955"),
                Arguments.of("9770024984648", "0024984X")
        );
    }

    @ParameterizedTest
    @MethodSource("validIsbn13")
    void validIsbn13(@NonNull final String isbnStr) {
        final ProductCode productCode = ISBN.parse(isbnStr);
        assertSame(ProductCodeType.Isbn13, productCode.getType());
    }

    @ParameterizedTest
    @MethodSource("invalidIsbn13")
    void invalidIsbn13(@NonNull final String isbnStr) {
        final ProductCode productCode = ISBN.parse(isbnStr);
        assertSame(ProductCodeType.Invalid, productCode.getType());
    }

    @ParameterizedTest
    @MethodSource("validIsbn1310")
    void validIsbn1310(@NonNull final String isbnStr,
                      @NonNull final String expected) {
        final ProductCode productCode = ISBN.parse(isbnStr);
        assertSame(ProductCodeType.Isbn13, productCode.getType());
        assertEquals(expected, productCode.asText(ProductCodeType.Isbn10));
    }

    @ParameterizedTest
    @MethodSource("validIsbn1013")
    void validIsbn1013(@NonNull final String isbnStr,
                       @NonNull final String expected) {
        final ProductCode productCode = ISBN.parse(isbnStr);
        assertSame(ProductCodeType.Isbn10, productCode.getType());
        assertEquals(expected, productCode.asText(ProductCodeType.Isbn13));
    }

    @ParameterizedTest
    @MethodSource("validSbn")
    void validSbn(@NonNull final String sbnStr,
                  @NonNull final String expected) {
        final ProductCode productCode = ISBN.parse(sbnStr);
        assertSame(ProductCodeType.Isbn10, productCode.getType());
        assertEquals(expected, productCode.asText(ProductCodeType.Isbn10));
    }

    @ParameterizedTest
    @MethodSource("validUpcIsbn")
    void validUpcIsbn(@NonNull final String upcStr,
                      @NonNull final String expected) {
        final ProductCode productCode = ISBN.parse(upcStr);
        assertSame(ProductCodeType.Isbn10, productCode.getType());
        assertEquals(expected, productCode.asText(ProductCodeType.Isbn10));
    }


    @ParameterizedTest
    @MethodSource("valid_upc")
    void valid_upc(@NonNull final String upcStr,
                   @NonNull final String expected) {
        final ProductCode productCode = ISBN.parse(upcStr);
        assertSame(ProductCodeType.UpcA, productCode.getType());
        assertEquals(expected, productCode.asText(ProductCodeType.UpcA));
    }

    @ParameterizedTest
    @MethodSource("valid_ean13")
    void valid_ean13(@NonNull final String eanStr,
                     @NonNull final String expected) {
        final ProductCode productCode = ISBN.parse(eanStr);
        assertSame(ProductCodeType.Ean13, productCode.getType());
        assertEquals(expected, productCode.asText(ProductCodeType.Ean13));
    }

    @ParameterizedTest
    @MethodSource("valid_issn8")
    void valid_issn8(@NonNull final String issnStr,
                     @NonNull final String expected) {
        final ProductCode productCode = ISBN.parse(issnStr);
        assertSame(ProductCodeType.Issn8, productCode.getType());
        assertEquals(expected, productCode.asText(ProductCodeType.Issn8));
    }

    @ParameterizedTest
    @MethodSource("valid_issn13")
    void valid_issn13(@NonNull final String issnStr,
                      @NonNull final String expected) {
        final ProductCode productCode = ISBN.parse(issnStr);
        assertSame(ProductCodeType.Issn13, productCode.getType());
        assertEquals(expected, productCode.asText(ProductCodeType.Issn13));
    }

    @ParameterizedTest
    @MethodSource("valid_issn138")
    void valid_issn138(@NonNull final String issnStr,
                       @NonNull final String expected) {
        final ProductCode productCode = ISBN.parse(issnStr);
        assertSame(ProductCodeType.Issn13, productCode.getType());
        assertEquals(expected, productCode.asText(ProductCodeType.Issn8));
    }
}
