/*
 * 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 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 com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.sdm.application.service.BallotService;
import com.scytl.products.ov.sdm.domain.model.ballot.BallotRepository;
import com.scytl.products.ov.sdm.domain.model.ballot.BallotSignInputData;

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;

/**
 * Endpoint for managing ballot information.
 */
@RestController
@RequestMapping("/sdm-ws-rest/ballots")
@Api(value = "Ballot REST API", description = "")
public class BallotResource {

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

	@Autowired
	private BallotRepository ballotRepository;

	@Autowired
	private BallotService ballotService;

	@Autowired
	private TransactionInfoProvider transactionInfoProvider;

	@Autowired
	private SecureLoggingWriter secureLogger;

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

	/**
	 * Returns a list of ballots identified by an election event identifier.
	 *
	 * @param electionEventId the election event id.
	 * @return An election event identified by its id.
	 */
	@RequestMapping(value = "/electionevent/{electionEventId}", method = RequestMethod.GET, produces = "application/json")
	@ResponseBody
	@ApiOperation(value = "List of ballots", notes = "Service to retrieve the list of ballots.", response = String.class)
	public String getBallots(@ApiParam(value = "String", required = true) @PathVariable String electionEventId) {
		return ballotRepository.listByElectionEvent(electionEventId);
	}

	/**
	 * Change the state of the ballot from locked to signed for a given election event and ballot id.
	 *
	 * @param electionEventId the election event id.
	 * @param ballotId the ballot id.
	 * @return HTTP status code 200 - If the ballot is successfully signed. HTTP status code 404 - If the resource is not
	 *         found. HTTP status code 412 - If the ballot is already signed.
	 */
	@RequestMapping(value = "/electionevent/{electionEventId}/ballot/{ballotId}", method = RequestMethod.PUT)
	@ApiOperation(value = "Sign ballot", notes = "Service to change the state of the ballot from locked to signed "
		+ "for a given election event and ballot id..", response = Void.class)
	@ApiResponses(value = {@ApiResponse(code = 404, message = "Not Found"),
			@ApiResponse(code = 412, message = "Precondition Failed") })
	public ResponseEntity<Void> signBallot(
			@ApiParam(value = "String", required = true) @PathVariable String electionEventId,
			@ApiParam(value = "String", required = true) @PathVariable String ballotId,
			@ApiParam(value = "BallotSignInputData", required = true) @RequestBody final BallotSignInputData input) {

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

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

		try {
			ballotService.sign(electionEventId, ballotId, input.getPrivateKeyPEM());
			secureLogger.log(Level.INFO, new LogContent.LogContentBuilder()
					.logEvent(SdmSecureLogEvent.BALLOT_SIGNED_SUCCESSFULLY)
					.objectId(ballotId).electionEvent(electionEventId)
					.createLogInfo());
		} catch (ResourceNotFoundException e) {
			LOGGER.error("An error occurred while fetching the ballot to sign", e);
			secureLogger.log(Level.ERROR, new LogContent.LogContentBuilder()
					.logEvent(SdmSecureLogEvent.SIGNING_BALLOT_FAILED)
					.objectId(ballotId).electionEvent(electionEventId)
					.additionalInfo("err_desc", e.getMessage())
					.createLogInfo());
			return new ResponseEntity<>(HttpStatus.NOT_FOUND);
		} catch (GeneralCryptoLibException e) {
			LOGGER.error("An error occurred while signing the ballot", e);
			secureLogger.log(Level.ERROR, new LogContent.LogContentBuilder()
					.logEvent(SdmSecureLogEvent.SIGNING_BALLOT_FAILED)
					.objectId(ballotId).electionEvent(electionEventId)
					.additionalInfo("err_desc", e.getMessage())
					.createLogInfo());
			return new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED);
		}

		return new ResponseEntity<>(HttpStatus.OK);
	}
}
