import os
import yaml
import json
import logging
import ipaddress
from datetime import timedelta

from google.cloud import secretmanager, certificate_manager_v1
import google.cloud.logging
from google.api_core.exceptions import NotFound, AlreadyExists
from google.cloud.security import privateca_v1

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



# Initialize Google Cloud Logging
client = google.cloud.logging.Client()
client.setup_logging()

# Configure the logger
logger = logging.getLogger("uvicorn")


def create_secret(project_id, secret_name, secret_value, region):
    client = secretmanager.SecretManagerServiceClient()
    parent = f"projects/{project_id}"
    secret_path = f"projects/{project_id}/secrets/{secret_name}"

    try: 
        client.get_secret(secretmanager.GetSecretRequest(name=secret_path))
        logging.info(f"Secret '{secret_name}' already exists. Skipping creation.")
        return
    except NotFound:
        pass  # Secret doesn't exist, continue with creation

    create_secret_request = secretmanager.CreateSecretRequest(
        parent=parent,
        secret_id=secret_name,
        secret=secretmanager.Secret(
            replication=secretmanager.Replication(
                user_managed=secretmanager.Replication.UserManaged(
                    replicas=[
                        secretmanager.Replication.UserManaged.Replica(
                            location=region
                        )
                    ]
                )
            )
        )
    )

    try:
        secret = client.create_secret(request=create_secret_request)
    except Exception as e:
        logging.error(f"Error creating secret '{secret_name}': {str(e)}")
        return

    add_secret_version_request = secretmanager.AddSecretVersionRequest(
        parent=secret_path,
        payload=secretmanager.SecretPayload(
            data=secret_value.encode("UTF-8")
        )
    )

    try:
        client.add_secret_version(request=add_secret_version_request)
    except Exception as e:
        logging.error(f"Error creating secret value for '{secret_name}': {str(e)}")
        return


def generate_csr(identifier: str) -> tuple[str, str]:
    """
    Generate a CSR and private key using cryptography library.
    Automatically detects whether identifier is an IP address or DNS name.
    
    Args:
        identifier: Either a DNS name or IP address
    
    Returns:
        A tuple of (csr_pem, key_pem) as strings.
    """
    # Detect if identifier is an IP address
    is_ip = False
    try:
        ip_obj = ipaddress.ip_address(identifier)
        is_ip = True
    except ValueError:
        is_ip = False
    
    # Generate private key
    private_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048,
        backend=default_backend()
    )
    
    # Build the subject
    subject = x509.Name([
        x509.NameAttribute(NameOID.COMMON_NAME, identifier)
    ])
    
    # Build Subject Alternative Name extension
    if is_ip:
        # For IP addresses
        san_list = [x509.IPAddress(ip_obj)]
    else:
        # For DNS names (includes wildcard)
        san_list = [
            x509.DNSName(identifier),
            x509.DNSName(f"*.{identifier}")
        ]
    
    # Create CSR
    csr = x509.CertificateSigningRequestBuilder().subject_name(
        subject
    ).add_extension(
        x509.SubjectAlternativeName(san_list),
        critical=False
    ).sign(private_key, hashes.SHA256(), backend=default_backend())
    
    # Serialize to PEM format
    csr_pem = csr.public_bytes(serialization.Encoding.PEM).decode('utf-8')
    key_pem = private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption()
    ).decode('utf-8')
    
    identifier_type = "IP address" if is_ip else "DNS name"
    logger.info(f"Generated CSR and private key for {identifier_type}: {identifier}")
    return csr_pem, key_pem


def request_certificate_from_ca(ca_project_id: str, csr_pem: str, ca_pool: str, region: str) -> str:
    """
    Request a certificate from Google Cloud Private CA using the CSR.
    Returns the issued certificate in PEM format as a string.
    """
    client = privateca_v1.CertificateAuthorityServiceClient()
    
    # Build the CA pool parent path
    # Format: projects/{project}/locations/{location}/caPools/{ca_pool}
    ca_pool_path = client.ca_pool_path(
        project=ca_project_id,
        location=region,
        ca_pool=ca_pool
    )
    
    # Create the certificate request
    certificate = privateca_v1.Certificate(
        lifetime=timedelta(days=365),  # 1 year validity (P1Y)
        pem_csr=csr_pem
    )
    
    request = privateca_v1.CreateCertificateRequest(
        parent=ca_pool_path,
        certificate=certificate
    )
    
    try:
        # Request the certificate
        response = client.create_certificate(request=request)
        cert_pem = response.pem_certificate
        
        logger.info(f"Successfully requested certificate from CA")
        return cert_pem
        
    except Exception as e:
        logger.error(f"Error requesting certificate from CA: {str(e)}")
        raise


def upload_certificate_into_certificate_manager(project_id: str, cert_name: str, cert_pem: str, key_pem: str) -> None:
    """
    Create a certificate in Google Cloud Certificate Manager.
    """
    client = certificate_manager_v1.CertificateManagerClient()
    
    # Build the parent path
    parent = f"projects/{project_id}/locations/global"
    certificate_path = f"{parent}/certificates/{cert_name}"
    
    # Check if certificate already exists
    try:
        client.get_certificate(name=certificate_path)
        logger.info(f"Certificate '{cert_name}' already exists in Certificate Manager. Skipping creation.")
        return
    except NotFound:
        pass  # Certificate doesn't exist, continue with creation
    
    # Create the certificate with self-managed certificate data
    certificate = certificate_manager_v1.Certificate(
        name=certificate_path,
        self_managed=certificate_manager_v1.Certificate.SelfManagedCertificate(
            pem_certificate=cert_pem,
            pem_private_key=key_pem
        )
    )
    
    request = certificate_manager_v1.CreateCertificateRequest(
        parent=parent,
        certificate_id=cert_name,
        certificate=certificate
    )
    
    try:
        operation = client.create_certificate(request=request)
        # Wait for the operation to complete
        result = operation.result()
        logger.info(f"Successfully created certificate '{cert_name}' in Certificate Manager")
    except AlreadyExists:
        logger.info(f"Certificate '{cert_name}' already exists in Certificate Manager")
    except Exception as e:
        logger.error(f"Error creating certificate in Certificate Manager: {str(e)}")
        raise