/*
 * 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.commons.beans.domain.model.tenant;

import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Container class that maintains a map that links tenant IDs to private keys
 * and their related certificate chains. These mappings are stored in memory.
 * <P>
 * For each tenant ID, zero or more private keys and/or certificate chains can
 * be stored.
 */
public class TenantSystemKeys {

    private static final String ENCRYPTION_KEY_ALIAS = "encryptionkey";

    private static final String SIGNING_KEY_ALIAS = "signingkey";

    /**
     * Map of private keys indexed by type (encryption, signing) and then
     * indexed by tenant.
     */
    private final Map<String, Map<String, PrivateKey>> privateKeys = new HashMap<>();

    /**
     * Map of certificate chains indexed by type (encryption, signing) and then
     * indexed by tenant.
     */
    private final Map<String, Map<String, X509Certificate[]>> certificateChains = new HashMap<>();

    /**
     * Adds an entry (a tenant ID and a list of private keys) into the map.
     *
     * @param tenantId
     *            the tenant ID
     * @param tenantPrivateKeys
     *            the private key(s) associated with the tenant ID.
     */
    public void setInitialized(final String tenantId, final Map<String, PrivateKey> tenantPrivateKeys) {
        privateKeys.put(tenantId, tenantPrivateKeys);
    }

    /**
     * Adds an entry (a tenant ID and a list of private keys) into the map.
     *
     * @param tenantId
     *            the tenant ID
     * @param tenantCertificateChains
     *            the certificate chain(s) of the public key(s) associated with
     *            the tenant ID.
     */
    public void addCertificateChains(final String tenantId,
            final Map<String, X509Certificate[]> tenantCertificateChains) {
        certificateChains.put(tenantId, tenantCertificateChains);
    }

    /**
     * Invalidates the key(s) associated with the specified tenant ID. This
     * results in the mapping associated with this tenant ID being deleted from
     * the map.
     *
     * @param tenantId
     */
    public void invalidate(final String tenantId) {
        privateKeys.remove(tenantId);
    }

    /**
     * Determines whether or not there is an entry for the specified tenant ID
     * in the mapping.
     */
    public boolean getInitialized(final String tenantId) {
        return privateKeys.containsKey(tenantId);
    }

    /**
     * Obtain the private keys associated with a tenant ID, if any exist.
     * <P>
     * Note: behaves like a map in the fact that it returns null if no entry is
     * found for the specified tenant ID.
     */
    @Deprecated // does not honour aliases
    public List<PrivateKey> getTenantPrivateKeys(final String tenantId) {

        return Collections.singletonList(privateKeys.get(tenantId).get(ENCRYPTION_KEY_ALIAS));
    }

    /**
     * Gets a tenant's private key from the encryption key pair (or, more
     * precisely, the decryption key).
     * 
     * @param tenantId
     *            the tenant identifier
     * @return the encryption key for the current service and specified tenant.
     */
    public PrivateKey getEncryptionPrivateKey(String tenantId) {
        validateTenant(privateKeys.keySet(), tenantId);

        return privateKeys.get(tenantId).get(ENCRYPTION_KEY_ALIAS);
    }

    /**
     * Gets a tenant's private key from the signing key pair.
     * 
     * @param tenantId
     *            the tenant identifier
     * @return the requested private key for the current service and specified
     *         tenant.
     */
    public PrivateKey getSigningPrivateKey(String tenantId) {
        validateTenant(privateKeys.keySet(), tenantId);

        return privateKeys.get(tenantId).get(SIGNING_KEY_ALIAS);
    }

    public X509Certificate[] getSigningCertificateChain(String tenantId) {
        validateTenant(certificateChains.keySet(), tenantId);

        return certificateChains.get(tenantId).get(SIGNING_KEY_ALIAS);
    }

    /**
     * Ensure that a set of keys contains the tenant ID.
     * 
     * @param keys
     *            the keys to check, typically passed as <@code
     *            mapToCheck.keySet()>
     * @param tenantId
     *            the tenant to look for
     */
    private void validateTenant(Set<String> keys, String tenantId) {
        if (!keys.contains(tenantId)) {
            throw new IllegalArgumentException("No keys found for tenant " + tenantId);
        }
    }
}
