package org.jivesoftware.smackx.ikey.element;

import org.apache.xml.security.Init;
import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.c14n.InvalidCanonicalizerException;
import org.apache.xml.security.parser.XMLParserException;
import org.bouncycastle.openpgp.PGPException;
import org.jivesoftware.smack.util.stringencoder.Base64;
import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureCreationMechanism;
import org.jivesoftware.smackx.ikey.IkeySignatureCreator;
import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureVerificationMechanism;
import org.jivesoftware.smackx.ikey.IkeySignatureVerifier;
import org.jivesoftware.smackx.ikey.mechanism.IkeyType;
import org.jivesoftware.smackx.ikey.util.canonicalization.XmlSecElementCanonicalizer;
import org.jivesoftware.smackx.ikey_ox.OxIkeySignatureCreationMechanism;
import org.jivesoftware.smackx.ikey_ox.OxIkeySignatureVerificationMechanism;

import org.jivesoftware.smackx.util.MercurySmackTestSuite;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.impl.JidCreate;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.key.OpenPgpV4Fingerprint;
import org.pgpainless.key.collection.PGPKeyRing;
import org.pgpainless.key.generation.KeySpec;
import org.pgpainless.key.generation.type.ECDSA;
import org.pgpainless.key.generation.type.curve.EllipticCurve;
import org.pgpainless.key.protection.UnprotectedKeysProtector;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertTrue;

public class IkeySignatureCreatorAndVerifierTest extends MercurySmackTestSuite {

    @BeforeAll
    public static void initialize() {
        if (!Init.isInitialized()) {
            Init.init();
        }
    }

    @Test
    public void createIkeyElementAndVerifySignature() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, URISyntaxException, XMLParserException, IOException, CanonicalizationException, InvalidCanonicalizerException {
        EntityBareJid jid = JidCreate.entityBareFromOrThrowUnchecked("alice@wonderland.lit");
        PGPKeyRing keyRing = PGPainless.generateKeyRing()
                .withMasterKey(KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256))
                        .withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)
                        .withDefaultAlgorithms())
                .withPrimaryUserId("xmpp:" + jid)
                .withoutPassphrase()
                .build();
        XmlSecElementCanonicalizer elementCanonicalizer = new XmlSecElementCanonicalizer(Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS));

        IkeySignatureCreationMechanism signingMechanism = new OxIkeySignatureCreationMechanism(
                keyRing.getSecretKeys(), new UnprotectedKeysProtector());
        IkeySignatureCreator creator = new IkeySignatureCreator(signingMechanism, elementCanonicalizer);
        IkeySignatureVerificationMechanism verificationMechanism = new OxIkeySignatureVerificationMechanism(keyRing.getPublicKeys());
        IkeySignatureVerifier verifier = new IkeySignatureVerifier(verificationMechanism, elementCanonicalizer);

        OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(keyRing.getMasterKey());
        SuperordinateElement superordinate = new SuperordinateElement(Base64.encodeToString(keyRing.getMasterKey().getEncoded()));
        List<SubordinateElement> subList = new ArrayList<>();
        subList.add(new SubordinateElement(
                new URI("xmpp:" + jid + "?;node=urn:xmpp:openpgp:0:public-keys:" + fingerprint + ";item=2020-01-21T10:46:21Z"),
                fingerprint.toString()));
        SubordinateListElement subs = new SubordinateListElement(jid, new Date(), subList);

        ProofElement proofElement = creator.createProofFor(subs);
        IkeyElement ikeyElement = new IkeyElement(IkeyType.OX, superordinate, subs, proofElement);

        System.out.println(ikeyElement.toXML().toString());

        assertTrue(verifier.verify(ikeyElement, jid));
    }
}
