import subprocess
import os
import tempfile
import shutil

def generate_root_ca(ca_dir, config_path, openssl_path, ca_basename="local-root"):
    """
    Generates a Root CA private key and self-signed certificate.
    
    Args:
        ca_dir (str): Path to the CA directory where keys/certs are stored
        config_path (str): Path to openssl.cnf file
        openssl_path (str): Path to openssl.exe
    """
    ca_key_path = os.path.join(ca_dir, f"{ca_basename}.key")
    ca_crt_path = os.path.join(ca_dir, f"{ca_basename}.crt")


    if os.path.exists(ca_key_path) or os.path.exists(ca_crt_path):
        return f"⚠️ '{ca_basename}' already exists. Delete files to recreate."


    try:
        # Step 1: Generate CA private key
        key_cmd = [
            openssl_path,
            "genrsa",
            "-out", ca_key_path,
            "4096"
        ]
        subprocess.run(key_cmd, check=True)

        # Step 2: Generate self-signed certificate
        cert_cmd = [
            openssl_path,
            "req",
            "-x509",
            "-new",
            "-nodes",
            "-key", ca_key_path,
            "-sha256",
            "-days", "825",
            "-out", ca_crt_path,
            "-config", config_path,
            "-extensions", "v3_ca"
        ]
        subprocess.run(cert_cmd, check=True)

        return "✅ Root CA generated successfully."

    except subprocess.CalledProcessError as e:
        return f"❌ OpenSSL error: {e}"
    except Exception as ex:
        return f"❌ Unexpected error: {ex}"

def generate_certificate(ca_path, cert_output_path, config_path, openssl_path, cert_name, san_list, validity_days=365, ca_basename="TestRootCA"):

    if cert_output_path is None or cert_name is None:
        return "❌ Internal error: Missing cert output path or name."

    os.makedirs(cert_output_path, exist_ok=True)

    ca_key_path = os.path.join(ca_path, f"{ca_basename}.key")
    ca_crt_path = os.path.join(ca_path, f"{ca_basename}.crt")

    cert_key_path = os.path.join(cert_output_path, f"{cert_name}.key")
    cert_csr_path = os.path.join(cert_output_path, f"{cert_name}.csr")
    cert_crt_path = os.path.join(cert_output_path, f"{cert_name}.crt")

    if os.path.exists(cert_key_path) or os.path.exists(cert_crt_path):
        return f"⚠️ Certificate for '{cert_name}' already exists. Delete files to recreate."

    try:
        # Step 1: Generate private key
        key_cmd = [
            openssl_path,
            "genrsa",
            "-out", cert_key_path,
            "2048"
        ]
        subprocess.run(key_cmd, check=True)

        # Step 2: Prepare a temp openssl config with SAN extension
        with open(config_path, "r") as f:
            base_config = f.read()

        san_entries = []
        for san in san_list:
            san = san.strip()
            if san.replace('.', '').isdigit():  # crude IP check
                san_entries.append(f"IP:{san}")
            else:
                san_entries.append(f"DNS:{san}")
        san_str = ",".join(san_entries)

        # Append v3_req section with SAN
        temp_config = base_config + f"\n[v3_req]\nsubjectAltName={san_str}\n"

        with tempfile.NamedTemporaryFile("w", delete=False) as temp_conf_file:
            temp_conf_file.write(temp_config)
            temp_conf_path = temp_conf_file.name

        # Step 3: Generate CSR with SAN extension
        csr_cmd = [
            openssl_path,
            "req",
            "-new",
            "-key", cert_key_path,
            "-out", cert_csr_path,
            "-subj", f"/CN={cert_name}",
            "-config", temp_conf_path,
            "-reqexts", "v3_req"
        ]
        subprocess.run(csr_cmd, check=True)

        # Step 4: Sign CSR with CA key, generating cert with SAN extension
        cert_cmd = [
            openssl_path,
            "x509",
            "-req",
            "-in", cert_csr_path,
            "-CA", ca_crt_path,
            "-CAkey", ca_key_path,
            "-CAcreateserial",
            "-out", cert_crt_path,
            "-days", str(validity_days),
            "-sha256",
            "-extfile", temp_conf_path,
            "-extensions", "v3_req"
        ]
        subprocess.run(cert_cmd, check=True)

        # Clean up temp config file
        os.remove(temp_conf_path)

        return f"✅ Certificate for '{cert_name}' generated successfully."

    except subprocess.CalledProcessError as e:
        return f"❌ OpenSSL error: {e}"
    except Exception as ex:
        return f"❌ Unexpected error: {ex}"