# SPDX-FileCopyrightText: 2022 FIT-Connect contributors
#
# SPDX-License-Identifier: EUPL-1.2
import os
import pathlib
import random
import tempfile

import pytest
import certificateValidation as verify
from OpenSSL import crypto as openssl_crypto
import pkcs12ToJwk as pkcs
from pkcs12ToJwk import KeyLengthError

VALID_KEYSTORE = "keystores/keystore-5767.p12"
REVERSED_KEYSTORE = "keystores/keystore-5767-reordered-chain.p12"


def make_temp_dir():
    return pathlib.Path(tempfile.mkdtemp())


def test_happy_path():
    errors = []

    force = True

    output = make_temp_dir()
    env = "test"
    os.environ["PKCS12_CONTAINER_PASS"] = os.getenv("KEYSTORE_5767_PASS")

    try:
        (
            private_key,
            certificate,
            certificate_chain,
        ) = pkcs.read_pkcs12(VALID_KEYSTORE, env)
    except Exception:
        pytest.fail("Error reading in the certificate.")

    # write jwk
    try:
        pkcs.write_jwk_files(private_key, certificate, certificate_chain, output, force)
    except Exception as e:
        errors.append("Error generating JWKs.")
        errors.append(e)

    assert not errors, "errors occurred!"


def test_happy_path_self_signed():
    errors = []
    force = True

    output = make_temp_dir()
    # generate self-signed certificate
    private_key, certificate = pkcs.create_self_signed_cert()
    certificate_chain = []

    # write jwk
    try:
        pkcs.write_jwk_files(private_key, certificate, certificate_chain, output, force)
    except Exception as e:
        errors.append("Error generating JWKs.")
        errors.append(e)

    assert not errors, "errors occurred!"


def test_certificate_verification():
    errors = []

    env = "test"
    os.environ["PKCS12_CONTAINER_PASS"] = os.getenv("KEYSTORE_5767_PASS")

    try:
        (
            private_key,
            certificate,
            certificate_chain,
        ) = pkcs.read_pkcs12(VALID_KEYSTORE, env)
    except Exception:
        pytest.fail("Error reading in the certificate.")

    # verify chain
    if not verify.verify_certificate_chain(certificate, certificate_chain, env):
        errors.append("Error verifying certificate chain.")

    # check key usage
    if not verify.check_cert_key_usage(certificate):
        errors.append("Error verifying key usage.")

    # check key length
    if not verify.check_key_length(certificate):
        errors.append("Error verifying key length.")

    # check signature and hash algorithms
    if not verify.verify_certificate_algorithms(certificate):
        errors.append("Error verifying signature and hash algorithms.")

    assert not errors, "errors occurred!"


def test_wrong_key_length():
    # create self-signed cert with wrong key length
    keypair = openssl_crypto.PKey()
    keypair.generate_key(openssl_crypto.TYPE_RSA, 2048)

    cert = openssl_crypto.X509()
    cert.get_subject().C = "DE"
    cert.get_subject().O = "Testbehoerde"
    cert.get_subject().CN = "FIT Connect Testzertifikat"
    cert.set_serial_number(random.randint(50000000, 100000000))
    cert.gmtime_adj_notBefore(0)
    cert.gmtime_adj_notAfter(10 * 365 * 24 * 60 * 60)
    cert.set_issuer(cert.get_subject())
    cert.set_pubkey(keypair)
    cert.sign(keypair, "sha512")

    # expect error during key length verification
    with pytest.raises(KeyLengthError):
        pkcs.write_jwk_files(keypair, cert, [], make_temp_dir(), True)


def test_happy_path_self_signed_with_real_ca():
    errors = []

    path = "trusted-root-certificates/unit-tests/keyStore.p12"
    env = "unit-test"

    force = True

    output = make_temp_dir()
    os.environ["PKCS12_CONTAINER_PASS"] = "123456789"
    try:
        (
            private_key,
            certificate,
            certificate_chain,
        ) = pkcs.read_pkcs12(path, env)
    except Exception:
        pytest.fail("Error reading in the certificate.")

    # write jwk
    try:
        pkcs.write_jwk_files(private_key, certificate, certificate_chain, output, force)
    except Exception as e:
        errors.append("Error generating JWKs.")
        errors.append(e)

    assert not errors, "errors occurred!"


def test_keystore_wrong_key_length():
    path = "trusted-root-certificates/unit-tests/keyStore_wrong_key_size.p12"
    env = "unit-test"

    force = True

    output = make_temp_dir()
    os.environ["PKCS12_CONTAINER_PASS"] = "123456789"

    try:
        (
            private_key,
            certificate,
            certificate_chain,
        ) = pkcs.read_pkcs12(path, env)
    except Exception:
        pytest.fail("Error reading in the certificate.")

    # write jwk -> Should fail due to incorrect key length
    with pytest.raises(KeyLengthError):
        pkcs.write_jwk_files(private_key, certificate, certificate_chain, output, force)


def test_reversed_certificate_chain():
    errors = []

    force = True

    output = make_temp_dir()
    env = "test"
    os.environ["PKCS12_CONTAINER_PASS"] = os.getenv("KEYSTORE_5767_PASS")

    # read pkcs12 container with reverse order of certificates (leaf, root, ca instead of leaf, ca, root). This should be auto-detected by `read_pkcs12`.
    try:
        (
            private_key,
            certificate,
            certificate_chain,
        ) = pkcs.read_pkcs12(REVERSED_KEYSTORE, env)
    except Exception:
        pytest.fail("Error reading in the certificate.")

    # write jwk
    try:
        pkcs.write_jwk_files(private_key, certificate, certificate_chain, output, force)
    except Exception as e:
        errors.append("Error generating JWKs.")
        errors.append(e)

    assert not errors, "errors occurred!"