/*
 * Copyright (c) eHealth
 */
package be.fgov.ehealth.technicalconnector.jca.proxy;

import be.ehealth.technicalconnector.beid.impl.CommonsEidAdaptor;
import be.fedict.commons.eid.client.BeIDCard;
import be.fedict.commons.eid.client.FileType;
import be.fedict.commons.eid.client.impl.BeIDDigest;
import be.fedict.commons.eid.jca.BeIDProvider;
import be.fgov.ehealth.technicalconnector.distributedkeys.DistributedSignerProxy;
import org.apache.commons.lang.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.Security;
import java.security.SignatureException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static be.ehealth.technicalconnector.utils.ConnectorIOUtils.closeQuietly;


/**
 * Proxy class for BeID
 *
 * @author EHP
 */
public class BeIDDistributedSigner implements DistributedSignerProxy {

    private static Logger LOG = LoggerFactory.getLogger(BeIDDistributedSigner.class);

    private Map<String, List<X509Certificate>> chainMap = new HashMap<String, List<X509Certificate>>();

    public BeIDDistributedSigner() {
        Security.addProvider(new BeIDProvider());
    }

    @Override
    public byte[] sign(byte[] digestValue, String digestAlgo, String alias) throws SignatureException {
        BeIDDigest digest = null;
        for (BeIDDigest beEidDigest : BeIDDigest.values()) {
            if (beEidDigest.getStandardName()
                    .equalsIgnoreCase(digestAlgo)) {
                digest = beEidDigest;
                break;
            }
        }
        FileType type = getFileType(alias);
        Validate.notNull(digest);

        BeIDCard card = null;
        try {
            card = new CommonsEidAdaptor().getBeIDCard();
            return card.sign(digestValue, digest, type, false);
        } catch (Exception e) {
            throw new SignatureException(e);
        } finally {
            closeQuietly(card);
        }
    }

    private FileType getFileType(String alias) {
        FileType type = null;
        if ("Authentication".equalsIgnoreCase(alias)) {
            type = FileType.AuthentificationCertificate;
        } else if ("Signature".equalsIgnoreCase(alias)) {
            type = FileType.NonRepudiationCertificate;
        }
        Validate.notNull(type);
        return type;
    }

    @Override
    public Set<String> getAliases() {
        Set<String> result = new HashSet<String>();
        result.add("Authentication");
        result.add("Signature");
        return result;
    }

    @Override
    public List<X509Certificate> getCertificateChain(String alias) {
        if (chainMap.containsKey(alias)) {
            return chainMap.get(alias);
        }
        LOG.debug("getCertificateChain");
        BeIDCard card = null;
        try {
            card = new CommonsEidAdaptor().getBeIDCard();
            List<X509Certificate> certificateChain = card.getCertificateChain(getFileType(alias));
            chainMap.put(alias, certificateChain);
            return certificateChain;
        } catch (Exception e) {
            throw new IllegalArgumentException(e);
        } finally {
            closeQuietly(card);
        }
    }

    @Override
    public String getAlgorithm(String alias) {
        List<X509Certificate> chain = getCertificateChain(alias);
        if (chain != null && !chain.isEmpty()) {
            return chain.get(0).getPublicKey().getAlgorithm();
        }
        throw new IllegalArgumentException("Unable to determine algorithm");
    }
}
