/*
 * 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.commons.util.JsonUtils;
import com.scytl.products.ov.sdm.application.log.SdmSecureLogEvent;
import com.scytl.products.ov.sdm.application.service.ElectionEventService;
import com.scytl.products.ov.sdm.domain.model.electionevent.ElectionEventRepository;
import com.scytl.products.ov.sdm.domain.model.generator.DataGeneratorResponse;
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.apache.commons.lang3.exception.ExceptionUtils;
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.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
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.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import javax.json.JsonObject;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.file.Path;

/**
 * The election event end-point.
 */
@RestController
@RequestMapping("/sdm-ws-rest/electionevents")
@Api(value = "Election event REST API", description = "")
public class ElectionEventResource {

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

    // The election event repository
    @Autowired
    private ElectionEventRepository electionEventRepository;

    // The configuration of contests / elections
    @Value("${elections.config.filename}")
    private String configFile;

    @Autowired
    private ElectionEventService electionEventService;

    @Autowired
    private TransactionInfoProvider transactionInfoProvider;

    @Autowired
    private SecureLoggingWriter secureLogger;

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

    /**
     * Runs the securization of an election event.
     *
     * @param electionEventId the election event id.
     * @return a list of ids of the created election events.
     */
    @RequestMapping(value = "/{electionEventId}", method = RequestMethod.POST, produces = "application/json")
    @ApiOperation(value = "Secure election event", notes = "Service to secure an election event.", response = Void.class)
    @ApiResponses(value = {@ApiResponse(code = 404, message = "Not Found")})
    public ResponseEntity<Void> secureElectionEvent(
        @ApiParam(value = "String", required = true) @PathVariable String electionEventId) throws IOException {

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

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

        DataGeneratorResponse response;

        try {
            response = electionEventService.create(electionEventId);
        } catch (IOException ex) {
            secureLogger.log(Level.ERROR, new LogContent.LogContentBuilder()
                    .logEvent(SdmSecureLogEvent.SECURING_EE_FAILED)
                    .objectId(electionEventId).electionEvent(electionEventId)
                    .additionalInfo("err_desc", ex.getMessage()).createLogInfo());

            throw ex;
        }

        if (response.isSuccessful()) {
            secureLogger.log(Level.INFO, new LogContent.LogContentBuilder()
                    .logEvent(SdmSecureLogEvent.SECURING_EE_COMPLETED_SUCCESSFULLY)
                    .electionEvent(electionEventId).objectId(electionEventId)
                    .createLogInfo());

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

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

    /**
     * Returns an election event identified by its id.
     *
     * @param electionEventId the election event id.
     * @return An election event identified by its id.
     */
    @RequestMapping(value = "/{electionEventId}", method = RequestMethod.GET, produces = "application/json")
    @ResponseBody
    @ApiOperation(value = "Get election event", notes = "Service to retrieve a given election event.", response = String.class)
    @ApiResponses(value = {@ApiResponse(code = 404, message = "Not Found")})
    public ResponseEntity<String> getElectionEvent(
        @ApiParam(value = "String", required = true) @PathVariable String electionEventId) {

        String result = electionEventRepository.find(electionEventId);
        JsonObject jsonObject = JsonUtils.getJsonObject(result);
        if (!jsonObject.isEmpty()) {
            return new ResponseEntity<>(result, HttpStatus.OK);
        }
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }

    /**
     * Returns a list of all election events.
     *
     * @return The list of election events.
     */
    @RequestMapping(method = RequestMethod.GET, produces = "application/json")
    @ResponseBody
    @ApiOperation(value = "Get election events", notes = "Service to retrieve the list of events.", response = String.class)
    public ResponseEntity<String> getElectionEvents() {
        return ResponseEntity.ok(electionEventRepository.list());
    }

    /**
     * Returns a list of all election events.
     *
     * @return The list of election events.
     */
    @RequestMapping(value = "/{electionEventId}/participation", method = RequestMethod.GET, produces = "text/plain")
    @ResponseBody
    @ApiOperation(value = "Download the election participation/used voting cards",
        notes = "Service to download a file with the list of used voting cards. Returns the path to the file if downloaded successfully",
        response = String.class)
    public ResponseEntity<String> downloadParticipationFile(@PathVariable("electionEventId") final String electionEventId) {

        final Path participationFile = electionEventService.downloadParticipationFile(electionEventId);
        return ResponseEntity.status(HttpStatus.OK).body(participationFile.toString());
    }

    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> error(HttpServletRequest req, Exception exception) {
        LOGGER.error(String.format("Failed to process request to '%s.", req.getRequestURI()), exception);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
            .contentType(MediaType.TEXT_PLAIN)
            .body(ExceptionUtils.getRootCauseMessage(exception));
    }
}
