package com.ufund.api.ufundapi.controller;

import java.io.IOException;

import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import com.ufund.api.ufundapi.model.User;
import com.ufund.api.ufundapi.persistence.UserFileDAO;

@Tag("Controller-tier")
public class UserControllerTest {
    private UserController userController;
    private UserFileDAO mockUserDAO;

    @BeforeEach
    public void setupUserController() {
        mockUserDAO = mock(UserFileDAO.class);
        userController = new UserController(mockUserDAO);

    }

    @Test
    public void testGetUser() throws IOException { // getUser may throw IOException
        // Setup
        String username = "Test";
        User user = new User(username);
        // When the same id is passed in, our mock User DAO will return the User object
        when(mockUserDAO.getUser(username)).thenReturn(user);

        // Invoke
        ResponseEntity<User> response = userController.getUser(username);

        // Analyze
        assertEquals(HttpStatus.OK, response.getStatusCode());
        assertEquals(user, response.getBody());
    }

    @Test
    public void testGetUserNotFound() throws Exception { // createUser may throw IOException
        // Setup
        String username = "Test";
        // When the same id is passed in, our mock User DAO will return null, simulating
        // no User found
        when(mockUserDAO.getUser(username)).thenReturn(null);

        // Invoke
        ResponseEntity<User> response = userController.getUser(username);

        // Analyze
        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
    }

    @Test
    public void testGetUserHandleException() throws Exception { // createUser may throw IOException
        // Setup
        String username = "Test";
        // When getUser is called on the Mock User DAO, throw an IOException
        doThrow(new IOException()).when(mockUserDAO).getUser(username);

        // Invoke
        ResponseEntity<User> response = userController.getUser(username);

        // Analyze
        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
    }

    /*****************************************************************
     * The following tests will fail until all userController methods
     * are implemented.
     ****************************************************************/

    @Test
    public void testCreateUser() throws IOException { // createUser may throw IOException
        // Setup
        String username = "Test";
        User user = new User(username);
        // when createUser is called, return true simulating successful
        // creation and save
        when(mockUserDAO.createUser(user)).thenReturn(user);

        // Invoke
        ResponseEntity<User> response = userController.createUser(user);

        // Analyze
        assertEquals(HttpStatus.CREATED, response.getStatusCode());
        assertEquals(user, response.getBody());
    }

    @Test
    public void testCreateUserFailed() throws IOException { // createUser may throw IOException
        // Setup
        String username = "Test";
        User user = new User(username);
        // when createUser is called, return false simulating failed
        // creation and save
        when(mockUserDAO.createUser(user)).thenReturn(null);

        // Invoke
        ResponseEntity<User> response = userController.createUser(user);

        // Analyze
        assertEquals(HttpStatus.CONFLICT, response.getStatusCode());
    }

    @Test
    public void testCreateUserHandleException() throws IOException { // createUser may throw IOException
        // Setup
        String username = "Test";
        User user = new User(username);

        // When createUser is called on the Mock User DAO, throw an IOException
        doThrow(new IOException()).when(mockUserDAO).createUser(user);

        // Invoke
        ResponseEntity<User> response = userController.createUser(user);

        // Analyze
        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
    }

    @Test
    public void testUpdateUser() throws IOException { // updateUser may throw IOException
        // Setup
        String username = "Test";
        User user = new User("Bob");
        // when updateUser is called, return true simulating successful
        // update and save
        when(mockUserDAO.updateUser(user, username)).thenReturn(user);

        // Invoke
        ResponseEntity<User> response = userController.updateUser(user, username);

        // Analyze
        assertEquals(HttpStatus.OK, response.getStatusCode());
        assertEquals(user, response.getBody());
    }

    @Test
    public void testUpdateUserFailed() throws IOException { // updateUser may throw IOException
        // Setup
        String username = "Test";
        User user = new User("Bob");
        // when updateUser is called, return true simulating successful
        // update and save
        when(mockUserDAO.updateUser(user, username)).thenReturn(null);

        // Invoke
        ResponseEntity<User> response = userController.updateUser(user, username);

        // Analyze
        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
    }

    @Test
    public void testUpdateUserHandleException() throws IOException { // updateUser may throw IOException
        // Setup
        String username = "Test";
        User user = new User("Bob");
        // When updateUser is called on the Mock User DAO, throw an IOException
        doThrow(new IOException()).when(mockUserDAO).updateUser(user, username);

        // Invoke
        ResponseEntity<User> response = userController.updateUser(user, username);

        // Analyze
        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
    }

    @Test
    public void testDeleteUser() throws IOException { // deleteUser may throw IOException
        // Setup
        String username = "Test";
        // when deleteUser is called return true, simulating successful deletion
        when(mockUserDAO.deleteUser(username)).thenReturn(true);

        // Invoke
        ResponseEntity<User> response = userController.deleteUser(username);

        // Analyze
        assertEquals(HttpStatus.OK, response.getStatusCode());
    }

    @Test
    public void testDeleteUserNotFound() throws IOException { // deleteUser may throw IOException
        // Setup
        String username = "Test";
        // when deleteUser is called return false, simulating failed deletion
        when(mockUserDAO.deleteUser(username)).thenReturn(false);

        // Invoke
        ResponseEntity<User> response = userController.deleteUser(username);

        // Analyze
        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
    }

    @Test
    public void testDeleteUserHandleException() throws IOException { // deleteUser may throw IOException
        // Setup
        String username = "Test";
        // When deleteUser is called on the Mock User DAO, throw an IOException
        doThrow(new IOException()).when(mockUserDAO).deleteUser(username);

        // Invoke
        ResponseEntity<User> response = userController.deleteUser(username);

        // Analyze
        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
    }

}