package com.paktalin.agilejava;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * Starting with TDD here.
 * Test for Student class
 */

class StudentTest {

    @Test
    void testCreate() {
        Student student = new Student("Jane Doe");
        verifyNamesCorrect(student, "Jane Doe", "Jane", "Doe", "");

        student = new Student("Blow");
        verifyNamesCorrect(student, "Blow", "", "Blow", "");

        student = new Student("Raymond Douglas Davies");
        verifyNamesCorrect(student, "Raymond Douglas Davies",
                "Raymond", "Davies", "Douglas");
    }

    @Test
    void testStudentStatus() {
        Student student = new Student("a");
        assertEquals(0, student.getCredits());
        assertFalse(student.isFullTime());

        student.addCredits(3);
        assertEquals(3, student.getCredits());
        assertFalse(student.isFullTime());

        student.addCredits(4);
        assertEquals(7, student.getCredits());
        assertFalse(student.isFullTime());

        student.addCredits(5);
        assertEquals(Student.CREDITS_REQUIRED_FOR_FULL_TIME, student.getCredits());
        assertTrue(student.isFullTime(), "Not enough credits for FullTime status");
    }

    @Test
    void testInState() {
        Student student = new Student("a");
        assertFalse(student.isInState());
        student.setState(Student.IN_STATE);
        assertTrue(student.isInState());
        student.setState("MD");
        assertFalse(student.isInState());
    }

    @Test
    void testCalculateGpa() {
        Student student = new Student("a");

        assertGpa(student, 0.0);
        student.addGrade(Student.Grade.A);
        assertGpa(student, 4.0);
        student.addGrade(Student.Grade.B);
        assertGpa(student, 3.5);
        student.addGrade(Student.Grade.C);
        assertGpa(student, 3.0);
        student.addGrade(Student.Grade.D);
        assertGpa(student, 2.5);
        student.addGrade(Student.Grade.F);
        assertGpa(student, 2.0);
    }

    @Test
    void testCalculateHonorsStudentGpa() {
        Student student = createHonorsStudent();

        assertGpa(student, 0.0);
        student.addGrade(Student.Grade.A);
        assertGpa(student, 5.0);
        student.addGrade(Student.Grade.B);
        assertGpa(student, 4.5);
        student.addGrade(Student.Grade.C);
        assertGpa(student, 4.0);
        student.addGrade(Student.Grade.D);
        assertGpa(student, 3.5);
        student.addGrade(Student.Grade.F);
        assertGpa(student, 2.8);
    }

    @Test
    void testCharges() {
        Student student = new Student("a");
        student.addCharge(500);
        student.addCharge(200);
        student.addCharge(399);
        assertEquals(1099, student.totalCharges());
    }

    private void assertGpa(Student student, double expectedGpa) {
        final double GRADE_TOLERANCE = 0.05;
        assertEquals(expectedGpa, student.getGpa(), GRADE_TOLERANCE);
    }

    private Student createHonorsStudent() {
        Student student = new Student("a");
        student.setGradingStrategy(new HonorsGradingStrategy());
        return student;
    }

    private void verifyNamesCorrect(
            Student student, String name, String firstName, String lastName, String middleName) {
        assertEquals(name, student.getName());
        assertEquals(firstName, student.getFirstName());
        assertEquals(lastName, student.getLastName());
        assertEquals(middleName, student.getMiddleName());
    }
}