/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.camel.quarkus.component.xmlsecurity.it;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.xml.crypto.AlgorithmMethod;
import javax.xml.crypto.KeySelector;
import javax.xml.crypto.KeySelectorResult;
import javax.xml.crypto.XMLCryptoContext;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.spec.XPathType;

import org.w3c.dom.Node;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Named;
import jakarta.inject.Singleton;
import org.apache.camel.Message;
import org.apache.camel.component.xmlsecurity.api.KeyAccessor;
import org.apache.camel.component.xmlsecurity.api.XmlSignatureHelper;

@ApplicationScoped
public class XmlsecurityProducers {

    @Produces
    @Named("accessor")
    public KeyAccessor getAccessor(KeyPair keyPair) {
        return new KeyAccessor() {
            @Override
            public KeySelector getKeySelector(Message message) throws Exception {
                return KeySelector.singletonKeySelector(keyPair.getPrivate());
            }

            @Override
            public KeyInfo getKeyInfo(Message mess, Node messageBody, KeyInfoFactory keyInfoFactory) throws Exception {
                return null;
            }
        };
    }

    @Produces
    @Named("selector")
    public KeySelector getSelector(KeyPair keyPair) {
        return new KeySelector() {
            @Override
            public KeySelectorResult select(KeyInfo keyInfo, Purpose purpose, AlgorithmMethod algorithmMethod,
                    XMLCryptoContext xmlCryptoContext) {
                return () -> keyPair.getPublic();
            }
        };
    }

    @Produces
    @Named("canonicalizationMethod")
    public AlgorithmMethod getCanonicalizationMethod() {
        List<String> inclusivePrefixes = new ArrayList<>();
        inclusivePrefixes.add("ds");
        return XmlSignatureHelper.getCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE, inclusivePrefixes);
    }

    @Produces
    @Named("transformsXPath")
    public List<AlgorithmMethod> getTransformsXPathMethod() {
        List<XmlSignatureHelper.XPathAndFilter> list = new ArrayList<>(3);
        XmlSignatureHelper.XPathAndFilter xpath1 = new XmlSignatureHelper.XPathAndFilter("//n0:ToBeSigned",
                XPathType.Filter.INTERSECT.toString());
        list.add(xpath1);
        XmlSignatureHelper.XPathAndFilter xpath2 = new XmlSignatureHelper.XPathAndFilter("//n0:NotToBeSigned",
                XPathType.Filter.SUBTRACT.toString());
        list.add(xpath2);
        XmlSignatureHelper.XPathAndFilter xpath3 = new XmlSignatureHelper.XPathAndFilter("//n0:ReallyToBeSigned",
                XPathType.Filter.UNION.toString());
        list.add(xpath3);

        List<AlgorithmMethod> result = new ArrayList<>();
        result.add(XmlSignatureHelper.getCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE));

        Map<String, String> map = new HashMap<>();
        map.put("n0", "http://test/test");
        result.add(XmlSignatureHelper.getXPath2Transform(list, map));

        return result;
    }

    @Produces
    @Named("transformsXsltXPath")
    public List<AlgorithmMethod> getTransformsXsltXPathMethod() throws Exception {
        AlgorithmMethod transformXslt = XmlSignatureHelper.getXslTransform("/xslt-test.xsl");
        Map<String, String> namespaceMap = new HashMap<>();
        namespaceMap.put("n0", "https://org.apache/camel/xmlsecurity/test");
        AlgorithmMethod transformXpath = XmlSignatureHelper.getXPathTransform("//n0:XMLSecurity/n0:Content", namespaceMap);
        List<AlgorithmMethod> result = new ArrayList<>();
        result.add(XmlSignatureHelper.getCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE));
        result.add(transformXslt);
        result.add(transformXpath);
        return result;
    }

    @Singleton
    @Produces
    public KeyPair getKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyGen;
        keyGen = KeyPairGenerator.getInstance("RSA");
        keyGen.initialize(2048, new SecureRandom());
        return keyGen.generateKeyPair();
    }

    @Singleton
    @Produces
    @Named("key")
    public SecretKey key() throws NoSuchAlgorithmException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        keyGenerator.init(256);
        keyGenerator.generateKey();
        return keyGenerator.generateKey();
    }

    @Produces
    @Named("cryptoContextProperties")
    public Map<String, Object> cryptoContextProperties() {
        Map<String, Object> properties = new HashMap<>();
        properties.put("org.apache.jcp.xml.dsig.secureValidation", Boolean.FALSE);
        return properties;
    }
}
