package xyz.zhangyi.ddd.eas.valueadded.employeecontext.domain.employee;

import org.junit.Before;
import org.junit.Test;
import xyz.zhangyi.ddd.eas.valueadded.employeecontext.domain.employee.IDCard;
import xyz.zhangyi.ddd.eas.valueadded.employeecontext.domain.exception.InvalidIdCardException;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

public class IDCardTest {
    private static final String NULL_OR_EMPTY_ERROR_MESSAGE = "Id card number should not be null or empty";
    private static final String DIGIT_NUMBER_ERROR_MESSAGE = "is not begin with digit number";
    private DateTimeFormatter dateFormatter;
    private String validIdCardNumberOfMale = "34052419800101001X";
    private String validIdCardNumberOfFemale = "510225199011015187";

    @Before
    public void setUp() {
        dateFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
    }

    @Test
    public void should_throw_InvalidIdCardException_given_null_number() {
        assertThatThrownBy(() -> new IDCard(null))
                .isInstanceOf(InvalidIdCardException.class)
                .hasMessageContaining(NULL_OR_EMPTY_ERROR_MESSAGE);
    }

    @Test
    public void should_throw_InvalidIdCardException_given_empty_number() {
        assertThatThrownBy(() -> new IDCard(""))
                .isInstanceOf(InvalidIdCardException.class)
                .hasMessageContaining(NULL_OR_EMPTY_ERROR_MESSAGE);
    }

    @Test
    public void should_throw_InvalidIdCardException_given_number_which_length_is_not_18() {
        assertThatThrownBy(() -> new IDCard("1234567890123456789"))
                .isInstanceOf(InvalidIdCardException.class)
                .hasMessageContaining("is not 18");
    }

    @Test
    public void should_throw_InvalidIdCardException_given_number_start_with_non_digital() {
        assertThatThrownBy(() -> new IDCard("X12345678901234567"))
                .isInstanceOf(InvalidIdCardException.class)
                .hasMessageContaining(DIGIT_NUMBER_ERROR_MESSAGE);
    }

    @Test
    public void should_throw_InvalidIdCardException_given_number_start_with_0() {
        assertThatThrownBy(() -> new IDCard("012345678901234567"))
                .isInstanceOf(InvalidIdCardException.class)
                .hasMessageContaining(DIGIT_NUMBER_ERROR_MESSAGE);
    }

    @Test
    public void should_throw_InvalidIdCardException_given_number_contains_non_digital_value_start_2_to_17() {
        assertThatThrownBy(() -> new IDCard("1X2345678901234567"))
                .isInstanceOf(InvalidIdCardException.class)
                .hasMessageContaining(DIGIT_NUMBER_ERROR_MESSAGE);
    }

    @Test
    public void should_throw_InvalidIdCardException_given_number_end_with_other_letter() {
        assertThatThrownBy(() -> new IDCard("12345678901234567Y"))
                .isInstanceOf(InvalidIdCardException.class)
                .hasMessageContaining(DIGIT_NUMBER_ERROR_MESSAGE);
    }

    @Test
    public void should_throw_InvalidIdCardException_given_number_with_invalid_birthday() {
        assertThatThrownBy(() -> new IDCard("510225198013015130"))
                .isInstanceOf(InvalidIdCardException.class)
                .hasMessageContaining("Birthday of Id card is invalid");
    }

    @Test
    public void should_throw_InvalidIdCardException_given_number_with_birthday_after_now() {
        LocalDateTime now = LocalDateTime.now().plusDays(1);
        String strOfNow = now.format(dateFormatter);

        assertThatThrownBy(() -> new IDCard(String.format("510225%s5130", strOfNow)))
                .isInstanceOf(InvalidIdCardException.class)
                .hasMessageContaining("Birthday of Id card is invalid");
    }

    @Test
    public void should_throw_InvalidIdCardException_given_number_with_birthday_before_1900() {
        assertThatThrownBy(() -> new IDCard(String.format("510225%s5130", "18991231")))
                .isInstanceOf(InvalidIdCardException.class)
                .hasMessageContaining("Birthday of Id card is invalid");
    }

    @Test
    public void should_throw_InvalidIdCardException_given_wrong_checksum() {
        assertThatThrownBy(() -> new IDCard("510225199011015131"))
                .isInstanceOf(InvalidIdCardException.class)
                .hasMessageContaining("Checksum")
                .hasMessageContaining("is wrong");
    }

    @Test
    public void should_set_number_for_id_card() {
        IDCard idCard = new IDCard(validIdCardNumberOfMale);

        assertThat(idCard.number()).isEqualTo(validIdCardNumberOfMale);
    }

    @Test
    public void should_be_male_given_number_with_odd_value_at_17() {
        IDCard idCard = new IDCard(validIdCardNumberOfMale);

        assertThat(idCard.isMale()).isTrue();
    }

    @Test
    public void should_be_male_given_number_with_even_value_at_17() {
        IDCard idCard = new IDCard(validIdCardNumberOfFemale);

        assertThat(idCard.isFemale()).isTrue();
    }
}