import datetime
import math
import numpy
import pathlib
import random
import string

from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID
from typing import Tuple


def random_string(length=5):
    multiplier = math.ceil(length/len(string.ascii_letters))
    return "".join(random.sample(string.ascii_letters*multiplier, length))


def random_bytes(length=1024):
    return bytes(random_string(length), "utf-8")


def random_dict(items=10):
    return {random_string(): random_string() for _ in range(items)}


def random_array(length=10) -> list:
    return numpy.random.uniform(0, 1, size=length).tolist()


def create_self_signed_cert(path: pathlib.Path) -> Tuple[pathlib.Path,
                                                         pathlib.Path]:
    key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048,
        backend=default_backend(),
    )

    subject = issuer = x509.Name([
        x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
        x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "California"),
        x509.NameAttribute(NameOID.LOCALITY_NAME, "San Francisco"),
        x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Test Company"),
        x509.NameAttribute(NameOID.COMMON_NAME, "test.org"),
    ])

    cert = x509.CertificateBuilder().subject_name(
        subject
    ).issuer_name(
        issuer
    ).public_key(
        key.public_key()
    ).serial_number(
        x509.random_serial_number()
    ).not_valid_before(
        datetime.datetime.utcnow()
    ).not_valid_after(
        # Certificate will be valid for 10 days
        datetime.datetime.utcnow() + datetime.timedelta(days=10)
    ).add_extension(
        x509.SubjectAlternativeName([x509.DNSName(u"localhost")]),
        critical=False,  # Sign certificate with our private key.
    ).sign(key, hashes.SHA256(), default_backend())

    keypath = path.joinpath("key.pem")
    with open(path.joinpath(keypath), "wb") as key_pem:
        key_pem.write(key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.TraditionalOpenSSL,
            encryption_algorithm=serialization.NoEncryption(),
        ))

    certpath = path.joinpath("cert.pem")
    with open(path.joinpath(certpath), "wb") as cert_pem:
        cert_pem.write(cert.public_bytes(serialization.Encoding.PEM))

    return keypath, certpath
