package tutorial.rest.mvc;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestParam;
import tutorial.core.models.entities.ActiveIngredient;
import tutorial.core.models.entities.ActiveIngredientRecomendation;
import tutorial.core.services.AccountService;
import tutorial.core.services.ActiveIngredientRecomendationService;
import tutorial.core.services.ActiveIngredientService;
import tutorial.core.services.exceptions.ActiveIngredientExistsException;
import tutorial.core.services.exceptions.ActiveIngredientNotFoundException;
import tutorial.core.services.util.ActiveIngredientList;
import tutorial.core.services.util.ActiveIngredientRecomendationList;
import tutorial.rest.exceptions.ConflictException;
import tutorial.rest.exceptions.NotFoundException;
import tutorial.rest.resources.ActiveIngredientListResource;
import tutorial.rest.resources.ActiveIngredientListResourceWithRecomendations;
import tutorial.rest.resources.ActiveIngredientRecomendationListResource;
import tutorial.rest.resources.ActiveIngredientRecomendationResource;
import tutorial.rest.resources.ActiveIngredientResource;
import tutorial.rest.resources.ActiveIngredientResourceWithRecomendations;
import tutorial.rest.resources.asm.ActiveIngredientListResourceAsm;
import tutorial.rest.resources.asm.ActiveIngredientRecomendationListResourceAsm;
import tutorial.rest.resources.asm.ActiveIngredientRecomendationResourceAsm;
import tutorial.rest.resources.asm.ActiveIngredientResourceAsm;


/**
 * 
 * @author Xavier
 */
@Controller
@RequestMapping("/rest/activeIngredients")
public class ActiveIngredientController {
    private ActiveIngredientService activeIngredientService;
    private AccountService accountService;
    private ActiveIngredientRecomendationService activeIngredientRecomendationService;

    /**
     *
     * @param activeIngredientService
     * @param accountService
     * @param activeIngredientRecomendationService
     */
    @Autowired
    public ActiveIngredientController(ActiveIngredientService activeIngredientService,
            AccountService accountService,
            ActiveIngredientRecomendationService activeIngredientRecomendationService) {
        this.activeIngredientService = activeIngredientService;
        this.accountService = accountService;
        this.activeIngredientRecomendationService = activeIngredientRecomendationService;
    }

    /**
     * Adds one activeIngredient to the database
     * @param res the values that the created activeIngredient will have
     * @return returns the created activeIngredient
     */
    @RequestMapping(method = RequestMethod.POST)
    @PreAuthorize("permitAll")
    public ResponseEntity<ActiveIngredientResource> createActiveIngredient(
            @RequestBody ActiveIngredientResource res)
    {
        try {
            ActiveIngredient createdActiveIngredient = activeIngredientService.createActiveIngredient(res.toActiveIngredient());
            ActiveIngredientResource createdActiveIngredientRes = new ActiveIngredientResourceAsm().toResource(createdActiveIngredient);
            return new ResponseEntity<ActiveIngredientResource>(createdActiveIngredientRes, HttpStatus.CREATED);
        } catch(ActiveIngredientExistsException exception)
        {
            throw new ConflictException(exception);
        }
    }

    /**
     * Returns a List of all activeIngredients
     * @return a List of activeIngredients
     */
    @RequestMapping(method = RequestMethod.GET)
    @PreAuthorize("permitAll")
    public ResponseEntity<ActiveIngredientListResource> findAllActiveIngredients()
    {
        ActiveIngredientList activeIngredientList = activeIngredientService.findAllActiveIngredients();
        ActiveIngredientListResource activeIngredientListRes = new ActiveIngredientListResourceAsm().toResource(activeIngredientList);
        return new ResponseEntity<ActiveIngredientListResource>(activeIngredientListRes, HttpStatus.OK);
    }
    
    /**
     * Returns a List of all activeIngredients with their recommendations
     * @return 
     */
    @RequestMapping(value="/recomendations",
        method = RequestMethod.GET)
    @PreAuthorize("permitAll")
    public ResponseEntity<ActiveIngredientListResourceWithRecomendations> findAllActiveIngredientsWithRecomendations()
    {
        ActiveIngredientList activeIngredientList = activeIngredientService.findAllActiveIngredients();
        ActiveIngredientListResource activeIngredientListRes = new ActiveIngredientListResourceAsm().toResource(activeIngredientList);
        
        ActiveIngredientListResourceWithRecomendations returnList = new ActiveIngredientListResourceWithRecomendations(activeIngredientListRes);
        List list = returnList.getActiveIngredients();
        for(int i = 0;i<list.size();i++){
            ActiveIngredientResourceWithRecomendations item = returnList.getActiveIngredients().get(i);
            Long id = activeIngredientService.findActiveIngredientByName(item.getName()).getActiveIngredient_id();
            ActiveIngredientRecomendationListResource recomendations = new ActiveIngredientRecomendationListResourceAsm().toResource(activeIngredientService.findAllActiveIngredientRecomendations(id));
            returnList.getActiveIngredients().get(i).setRecomendations(recomendations);
        }
        
        return new ResponseEntity<ActiveIngredientListResourceWithRecomendations>(returnList, HttpStatus.OK);
    }

    /**
     * Returns the activeIngredient with the given Id
     * @param activeIngredientId the given Id
     * @return the activeIngredient with the given Id or a NOT_FOUND error
     */
    @RequestMapping(value="/{activeIngredientId}",
        method = RequestMethod.GET)
    @PreAuthorize("permitAll")
    public ResponseEntity<ActiveIngredientResource> getActiveIngredient(@PathVariable Long activeIngredientId)
    {
        ActiveIngredient activeIngredient = activeIngredientService.findActiveIngredient(activeIngredientId);
        if(activeIngredient != null) {
            ActiveIngredientResource res = new ActiveIngredientResourceAsm().toResource(activeIngredient);
            return new ResponseEntity<ActiveIngredientResource>(res, HttpStatus.OK);
        } else {
            return new ResponseEntity<ActiveIngredientResource>(HttpStatus.NOT_FOUND);
        }
    }

    /**
     * Adds a recommendation to the activeIngredient with the given Id
     * @param activeIngredientId the given Id
     * @param sentActiveIngredientRecomendation all the information of the recommendation
     * @return the activeIngredientRecommendation created or a NOT_FOUND exception
     */
    @RequestMapping(value="/{activeIngredientId}/activeIngredient-recomendations",
    method = RequestMethod.POST)
    @PreAuthorize("permitAll")
    public ResponseEntity<ActiveIngredientRecomendationResource> createActiveIngredientRecomendation(
            @PathVariable Long activeIngredientId,
            @RequestBody ActiveIngredientRecomendationResource sentActiveIngredientRecomendation
    ) {
        ActiveIngredientRecomendation createdActiveIngredientRecomendation = null;
        try {
            createdActiveIngredientRecomendation = activeIngredientService.createActiveIngredientRecomendation(activeIngredientId, sentActiveIngredientRecomendation.toActiveIngredientRecomendation());
            ActiveIngredientRecomendationResource createdResource = new ActiveIngredientRecomendationResourceAsm().toResource(createdActiveIngredientRecomendation);
            return new ResponseEntity<ActiveIngredientRecomendationResource>(createdResource, HttpStatus.CREATED);
        } catch (ActiveIngredientNotFoundException e) {
            throw new NotFoundException(e);
        }
    }

    /**
     * Returns a list of the recommendations of the activeIngredient with the given Id
     * @param activeIngredientId the given Id
     * @return the list of recommendations or a NOT_FOUND exception
     */
    @RequestMapping(value="/{activeIngredientId}/activeIngredient-recomendations",
    method = RequestMethod.GET)
    @PreAuthorize("permitAll")
    public ResponseEntity<ActiveIngredientRecomendationListResource> findAllActiveIngredientRecomendations(
            @PathVariable Long activeIngredientId)
    {
        try {
            ActiveIngredientRecomendationList list = activeIngredientService.findAllActiveIngredientRecomendations(activeIngredientId);
            ActiveIngredientRecomendationListResource res = new ActiveIngredientRecomendationListResourceAsm().toResource(list);
            return new ResponseEntity<ActiveIngredientRecomendationListResource>(res, HttpStatus.OK);
        } catch(ActiveIngredientNotFoundException exception)
        {
            throw new NotFoundException(exception);
        }
    }
    
    /**
     * Given the content of a csv file, creates all the activeIngredients sent in the data.
     * @param payload the content of a csv file
     * @return HttpStatus.CONFLICT if there is an Error or HttpStatus.CREATED if there was no problem and everything was created
     */
    @RequestMapping(value="/excel",
    method = RequestMethod.POST)
    @PreAuthorize("permitAll")
    public ResponseEntity createActiveIngredientsFromExcel(
            @RequestBody String payload
    ) {
        if(!payload.contains("\n")){
            return new ResponseEntity<ActiveIngredientResource>(HttpStatus.CONFLICT);
        }
        String[] rows = payload.split("\n");
        for(int i = 1;i<rows.length;i++){
            try {
            ActiveIngredientResource res = new ActiveIngredientResource();
            HashMap<String, String> map = new HashMap<String, String>();
            ArrayList<String> names = res.getValuesName();
            String[] row = parseCSVLine(rows[i]);
            for(int j = 0;j<names.size();j++){
                if(j<row.length){
                    map.put(names.get(j), row[j]);
                } 
            }
            ActiveIngredient createdActiveIngredient = activeIngredientService.createActiveIngredient(res.setValuesFromHashMap(map));
            new ActiveIngredientResourceAsm().toResource(createdActiveIngredient);
            } catch(ActiveIngredientExistsException e)
            {
                return new ResponseEntity<ActiveIngredientResource>(HttpStatus.CONFLICT);
            }
        }
        return new ResponseEntity<ActiveIngredientResource>(HttpStatus.CREATED);
    }
    
    /**
     * Given the content of a csv file, creates all the recommendations sent in the data.
     * @param payload the content of a csv file
     * @return HttpStatus.CONFLICT if there is an Error or HttpStatus.CREATED if there was no problem and everything was created
     */
    @RequestMapping(value="/excelRecomendations",
    method = RequestMethod.POST)
    @PreAuthorize("permitAll")
    public ResponseEntity createActiveIngredientsRecomendationsFromExcel(
            @RequestBody String payload
    ) {
        if(!payload.contains("\n")){
            return new ResponseEntity<ActiveIngredientResource>(HttpStatus.CONFLICT);
        }
        String[] rows = payload.split("\n");
        for(int i = 1;i<rows.length;i++){
            System.out.println("Line: "+i);
            try {
            ActiveIngredientRecomendationResource res = new ActiveIngredientRecomendationResource();
            HashMap<String, Object> map = new HashMap<String, Object>();
            ArrayList<String> names = res.getValuesName();
            String[] row = parseCSVLine(rows[i]);
            for(int j = 0;j<names.size();j++){
                if(j<row.length){
                    map.put(names.get(j), row[j]);
                } 
            }
            
            ActiveIngredient actIng = activeIngredientService.findActiveIngredientByName((String)map.get("activeIngredient"));
            if(actIng == null){
                return new ResponseEntity<ActiveIngredientResource>(HttpStatus.CONFLICT);
            }
            ActiveIngredientRecomendation createdActiveIngredientRecomendation = activeIngredientService.createActiveIngredientRecomendation(actIng.getActiveIngredient_id(),res.setValuesFromHashMap(map));
            
            new ActiveIngredientRecomendationResourceAsm().toResource(createdActiveIngredientRecomendation);
            } catch(ActiveIngredientExistsException e)
            {
                return new ResponseEntity<ActiveIngredientResource>(HttpStatus.CONFLICT);
            }
        }
        return new ResponseEntity<ActiveIngredientResource>(HttpStatus.CREATED);
    }
    
    /**
     * 
     * @param row
     * @return 
     */
    private String[] parseCSVLine(String row){
        String[] splitted = row.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)");
        for( int i = 0; i < splitted.length-1; i++){
            String element = splitted[i];
            if(element.startsWith("\"") && element.endsWith("\"")){
                element = element.substring(1, element.length()-1);
            }
            splitted[i] = element;
        }
        return splitted;
    }

    /**
     * If the user and password are in the database, and the user is an administrator,
     * deletes all data from the activeIngredient and activeIngredientRecomendation table
     * @param name user email
     * @param password user password in MD5
     * @return HttpStatus.CONFLICT if the password doesn't match, HttpStatus.OK if everything went OK
     */
    @RequestMapping(value="/clearTable",
    method = RequestMethod.POST)
    @PreAuthorize("permitAll")
    public ResponseEntity deleteAllEmailDomains(
            @RequestParam(value="name", required = true) String name,
            @RequestParam(value="password", required = true) String password) {
        if(!accountService.isAccountCorrect(name, password)) {
            return new ResponseEntity(HttpStatus.CONFLICT);
        }else{
            activeIngredientRecomendationService.deleteAllRecomendations();
            activeIngredientService.deleteAllActiveIngredients();
        }
        return new ResponseEntity(HttpStatus.OK);
    }
}
