"""Normalize tri-cloud events into NormalizedDriftEvent.

Supports GCP Cloud Asset Inventory, AWS EventBridge Config CI,
and Azure Event Grid activity log events.
"""

from __future__ import annotations

import logging
import re

from backend.drift.models import NormalizedDriftEvent

_log = logging.getLogger(__name__)

# ---------------------------------------------------------------------------
# Resource type mapping dicts (cloud type → Terraform type)
# ---------------------------------------------------------------------------

GCP_RESOURCE_MAP: dict[str, str] = {
    "compute.googleapis.com/Instance": "google_compute_instance",
    "compute.googleapis.com/Disk": "google_compute_disk",
    "compute.googleapis.com/Network": "google_compute_network",
    "compute.googleapis.com/Subnetwork": "google_compute_subnetwork",
    "compute.googleapis.com/Firewall": "google_compute_firewall",
    "compute.googleapis.com/Address": "google_compute_address",
    "compute.googleapis.com/ForwardingRule": "google_compute_forwarding_rule",
    "compute.googleapis.com/HealthCheck": "google_compute_health_check",
    "compute.googleapis.com/InstanceGroup": "google_compute_instance_group",
    "compute.googleapis.com/InstanceTemplate": "google_compute_instance_template",
    "storage.googleapis.com/Bucket": "google_storage_bucket",
    "sqladmin.googleapis.com/Instance": "google_sql_database_instance",
    "container.googleapis.com/Cluster": "google_container_cluster",
    "container.googleapis.com/NodePool": "google_container_node_pool",
    "iam.googleapis.com/ServiceAccount": "google_service_account",
    "iam.googleapis.com/ServiceAccountKey": "google_service_account_key",
    "cloudkms.googleapis.com/CryptoKey": "google_kms_crypto_key",
    "cloudkms.googleapis.com/KeyRing": "google_kms_key_ring",
    "dns.googleapis.com/ManagedZone": "google_dns_managed_zone",
    "pubsub.googleapis.com/Topic": "google_pubsub_topic",
    "pubsub.googleapis.com/Subscription": "google_pubsub_subscription",
    "bigquery.googleapis.com/Dataset": "google_bigquery_dataset",
    "bigquery.googleapis.com/Table": "google_bigquery_table",
    "logging.googleapis.com/LogSink": "google_logging_project_sink",
    "monitoring.googleapis.com/AlertPolicy": "google_monitoring_alert_policy",
    "cloudfunctions.googleapis.com/Function": "google_cloudfunctions_function",
    "run.googleapis.com/Service": "google_cloud_run_service",
    "redis.googleapis.com/Instance": "google_redis_instance",
    "spanner.googleapis.com/Instance": "google_spanner_instance",
    "secretmanager.googleapis.com/Secret": "google_secret_manager_secret",
}

AWS_RESOURCE_MAP: dict[str, str] = {
    "AWS::EC2::Instance": "aws_instance",
    "AWS::EC2::VPC": "aws_vpc",
    "AWS::EC2::Subnet": "aws_subnet",
    "AWS::EC2::SecurityGroup": "aws_security_group",
    "AWS::EC2::NetworkInterface": "aws_network_interface",
    "AWS::EC2::Volume": "aws_ebs_volume",
    "AWS::EC2::EIP": "aws_eip",
    "AWS::EC2::InternetGateway": "aws_internet_gateway",
    "AWS::EC2::NatGateway": "aws_nat_gateway",
    "AWS::EC2::RouteTable": "aws_route_table",
    "AWS::S3::Bucket": "aws_s3_bucket",
    "AWS::RDS::DBInstance": "aws_db_instance",
    "AWS::RDS::DBCluster": "aws_rds_cluster",
    "AWS::Lambda::Function": "aws_lambda_function",
    "AWS::IAM::Role": "aws_iam_role",
    "AWS::IAM::Policy": "aws_iam_policy",
    "AWS::IAM::User": "aws_iam_user",
    "AWS::KMS::Key": "aws_kms_key",
    "AWS::ECS::Cluster": "aws_ecs_cluster",
    "AWS::ECS::Service": "aws_ecs_service",
    "AWS::EKS::Cluster": "aws_eks_cluster",
    "AWS::SNS::Topic": "aws_sns_topic",
    "AWS::SQS::Queue": "aws_sqs_queue",
    "AWS::DynamoDB::Table": "aws_dynamodb_table",
    "AWS::ElasticLoadBalancingV2::LoadBalancer": "aws_lb",
    "AWS::CloudFront::Distribution": "aws_cloudfront_distribution",
    "AWS::CloudTrail::Trail": "aws_cloudtrail",
    "AWS::CloudWatch::Alarm": "aws_cloudwatch_metric_alarm",
    "AWS::SecretsManager::Secret": "aws_secretsmanager_secret",
    "AWS::ElastiCache::ReplicationGroup": "aws_elasticache_replication_group",
}

AZURE_RESOURCE_MAP: dict[str, str] = {
    "Microsoft.Compute/virtualMachines": "azurerm_virtual_machine",
    "Microsoft.Compute/disks": "azurerm_managed_disk",
    "Microsoft.Network/virtualNetworks": "azurerm_virtual_network",
    "Microsoft.Network/networkSecurityGroups": "azurerm_network_security_group",
    "Microsoft.Network/publicIPAddresses": "azurerm_public_ip",
    "Microsoft.Network/loadBalancers": "azurerm_lb",
    "Microsoft.Network/applicationGateways": "azurerm_application_gateway",
    "Microsoft.Network/networkInterfaces": "azurerm_network_interface",
    "Microsoft.Storage/storageAccounts": "azurerm_storage_account",
    "Microsoft.Sql/servers": "azurerm_mssql_server",
    "Microsoft.Sql/servers/databases": "azurerm_mssql_database",
    "Microsoft.DBforPostgreSQL/flexibleServers": "azurerm_postgresql_flexible_server",
    "Microsoft.Web/sites": "azurerm_linux_web_app",
    "Microsoft.KeyVault/vaults": "azurerm_key_vault",
    "Microsoft.ContainerService/managedClusters": "azurerm_kubernetes_cluster",
    "Microsoft.ContainerRegistry/registries": "azurerm_container_registry",
    "Microsoft.ServiceBus/namespaces": "azurerm_servicebus_namespace",
    "Microsoft.EventHub/namespaces": "azurerm_eventhub_namespace",
    "Microsoft.Cache/Redis": "azurerm_redis_cache",
    "Microsoft.DocumentDB/databaseAccounts": "azurerm_cosmosdb_account",
    "Microsoft.Authorization/roleAssignments": "azurerm_role_assignment",
    "Microsoft.ManagedIdentity/userAssignedIdentities": (
        "azurerm_user_assigned_identity"
    ),
    "Microsoft.Insights/diagnosticSettings": "azurerm_monitor_diagnostic_setting",
    "Microsoft.Insights/activityLogAlerts": "azurerm_monitor_activity_log_alert",
    "Microsoft.Resources/resourceGroups": "azurerm_resource_group",
    "Microsoft.Logic/workflows": "azurerm_logic_app_workflow",
    "Microsoft.Cdn/profiles": "azurerm_cdn_profile",
    "Microsoft.Network/privateDnsZones": "azurerm_private_dns_zone",
    "Microsoft.Network/frontDoors": "azurerm_frontdoor",
    "Microsoft.OperationalInsights/workspaces": "azurerm_log_analytics_workspace",
}


# ---------------------------------------------------------------------------
# GCP Cloud Asset Inventory normalizer
# ---------------------------------------------------------------------------


def normalize_gcp_cai(event_data: dict) -> NormalizedDriftEvent:  # type: ignore[type-arg]
    """Normalize GCP Cloud Asset Inventory real-time feed event."""
    asset = event_data.get("asset")
    prior_asset = event_data.get("priorAsset")

    if asset is None and prior_asset is None:
        msg = "GCP CAI event missing both asset and priorAsset"
        raise ValueError(msg)

    # Determine change type
    if prior_asset is None:
        change_type: str = "create"
    elif asset is None:
        change_type = "delete"
    else:
        change_type = "update"

    # Extract from whichever asset is present
    active = asset or prior_asset
    asset_name: str = active.get("name", "")
    asset_type: str = active.get("assetType", "")

    # Extract project ID from asset name
    # Format: //service.googleapis.com/projects/{project_id}/...
    project_match = re.search(r"/projects/([^/]+)", asset_name)
    cloud_account_id = project_match.group(1) if project_match else ""

    resource_type = GCP_RESOURCE_MAP.get(asset_type)
    if resource_type is None:
        _log.warning("Unknown GCP resource type: %s — using raw", asset_type)
        resource_type = asset_type

    window = event_data.get("window", {})
    event_timestamp = window.get("startTime", "")

    return NormalizedDriftEvent(
        cloud="gcp",
        cloud_account_id=cloud_account_id,
        resource_type=resource_type,
        resource_id=asset_name,
        change_type=change_type,  # type: ignore[arg-type]
        event_timestamp=event_timestamp,
        raw_event=event_data,
    )


# ---------------------------------------------------------------------------
# AWS EventBridge Config CI normalizer
# ---------------------------------------------------------------------------


def normalize_aws_eventbridge(event_data: dict) -> NormalizedDriftEvent:  # type: ignore[type-arg]
    """Normalize AWS Config Configuration Item Change event."""
    detail = event_data.get("detail", {})
    config_item = detail.get("configurationItem", {})
    diff = detail.get("configurationItemDiff", {})

    aws_resource_type: str = config_item.get("resourceType", "")
    resource_type = AWS_RESOURCE_MAP.get(aws_resource_type)
    if resource_type is None:
        _log.warning("Unknown AWS resource type: %s — using raw", aws_resource_type)
        resource_type = aws_resource_type

    change_raw: str = diff.get("changeType", "UPDATE").upper()
    change_map = {"CREATE": "create", "UPDATE": "update", "DELETE": "delete"}
    change_type = change_map.get(change_raw, "update")

    return NormalizedDriftEvent(
        cloud="aws",
        cloud_account_id=config_item.get("awsAccountId", ""),
        resource_type=resource_type,
        resource_id=config_item.get("resourceId", ""),
        change_type=change_type,  # type: ignore[arg-type]
        event_timestamp=config_item.get("configurationItemCaptureTime", ""),
        raw_event=event_data,
    )


# ---------------------------------------------------------------------------
# Azure Event Grid normalizer
# ---------------------------------------------------------------------------


def normalize_azure_eventgrid(event_data: dict) -> NormalizedDriftEvent:  # type: ignore[type-arg]
    """Normalize Azure Event Grid activity log / policy event."""
    data = event_data.get("data", {})
    resource_uri: str = data.get("resourceUri", "")
    operation_name: str = data.get("operationName", "")

    # Extract subscription ID from resourceUri
    sub_match = re.search(r"/subscriptions/([^/]+)", resource_uri)
    cloud_account_id = sub_match.group(1) if sub_match else ""

    # Extract Azure resource type from operationName
    # Format: Microsoft.Compute/virtualMachines/write
    op_parts = operation_name.rsplit("/", 1)
    azure_type = op_parts[0] if op_parts else operation_name

    resource_type = AZURE_RESOURCE_MAP.get(azure_type)
    if resource_type is None:
        _log.warning("Unknown Azure resource type: %s — using raw", azure_type)
        resource_type = azure_type

    # Infer change type from operationName suffix
    event_type: str = event_data.get("eventType", "")
    if "delete" in operation_name.lower() or "Delete" in event_type:
        change_type = "delete"
    elif "Write" in event_type:
        change_type = "update"
    else:
        change_type = "update"

    return NormalizedDriftEvent(
        cloud="azure",
        cloud_account_id=cloud_account_id,
        resource_type=resource_type,
        resource_id=resource_uri,
        change_type=change_type,  # type: ignore[arg-type]
        event_timestamp=event_data.get("eventTime", ""),
        raw_event=event_data,
    )


# ---------------------------------------------------------------------------
# Dispatcher
# ---------------------------------------------------------------------------

_NORMALIZERS = {
    "gcp": normalize_gcp_cai,
    "aws": normalize_aws_eventbridge,
    "azure": normalize_azure_eventgrid,
}


def normalize_event(cloud: str, event_data: dict) -> NormalizedDriftEvent:  # type: ignore[type-arg]
    """Route to cloud-specific normalizer. Raises ValueError for unknown cloud."""
    normalizer = _NORMALIZERS.get(cloud)
    if normalizer is None:
        msg = f"Unknown cloud provider: {cloud}"
        raise ValueError(msg)
    return normalizer(event_data)
