package cl.altiuz.reports;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.UUID;

import javax.xml.crypto.KeySelector;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.camel.Message;
import org.apache.camel.component.xmlsecurity.api.KeyAccessor;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

public class RucKeyAccesor implements KeyAccessor {
    private KeyStore keyStore;
    private String keystorePath;
    private String keystorePassword;

    public RucKeyAccesor(String keystorePath, String keystorePassword)
            throws KeyStoreException, NoSuchAlgorithmException,
            CertificateException, IOException {
        File file = new File(keystorePath);
        FileInputStream is = new FileInputStream(file);
        keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(is, keystorePassword.toCharArray());
        this.keystorePath = keystorePath;
        this.keystorePassword = keystorePassword;
    }

    private static final String XPATH_RUC_EMISOR = "//infoTributaria/ruc/text()";

    @Override
    public KeySelector getKeySelector(Message message)
            throws XPathExpressionException, KeyStoreException,
            UnrecoverableKeyException, NoSuchAlgorithmException,
            CertificateException, IOException {
        Document body = (Document) message.getBody();

        XPath xPath = XPathFactory.newInstance().newXPath();
        String rucEmisor = (String) xPath.evaluate(XPATH_RUC_EMISOR,
                body.getDocumentElement(), XPathConstants.STRING);

        if (rucEmisor == null) {
            throw new IllegalArgumentException("RUC no encontrado");
        }
        //System.out.println("RUC Emisor: " + rucEmisor);

        Enumeration<String> enumeration = keyStore.aliases();
        Certificate certificate = null;
        while (enumeration.hasMoreElements()) {
            String alias = enumeration.nextElement();
            if (rucEmisor.equals(alias)) {
                certificate = keyStore.getCertificate(alias);
                break;
            }
        }
        if (certificate == null) {
            throw new IllegalArgumentException("No hay certificado para RUC "
                    + rucEmisor);
        }
        PrivateKey key = (PrivateKey) keyStore.getKey(rucEmisor,
                rucEmisor.toCharArray());
        return new X509KeySelector(keystorePath, keystorePassword, false);

        // return KeySelector.singletonKeySelector(key);
    }

    @Override
    public KeyInfo getKeyInfo(Message message, Node messageBody,
            KeyInfoFactory keyInfoFactory) throws Exception {
        Document body = (Document) message.getBody();

        XPath xPath = XPathFactory.newInstance().newXPath();
        String rucEmisor = (String) xPath.evaluate(XPATH_RUC_EMISOR,
                body.getDocumentElement(), XPathConstants.STRING);
        return createKeyInfo(keyInfoFactory, rucEmisor);
    }

    private KeyInfo createKeyInfo(KeyInfoFactory kif, String rucEmisor)
            throws Exception {

        X509Certificate[] chain = getCertificateChain(rucEmisor);
        if (chain == null) {
            return null;
        }
        X509Data x509D = kif.newX509Data(Arrays.asList(chain));
        return kif.newKeyInfo(Collections.singletonList(x509D), "_"
                + UUID.randomUUID().toString());
    }

    private X509Certificate[] getCertificateChain(String alias)
            throws Exception {
        if (keyStore == null) {
            return null;
        }
        if (alias == null) {
            return null;
        }
        Certificate[] certs = keyStore.getCertificateChain(alias);
        if (certs == null) {
            return null;
        }
        ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>(
                certs.length);
        for (Certificate cert : certs) {
            if (cert instanceof X509Certificate) {
                System.out.println("DN: "
                        + ((X509Certificate) cert).getSubjectDN().getName());
                certList.add((X509Certificate) cert);
            }
        }
        return certList.toArray(new X509Certificate[certList.size()]);
    }
}
