/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.products.ov.sdm.ui.ws.rs.application;

import com.scytl.products.oscore.logging.api.domain.Level;
import com.scytl.products.oscore.logging.api.domain.LogContent;
import com.scytl.products.oscore.logging.api.writer.SecureLoggingWriter;
import com.scytl.products.oscore.logging.core.provider.TransactionInfoProvider;
import com.scytl.products.ov.sdm.application.log.SdmSecureLogEvent;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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 org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

import com.scytl.products.ov.commons.beans.exceptions.ApplicationException;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.sdm.application.service.IdleStatusService;
import com.scytl.products.ov.sdm.application.service.TallyingService;
import com.scytl.products.ov.sdm.domain.common.IdleState;
import com.scytl.products.ov.sdm.domain.model.tallying.TallyingInputData;

/**
 * The tallying endpoint.
 */
@RestController
@RequestMapping("/sdm-ws-rest/tallying")
@Api(value = "Tallying REST API", description = "")
public class TallyingResource {

	private static final Logger LOGGER = LoggerFactory.getLogger(TallyingResource.class);

	@Autowired
	private TallyingService tallyingService;

	@Autowired
	private IdleStatusService idleStatusService;

	@Autowired
	private TransactionInfoProvider transactionInfoProvider;

	@Autowired
	private SecureLoggingWriter secureLogger;

	@Value("${tenantID}")
	private String tenantId;

	/**
	 * Performs the tallying of certain ballot box.
	 *
	 * @param electionEventId the election event id.
	 * @param ballotBoxId the ballot box id.
	 * @return Status 200 on success.
	 * @throws IOException
	 */
	@RequestMapping(value = "/electionevent/{electionEventId}/ballotbox/{ballotBoxId}", method = RequestMethod.POST, produces = "application/json")
	@ResponseBody
	@ApiOperation(value = "Tally ballot box", notes = "Service to tally a given ballot box.", response = String.class)
	@ApiResponses(value = {@ApiResponse(code = 404, message = "Not Found"), @ApiResponse(code = 403, message = "Forbidden"),
			@ApiResponse(code = 500, message = "Internal Server Error"),
			@ApiResponse(code = 501, message = "Not Implemented")})
	public ResponseEntity<IdleState> tallyBallotBox(
			@ApiParam(value = "String", required = true) @PathVariable String electionEventId,
			@ApiParam(value = "String", required = true) @PathVariable String ballotBoxId,
			@ApiParam(value = "TallyingInputData", required = true) @RequestBody TallyingInputData inputData)
			throws IOException, ResourceNotFoundException {

		if (!tallyingService.isEnabled()) {
	        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
	    }

		transactionInfoProvider.generate(tenantId, "", "");

		secureLogger.log(Level.INFO, new LogContent.LogContentBuilder()
				.logEvent(SdmSecureLogEvent.TALLYING_RESULTS)
				.objectId(ballotBoxId).electionEvent(electionEventId)
				.createLogInfo());

		//checks firstly if the ballot box id is already doing an operation
		IdleState idleState = new IdleState();
		if (!(idleStatusService.isIdReady(ballotBoxId))){
			idleState.setIdle(true);
			return new ResponseEntity<>(idleState,HttpStatus.OK);
		}
		try {
			if (tallyingService.tallyBallotBox(electionEventId, ballotBoxId, inputData.getPrivateKeyPEM())) {
				idleStatusService.freeIdleStatus(ballotBoxId);

				secureLogger.log(Level.INFO, new LogContent.LogContentBuilder()
						.logEvent(SdmSecureLogEvent.TALLYING_RESULTS_COMPLETED_SUCCESSFULLY)
						.objectId(ballotBoxId).electionEvent(electionEventId)
						.createLogInfo());

				return new ResponseEntity<>(HttpStatus.OK);
			} else {
				idleStatusService.freeIdleStatus(ballotBoxId);
				return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
			}
		} catch (ResourceNotFoundException e) {
			idleStatusService.freeIdleStatus(ballotBoxId);
			LOGGER.error("A resource was missing when performing tallying", e);
			return new ResponseEntity<>(HttpStatus.NOT_FOUND);
		} catch (ApplicationException e) {
			idleStatusService.freeIdleStatus(ballotBoxId);
			LOGGER.error("The request can not be performed for the current resource status", e);
			return new ResponseEntity<>(HttpStatus.FORBIDDEN);
		}
	}
	
	@RequestMapping(value = "/status", method = RequestMethod.GET, produces = "application/json")
    @ResponseBody
    @ApiOperation(value = "Tallying is enabled", notes = "Returns the  tallying status.", response = Boolean.class)
    @ApiResponses({@ApiResponse(code = 500, message = "Internal Server Error")})
    public ResponseEntity<TallyingStatus> getStatus() {
        TallyingStatus status = new TallyingStatus();
        status.setEnabled(tallyingService.isEnabled());
        return new ResponseEntity<>(status, HttpStatus.OK);
    }
}
