from collections.abc import Callable
from uuid import UUID

from config.env import env
from tasks.jobs.attack_paths import provider_config as _provider_config

# Re-export provider config objects so existing imports keep working.
AWS_CONFIG = _provider_config.AWS_CONFIG
NormalizedList = _provider_config.NormalizedList
PROVIDER_CONFIGS = _provider_config.PROVIDER_CONFIGS
ProviderConfig = _provider_config.ProviderConfig

# Batch size for graph mutation operations (resource labeling and subgraph deletion)
GRAPH_MUTATION_BATCH_SIZE = env.int("ATTACK_PATHS_GRAPH_MUTATION_BATCH_SIZE", 1000)
# Batch size for Postgres findings fetch (keyset pagination page size)
FINDINGS_BATCH_SIZE = env.int("ATTACK_PATHS_FINDINGS_BATCH_SIZE", 1000)
# Neo4j internal labels (Prowler-specific, not provider-specific)
# - `Internet`: Singleton node representing external internet access for exposed-resource queries
# - `ProwlerFinding`: Label for finding nodes created by Prowler and linked to cloud resources
# - `_ProviderResource`: Added to ALL synced nodes for provider isolation and drop/query ops
INTERNET_NODE_LABEL = "Internet"
PROWLER_FINDING_LABEL = "ProwlerFinding"
PROVIDER_RESOURCE_LABEL = "_ProviderResource"

# Dynamic isolation labels that contain entity UUIDs and are added to every synced node during sync
# Format: `_Tenant_{uuid_no_hyphens}`, `_Provider_{uuid_no_hyphens}`
TENANT_LABEL_PREFIX = "_Tenant_"
PROVIDER_LABEL_PREFIX = "_Provider_"
DYNAMIC_ISOLATION_PREFIXES = [TENANT_LABEL_PREFIX, PROVIDER_LABEL_PREFIX]


# Labels added by Prowler that should be filtered from API responses
# Derived from provider configs + common internal labels
INTERNAL_LABELS: list[str] = [
    "Tenant",  # From Cartography, but it looks like it's ours
    PROVIDER_RESOURCE_LABEL,
    *[config.resource_label for config in PROVIDER_CONFIGS.values()],
]

# Provider isolation properties
PROVIDER_ELEMENT_ID_PROPERTY = "_provider_element_id"

PROVIDER_ISOLATION_PROPERTIES: list[str] = [
    PROVIDER_ELEMENT_ID_PROPERTY,
]

# Cartography bookkeeping metadata
CARTOGRAPHY_METADATA_PROPERTIES: list[str] = [
    "lastupdated",
    "firstseen",
    "_module_name",
    "_module_version",
]

INTERNAL_PROPERTIES: list[str] = [
    *PROVIDER_ISOLATION_PROPERTIES,
    *CARTOGRAPHY_METADATA_PROPERTIES,
]


# Provider Config Accessors


def is_provider_available(provider_type: str) -> bool:
    """Check if a provider type is available for Attack Paths scans."""
    return provider_type in PROVIDER_CONFIGS


def get_cartography_ingestion_function(provider_type: str) -> Callable | None:
    """Get the Cartography ingestion function for a provider type."""
    config = PROVIDER_CONFIGS.get(provider_type)
    return config.ingestion_function if config else None


def get_root_node_label(provider_type: str) -> str:
    """Get the root node label for a provider type (e.g., AWSAccount)."""
    config = PROVIDER_CONFIGS.get(provider_type)
    return config.root_node_label if config else "UnknownProviderAccount"


def get_node_uid_field(provider_type: str) -> str:
    """Get the UID field for a provider type (e.g., arn for AWS)."""
    config = PROVIDER_CONFIGS.get(provider_type)
    return config.uid_field if config else "UnknownProviderUID"


def get_provider_resource_label(provider_type: str) -> str:
    """Get the resource label for a provider type (e.g., `_AWSResource`)."""
    config = PROVIDER_CONFIGS.get(provider_type)
    return config.resource_label if config else "_UnknownProviderResource"


def _identity_short_uid(uid: str) -> str:
    """Fallback short-uid extractor for providers without a custom mapping."""
    return uid


def get_short_uid_extractor(provider_type: str) -> Callable[[str], str]:
    """Get the short-uid extractor for a provider type.

    Returns an identity function when the provider is unknown, so callers can
    rely on a callable always being returned.
    """
    config = PROVIDER_CONFIGS.get(provider_type)
    return config.short_uid_extractor if config else _identity_short_uid


# Dynamic Isolation Label Helpers


def _normalize_uuid(value: str | UUID) -> str:
    """Strip hyphens from a UUID string for use in Neo4j labels."""
    return str(value).replace("-", "")


def get_tenant_label(tenant_id: str | UUID) -> str:
    """Get the Neo4j label for a tenant (e.g., `_Tenant_019c41ee7df37deca684d839f95619f8`)."""
    return f"{TENANT_LABEL_PREFIX}{_normalize_uuid(tenant_id)}"


def get_provider_label(provider_id: str | UUID) -> str:
    """Get the Neo4j label for a provider (e.g., `_Provider_019c41ee7df37deca684d839f95619f8`)."""
    return f"{PROVIDER_LABEL_PREFIX}{_normalize_uuid(provider_id)}"


def is_dynamic_isolation_label(label: str) -> bool:
    """Check if a label is a dynamic tenant/provider isolation label."""
    return any(label.startswith(prefix) for prefix in DYNAMIC_ISOLATION_PREFIXES)
