/*
 * 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.vw.services.infrastructure.remote;

import java.io.IOException;

import javax.ejb.Stateless;
import javax.inject.Inject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.scytl.products.ov.commons.beans.domain.model.receipt.Receipt;
import com.scytl.products.ov.commons.beans.exceptions.ApplicationException;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.commons.beans.exceptions.VoteRepositoryException;
import com.scytl.products.ov.commons.dto.VoteAndComputeResults;
import com.scytl.products.ov.commons.infrastructure.remote.client.RetrofitConsumer;
import com.scytl.products.ov.commons.infrastructure.remote.client.RetrofitException;
import com.scytl.products.ov.commons.tracking.TrackIdInstance;
import com.scytl.products.ov.commons.util.PropertiesFileReader;
import com.scytl.products.ov.vw.services.domain.model.vote.VoteRepository;

import okhttp3.ResponseBody;

/**
 * Implementation of vote repository as a REST client which redirects operations
 * to other Contexts.
 */
@Stateless
public class VoteRepositoryImpl implements VoteRepository {

    // PropertiesFile
    private static final PropertiesFileReader PROPERTIES = PropertiesFileReader.getInstance();

    // The path to the resource votes
    private static final String VOTES_PATH = PROPERTIES.getPropertyValue("VOTES_PATH");

    private static final String RECEIPTS_PATH = PROPERTIES.getPropertyValue("RECEIPTS_PATH");

    // Instance of the track Id which will be written in the logs
    @Inject
    private TrackIdInstance trackId;

    private ElectionInformationClient electionInformationClient;

    // Instance of the secure logger
    private static final Logger LOG = LoggerFactory.getLogger("std");

    @Inject
    VoteRepositoryImpl(final ElectionInformationClient electionInformationClient) {
        this.electionInformationClient = electionInformationClient;
    }

    /**
     * Saves a vote in the repository. In this case, use a rest call to a
     * RESTful webservice to store the vote in the ballot box. As result of this
     * operation, the webservice returns a receipt.
     *
     * @param tenantId
     *            - the tenant id
     * @param electionEventId
     *            - the election event id
     * @param vote
     *            - the vote information with computation proofs and signatures.
     * @param authenticationTokenJsonString
     *            - the authentication token in json format.
     * @return Returns a receipt generated in the storage operation.
     * @throws IOException
     *             if there are some problem during mapping to a json format.
     * @throws ApplicationException
     *             if there are validation problems in any of the input
     *             parameters.
     * @throws ResourceNotFoundException
     */
    @Override
    public Receipt save(String tenantId, String electionEventId, VoteAndComputeResults vote,
            String authenticationTokenJsonString) throws ApplicationException, ResourceNotFoundException {
        return RetrofitConsumer.processResponse(electionInformationClient.saveVote(trackId.getTrackId(), VOTES_PATH,
            tenantId, electionEventId, vote, authenticationTokenJsonString));
    }

    /**
     * @throws VoteRepositoryException
     * @see com.scytl.products.ov.vw.services.domain.model.vote.VoteRepository#findByTenantIdElectionEventIdVotingCardId(String,
     *      String, String)
     */
    @Override
    public VoteAndComputeResults findByTenantIdElectionEventIdVotingCardId(String tenantId, String electionEventId,
            String votingCardId) throws ResourceNotFoundException, VoteRepositoryException {
        try {
            return RetrofitConsumer.processResponse(electionInformationClient.getVote(trackId.getTrackId(), VOTES_PATH,
                tenantId, electionEventId, votingCardId));
        } catch (RetrofitException rfE) {
            if (rfE.getHttpCode() == 404) {
                throw new ResourceNotFoundException("Error trying to find voting card.", rfE);
            } else {
                throw new VoteRepositoryException("Error trying to find by tenant id election event Id voting card Id.",
                    rfE);
            }
        }
    }

    /**
     * @throws VoteRepositoryException
     * @see VoteRepository#getReceiptByVotingCardId(String, String, String)
     */
    @Override
    public com.scytl.products.ov.commons.beans.domain.model.receipt.Receipt getReceiptByVotingCardId(String tenantId,
            String electionEventId, String votingCardId)
            throws ResourceNotFoundException, ApplicationException, VoteRepositoryException {
        try {
            return RetrofitConsumer.processResponse(electionInformationClient.getReceipt(trackId.getTrackId(),
                RECEIPTS_PATH, tenantId, electionEventId, votingCardId));
        } catch (RetrofitException rfE) {

            switch (rfE.getHttpCode()) {
            case 404:
                throw new ResourceNotFoundException("Receipt not found.", rfE);
            case 422:
                throw new ApplicationException("Error recovering the receipt.", rfE);
            default:
                throw new VoteRepositoryException("Error trying to get receipt by voting card Id.", rfE);
            }

        }
    }

    @Override
    public boolean voteExists(String tenantId, String electionEventId, String votingCardId)
            throws VoteRepositoryException {
        try (ResponseBody responseBody = RetrofitConsumer.processResponse(electionInformationClient
            .checkVote(trackId.getTrackId(), VOTES_PATH, tenantId, electionEventId, votingCardId))) {
            // 200 OK, vote exists
            return true;
        } catch (RetrofitException rfE) {
            if (rfE.getHttpCode() == 404) {
                return false;
            } else {
                LOG.error("Error trying to find if a Vote exists.", rfE);
                throw new VoteRepositoryException("Error trying to find if a Vote exists.", rfE);
            }
        }
    }
}
