/*
 * 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.vv.domain.model.content;

import java.io.IOException;
import java.security.PrivateKey;

import javax.ejb.EJB;
import javax.inject.Inject;

import com.scytl.cryptolib.api.asymmetric.AsymmetricServiceAPI;
import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.products.ov.commons.beans.VoteVerificationContextData;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.commons.beans.utils.PasswordEncrypter;
import com.scytl.products.ov.commons.cache.Cache;
import com.scytl.products.ov.commons.crypto.PasswordForObjectRepository;
import com.scytl.products.ov.commons.ui.ws.rs.ObjectMappers;
import com.scytl.products.ov.vv.domain.model.tenant.VvTenantSystemKeys;

/**
 *
 */
public class CodesSecretPasswordRepository implements PasswordForObjectRepository {

    // The verification content repository.
    @EJB
    private VerificationContentRepository verificationContentRepository;

    @EJB
    private VvTenantSystemKeys vvTenantSystemKeys;

    @Inject
    private AsymmetricServiceAPI asymmetricService;

    @EJB(beanName = "CodesSecretKeyKeystorePasswordCache")
    private Cache<String, String> passwordCache;

    private static final String KEY_SEPERATOR = "-";

    /**
     * Find a keystore data string given this tenant identifier, the
     * electionEventIdentifier and the verification card set id. *
     *
     * @param tenantId
     *            the tenant identifier.
     * @param electionEventId
     *            the election event identifier.
     * @param verificationCardSetId
     *            the verification card set identifier.
     * @return the keystore data represented as a string.
     * @throws ResourceNotFoundException
     *             if the keystore data can not be found.
     * @see com.scytl.products.ov.commons.crypto.KeystoreForObjectRepository#getJsonByTenantEEIDObjectId(java.lang.String,
     *      java.lang.String, java.lang.String)
     */
    @Override
    public String getByTenantEEIDObjectIdAlias(final String tenantId, final String eeId,
            final String verificationCardSetId, final String alias) throws ResourceNotFoundException {

        // Watch out: alias is not being used. Is it really needed? It was
        // introduced before objectId.

        String cacheKey = constructCacheKey(tenantId, eeId, verificationCardSetId);

        String cachedPassword = passwordCache.get(cacheKey);
		if ((cachedPassword != null) && (!cachedPassword.isEmpty())) {
			return cachedPassword;
		}

        String decryptedString;
        try {
            // recover from db
            VerificationContentEntity verificationContent = verificationContentRepository
                .findByTenantIdElectionEventIdVerificationCardSetId(tenantId, eeId, verificationCardSetId);
            VoteVerificationContextData voteVerificationContextData =
                ObjectMappers.fromJson(verificationContent.getJson(), VoteVerificationContextData.class);

            String passwordFromDB = voteVerificationContextData.getCodesSecretKeyPassword();

            decryptedString = decryptedPassword(tenantId, passwordFromDB);
        } catch (GeneralCryptoLibException | IOException e) {
            throw new ResourceNotFoundException(
                "Error while trying to recover tenant system password to decrypt the codes secret keystore password",
                e);
        }

        passwordCache.put(cacheKey, decryptedString);

        return decryptedString;
    }

    private String constructCacheKey(final String tenantId, final String eeId, final String verificationCardSetId) {

        return tenantId + KEY_SEPERATOR + eeId + KEY_SEPERATOR + verificationCardSetId;
    }

    private String decryptedPassword(final String tenantId, final String passwordFromDB)
            throws GeneralCryptoLibException, IOException {

        PasswordEncrypter passwordEncrypter = new PasswordEncrypter(asymmetricService);

        PrivateKey privateKey = vvTenantSystemKeys.getTenantPrivateKeys(tenantId).get(0);

        return passwordEncrypter.decryptPassword(passwordFromDB, privateKey);
    }
}
