"""
Python interface to EasyRSA CA.
"""

import subprocess
from datetime import datetime
from pathlib import Path

from OpenSSL import crypto
from passlib import pwd

from .config import Config, Settings


class EasyRSA:
    """
    Represents an EasyRSA PKI.
    """

    @property
    def pki_directory(self) -> Path:
        return Settings._.data_dir.joinpath("pki")

    @property
    def ca_password(self) -> str:
        config = Config._

        if (ca_password := config.crypto.ca_password) is None:
            ca_password = pwd.genword(
                length=32,
                charset="ascii_62",
            )

            config.crypto.ca_password = ca_password
            config.save()

        return config.crypto.ca_password

    def __easyrsa(
        self,
        *easyrsa_args: str,
    ) -> subprocess.CompletedProcess:
        return subprocess.run(
            [
                "easyrsa", "--batch",
                f"--pki-dir={self.pki_directory}",
                *easyrsa_args,
            ],
            stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
            check=True,
        )

    def __build_cert(
        self,
        cert_filename: Path,
        *easyrsa_args: str,
    ) -> crypto.X509:
        self.__easyrsa(*easyrsa_args)

        with open(
            self.pki_directory.joinpath(cert_filename), "r"
        ) as cert_file:
            return crypto.load_certificate(
                crypto.FILETYPE_PEM, cert_file.read()
            )

    def init_pki(self) -> bool:
        self.__easyrsa("init-pki")

    def build_ca(
        self,
    ) -> crypto.X509:
        config = Config._

        cert = self.__build_cert(
            Path("ca.crt"),

            f"--passout=pass:{self.ca_password}",
            f"--passin=pass:{self.ca_password}",

            # "--dn-mode=org",
            # "--req-c=EX",
            # "--req-st=EXAMPLE",
            # "--req-city=EXAMPLE",
            # "--req-org=EXAMPLE",
            # "--req-ou=EXAMPLE",
            # "--req-email=EXAMPLE",

            f"--req-cn={config.server_name}",
            f"--days={config.crypto.expiry_days}",

            # "--use-algo=ed",
            # "--curve=ed25519",

            "build-ca",
        )

        # self.__easyrsa("gen-dh")
        return cert

    def issue(
        self,
        cn: str = "kiwi-vpn-client",
        cert_type: str = "client"
    ) -> crypto.X509:
        config = Config._

        return self.__build_cert(
            Path(f"issued/{cn}.crt"),

            f"--passin=pass:{self.ca_password}",
            f"--days={config.crypto.expiry_days}",

            f"build-{cert_type}-full",
            cn,
            "nopass",
        )


if __name__ == "__main__":
    easy_rsa = EasyRSA()
    easy_rsa.init_pki()

    ca = easy_rsa.build_ca()
    server = easy_rsa.issue(cert_type="server", cn="kiwi-vpn-server")
    client = easy_rsa.issue(cert_type="client", cn="kiwi-vpn-client")

    date_format, encoding = "%Y%m%d%H%M%SZ", "ascii"

    for cert in [ca, server, client]:
        print(cert.get_subject().CN)
        print(cert.get_signature_algorithm().decode(encoding))
        print(datetime.strptime(
            cert.get_notAfter().decode(encoding), date_format))