"""Relationship builders for Defender for Cloud security findings."""

from typing import List

from ..adt_types.models import ResourceNode, Relationship
from .logging import get_logger

LOGGER = get_logger()


def build_defender_relationships(
    alerts: List[ResourceNode],
    assessments: List[ResourceNode],
    all_nodes: List[ResourceNode],
    materialize_missing: bool = False,
) -> List[Relationship]:
    """Build relationships between security findings and affected resources.

    Args:
        alerts: List of security alert nodes
        assessments: List of security assessment nodes
        all_nodes: All discovered nodes (for ID lookup)
        materialize_missing: Whether to create stub nodes for missing resources

    Returns:
        List of Relationship objects
    """
    relationships = []

    # Build alert → resource relationships
    relationships.extend(
        _build_alert_relationships(alerts, all_nodes, materialize_missing)
    )

    # Build assessment → resource relationships
    relationships.extend(
        _build_assessment_relationships(assessments, all_nodes, materialize_missing)
    )

    LOGGER.info(
        "Defender relationship building complete",
        extra={"context": {"total_relationships": len(relationships)}},
    )

    return relationships


def _build_alert_relationships(
    alerts: List[ResourceNode],
    all_nodes: List[ResourceNode],
    materialize_missing: bool,
) -> List[Relationship]:
    """Build relationships from security alerts to affected resources."""
    relationships = []
    node_id_map = {node.id: node for node in all_nodes}

    for alert in alerts:
        if alert.type != "Microsoft.Security/alerts":
            continue

        # Extract affected resources from alert properties
        affected_resources = alert.properties.get("affectedResources", [])

        for resource_id in affected_resources:
            if not resource_id:
                continue

            # Check if the target resource exists in our graph
            target_node = node_id_map.get(resource_id)

            if not target_node and materialize_missing:
                # Create a stub node for the missing resource
                target_node = ResourceNode(
                    id=resource_id,
                    name=resource_id.split("/")[-1],
                    type="Microsoft.Unknown/unknownResource",
                    subscription_id=alert.subscription_id,
                    location=None,
                    properties={"materialized": True, "reason": "defender_alert"},
                    tags={},
                )
                all_nodes.append(target_node)
                node_id_map[resource_id] = target_node

            if target_node:
                relationships.append(
                    Relationship(
                        source_id=alert.id,
                        target_id=resource_id,
                        relationship_type="affects",
                        properties={
                            "severity": alert.properties.get("severity"),
                            "status": alert.properties.get("status"),
                            "alertType": alert.properties.get("alertType"),
                        },
                    )
                )

    LOGGER.debug(
        "Built alert relationships",
        extra={"context": {"alert_count": len(alerts), "relationships": len(relationships)}},
    )

    return relationships


def _build_assessment_relationships(
    assessments: List[ResourceNode],
    all_nodes: List[ResourceNode],
    materialize_missing: bool,
) -> List[Relationship]:
    """Build relationships from security assessments to affected resources."""
    relationships = []
    node_id_map = {node.id: node for node in all_nodes}

    for assessment in assessments:
        if assessment.type != "Microsoft.Security/assessments":
            continue

        # Extract affected resource from assessment properties
        affected_resource_id = assessment.properties.get("affectedResourceId")

        if not affected_resource_id:
            # Some assessments are scoped to the subscription itself
            # In this case, we can create a relationship to the subscription
            if assessment.subscription_id:
                subscription_id = f"/subscriptions/{assessment.subscription_id}"
                # Check if we have a subscription node
                subscription_node = node_id_map.get(subscription_id)

                if subscription_node:
                    relationships.append(
                        Relationship(
                            source_id=assessment.id,
                            target_id=subscription_id,
                            relationship_type="affects",
                            properties={
                                "severity": assessment.properties.get("severity"),
                                "statusCode": assessment.properties.get("statusCode"),
                                "assessmentType": assessment.properties.get(
                                    "assessmentType"
                                ),
                            },
                        )
                    )
            continue

        # Check if the target resource exists in our graph
        target_node = node_id_map.get(affected_resource_id)

        if not target_node and materialize_missing:
            # Create a stub node for the missing resource
            target_node = ResourceNode(
                id=affected_resource_id,
                name=affected_resource_id.split("/")[-1],
                type="Microsoft.Unknown/unknownResource",
                subscription_id=assessment.subscription_id,
                location=None,
                properties={"materialized": True, "reason": "defender_assessment"},
                tags={},
            )
            all_nodes.append(target_node)
            node_id_map[affected_resource_id] = target_node

        if target_node:
            relationships.append(
                Relationship(
                    source_id=assessment.id,
                    target_id=affected_resource_id,
                    relationship_type="affects",
                    properties={
                        "severity": assessment.properties.get("severity"),
                        "statusCode": assessment.properties.get("statusCode"),
                        "assessmentType": assessment.properties.get("assessmentType"),
                    },
                )
            )

    LOGGER.debug(
        "Built assessment relationships",
        extra={
            "context": {
                "assessment_count": len(assessments),
                "relationships": len(relationships),
            }
        },
    )

    return relationships


def get_high_severity_alerts(alerts: List[ResourceNode]) -> List[ResourceNode]:
    """Filter alerts to only high and critical severity.

    Args:
        alerts: List of security alert nodes

    Returns:
        List of high/critical severity alerts
    """
    high_severity = ["High", "Critical"]
    return [
        alert
        for alert in alerts
        if alert.type == "Microsoft.Security/alerts"
        and alert.properties.get("severity") in high_severity
    ]


def get_active_alerts(alerts: List[ResourceNode]) -> List[ResourceNode]:
    """Filter alerts to only active (unresolved) alerts.

    Args:
        alerts: List of security alert nodes

    Returns:
        List of active alerts
    """
    return [
        alert
        for alert in alerts
        if alert.type == "Microsoft.Security/alerts"
        and alert.properties.get("status") == "Active"
    ]


def get_unhealthy_assessments(assessments: List[ResourceNode]) -> List[ResourceNode]:
    """Filter assessments to only unhealthy findings.

    Args:
        assessments: List of security assessment nodes

    Returns:
        List of unhealthy assessments
    """
    return [
        assessment
        for assessment in assessments
        if assessment.type == "Microsoft.Security/assessments"
        and assessment.properties.get("statusCode") == "Unhealthy"
    ]
