package org.kravemir.lightvalue.examples.hello;

import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.Objects;
import java.util.function.Consumer;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;

@RunWith(JUnitParamsRunner.class)
public class ContactTest {

    @Test
    public void testBuildGuineaPig() {
        Contact contact = buildGuineaPig();

        assertThat(contact.getName(), is("Guinea Pig"));
        assertThat(contact.getEmail(), is("runner@local.cage"));
    }

    @Test
    public void testBuildLabRat() {
        Contact contact = buildLabRat();

        assertThat(contact.getName(), is("Lab Rat"));
        assertThat(contact.getEmail(), is("test-subject@laboratory"));
    }

    @Test
    @Parameters
    public void testEquals(Contact a, Contact b, boolean expected) {
        assertThat(Objects.equals(a,b), is(expected));
    }

    public Object[] parametersForTestEquals() {
        return new Object[][] {
                {buildGuineaPig(), buildGuineaPig(), true},
                {buildLabRat(), buildLabRat(), true},
                {buildGuineaPig(), buildLabRat(), false},
                {buildLabRat(), buildGuineaPig(), false},
        };
    }


    @Test
    @Parameters
    public void testHashCode(Contact a, Contact b) {
        assertTrue(a.hashCode() == b.hashCode());
    }

    public Object[] parametersForTestHashCode() {
        return new Object[][] {
                {buildGuineaPig(), buildGuineaPig()},
                {buildLabRat(), buildLabRat()},
        };
    }

    @Test
    @Parameters
    public void testRebuild(Contact original) {
        Contact rebuilt = original.toBuilder().build();

        assertThat(rebuilt.hashCode(), is(original.hashCode()));
        assertTrue(Objects.equals(original, rebuilt));
    }

    public Object[] parametersForTestRebuild() {
        return new Object[][] {
                {buildGuineaPig()},
                {buildLabRat()},
        };
    }

    @Test
    @Parameters
    public void testRebuildModify(Contact original, Consumer<Contact.Builder> modifier) {
        Contact.Builder builder = original.toBuilder();
        modifier.accept(builder);
        Contact rebuilt = builder.build();

        assertFalse(Objects.equals(original, rebuilt));
    }

    public Object[] parametersForTestRebuildModify() {
        return new Object[][] {
                {buildGuineaPig(), contactModifier(b -> b.setName("CORRUPTED"))},
                {buildGuineaPig(), contactModifier(b -> b.setEmail("CORRUPTED"))},
                {buildLabRat(), contactModifier(b -> b.setName("CORRUPTED"))},
                {buildLabRat(), contactModifier(b -> b.setEmail("CORRUPTED"))},
        };
    }

    private Consumer<Contact.Builder> contactModifier(Consumer<Contact.Builder> modifier) {
        return modifier;
    }

    private Contact buildGuineaPig() {
        // NO CREATURE WAS HARMED in order to write this library
        return Contact.newBuilder()
                .setName("Guinea Pig")
                .setEmail("runner@local.cage")
                .build();
    }

    private Contact buildLabRat() {
        // NO CREATURE WAS HARMED in order to write this library
        return Contact.newBuilder()
                .setName("Lab Rat")
                .setEmail("test-subject@laboratory")
                .build();
    }
}
