package com.ufund.api.ufundapi.service;

import java.io.IOException;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.*;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

import com.ufund.api.ufundapi.DuplicateKeyException;
import com.ufund.api.ufundapi.model.Need;
import com.ufund.api.ufundapi.model.Need.GoalType;
import com.ufund.api.ufundapi.persistence.CupboardDAO;

@Tag("Service-tier")
public class CupboardServiceTest {

    private CupboardDAO mockCupboardDAO;
    private CupboardService cupboardService;

    @BeforeEach
    public void setupCupboardService() {
        mockCupboardDAO = mock(CupboardDAO.class);
        cupboardService = new CupboardService(mockCupboardDAO);

    }

    @Test
    public void testCreateNeed() throws IOException, DuplicateKeyException {
        // Setup
        String name = "Jellyfish";
        double maxGoal = 100.00;
        GoalType type = GoalType.MONETARY;
        Need need = new Need(name, type, maxGoal);

        // When the same id is passed in, our mock User DAO will return the User object
        when(mockCupboardDAO.addNeed(any())).thenReturn(need);
        when(mockCupboardDAO.getNeeds()).thenReturn(new Need[0]);

        // Invoke
        Need response = cupboardService.createNeed(name, maxGoal, type);

        // Analyze
        assertNotNull(response);
        assertEquals(need, response);
    }

    @Test
    public void testCreateNeedBadGoal() throws IOException, DuplicateKeyException {
        // Setup
        String name = "Jellyfish";
        double maxGoal = -100.00;
        GoalType type = GoalType.MONETARY;
        Need need = new Need(name, type, maxGoal);

        // When the same id is passed in, our mock User DAO will return the User object
        when(mockCupboardDAO.addNeed(any())).thenReturn(need);
        when(mockCupboardDAO.getNeeds()).thenReturn(new Need[0]);

        // Invoke
        // Need response = cupboardService.createNeed(name, maxGoal, type);

        // Analyze
        assertThrows(IllegalArgumentException.class, () -> {
            cupboardService.createNeed(name, maxGoal, type);
        });
    }

    @Test
    public void testCreateNeedDuplicate() throws IOException, DuplicateKeyException {
        // Setup
        String name = "Jellyfish";
        double maxGoal = 100.00;
        GoalType type = GoalType.MONETARY;
        Need need = new Need(name, type, maxGoal);
        Need[] needs = { need };

        // When the same id is passed in, our mock User DAO will return the User object
        when(mockCupboardDAO.addNeed(any())).thenReturn(need);
        when(mockCupboardDAO.getNeeds()).thenReturn(needs);

        // Invoke
        // Need response = cupboardService.createNeed(name, maxGoal, type);

        // Analyze
        assertThrows(DuplicateKeyException.class, () -> {
            cupboardService.createNeed(name, maxGoal, type);
        });
    }

    @Test
    public void testSearchNeeds() throws IOException, DuplicateKeyException {
        // Setup
        String name = "Jellyfish";
        double maxGoal = 100.00;
        GoalType type = GoalType.MONETARY;
        Need need = new Need(name, type, maxGoal);
        Need[] needs = { need };

        // When the same id is passed in, our mock User DAO will return the User object
        when(mockCupboardDAO.getNeeds()).thenReturn(needs);

        // Invoke
        Need[] response = cupboardService.searchNeeds("Jelly");

        // Analyze
        assertEquals(need, response[0]);
        assertEquals(need.getName(), response[0].getName());
    }

    @Test
    public void testSearchNeedsFail() throws IOException, DuplicateKeyException {
        // Setup
        String name = "Jellyfish";
        double maxGoal = 100.00;
        GoalType type = GoalType.MONETARY;
        Need need = new Need(name, type, maxGoal);
        Need[] needs = { need };

        // When the same id is passed in, our mock User DAO will return the User object
        when(mockCupboardDAO.getNeeds()).thenReturn(needs);

        // Invoke
        Need[] response = cupboardService.searchNeeds("Octopus");

        // Analyze
        assertArrayEquals(new Need[0], response);
    }

    @Test
    public void testGetNeed() throws IOException, DuplicateKeyException {
        // Setup
        String name = "Jellyfish";
        double maxGoal = 100.00;
        int id = 0;
        GoalType type = GoalType.MONETARY;
        Need need = new Need(name, type, maxGoal);

        // When the same id is passed in, our mock User DAO will return the User object
        when(mockCupboardDAO.getNeed(id)).thenReturn(need);

        // Invoke
        Need response = cupboardService.getNeed(id);

        // Analyze
        assertEquals(need, response);
    }

    @Test
    public void testUpdateNeed() throws IOException, DuplicateKeyException {
        // Setup
        String name = "Jellyfish";
        double maxGoal = 100.00;
        int id = 0;
        GoalType type = GoalType.MONETARY;
        Need need = new Need(name, type, maxGoal);
        Need newNeed = new Need("Octopus", type, maxGoal);

        // When the same id is passed in, our mock User DAO will return the User object
        when(mockCupboardDAO.updateNeed(any())).thenReturn(newNeed);

        // Invoke
        Need response = cupboardService.updateNeed(newNeed, id);

        // Analyze
        assertEquals(newNeed, response);
    }

    @Test
    public void testDeleteNeed() throws IOException, DuplicateKeyException {
        // Setup
        String name = "Jellyfish";
        double maxGoal = 100.00;
        int id = 0;
        GoalType type = GoalType.MONETARY;
        Need need = new Need(name, type, maxGoal);

        // When the same id is passed in, our mock User DAO will return the User object
        when(mockCupboardDAO.deleteNeed(id)).thenReturn(true);
        when(mockCupboardDAO.getNeeds()).thenReturn(new Need[0]);

        // Invoke
        boolean response = cupboardService.deleteNeed(id);
        Need[] responseNeeds = cupboardService.getNeeds();

        // Analyze
        assertTrue(response);
        assertArrayEquals(new Need[0], responseNeeds);
    }

}