"""Helper functions for M365 relationship building."""

import logging
from typing import Dict, List, Set

from azure_discovery.adt_types.models import ResourceNode, ResourceRelationship

logger = logging.getLogger(__name__)


def build_m365_relationships(
    m365_nodes: List[ResourceNode],
    all_nodes: List[ResourceNode],
    materialize_missing: bool = False,
) -> tuple[List[ResourceRelationship], List[ResourceNode]]:
    """Build relationships between M365 resources and Entra ID principals.

    Creates relationships:
    - SharePoint site → owner (User)
    - Team → member (User)
    - OneDrive drive → owner (User)
    - Exchange mailbox → owner (User)

    Args:
        m365_nodes: List of M365 resource nodes
        all_nodes: All discovered nodes (for cross-referencing)
        materialize_missing: If True, create placeholder nodes for missing principals

    Returns:
        Tuple of (relationships, materialized nodes)
    """
    logger.info(f"Building M365 relationships for {len(m365_nodes)} M365 resources")

    relationships: List[ResourceRelationship] = []
    materialized_nodes: List[ResourceNode] = []
    known_node_ids = {node.id for node in all_nodes}

    for node in m365_nodes:
        node_type = node.type

        # SharePoint sites → owner
        if node_type == "Microsoft.SharePoint/sites":
            rels, materialized = _build_sharepoint_relationships(node, known_node_ids, materialize_missing)
            relationships.extend(rels)
            materialized_nodes.extend(materialized)

        # Teams → members
        elif node_type == "Microsoft.Teams/teams":
            rels, materialized = _build_team_relationships(node, known_node_ids, materialize_missing)
            relationships.extend(rels)
            materialized_nodes.extend(materialized)

        # OneDrive drives → owner
        elif node_type == "Microsoft.OneDrive/drives":
            rels, materialized = _build_onedrive_relationships(node, known_node_ids, materialize_missing)
            relationships.extend(rels)
            materialized_nodes.extend(materialized)

        # Exchange mailboxes → owner
        elif node_type == "Microsoft.Exchange/mailboxes":
            rels, materialized = _build_exchange_relationships(node, known_node_ids, materialize_missing)
            relationships.extend(rels)
            materialized_nodes.extend(materialized)

    logger.info(
        f"Built {len(relationships)} M365 relationships "
        f"({len(materialized_nodes)} materialized endpoints)"
    )

    return relationships, materialized_nodes


def _build_sharepoint_relationships(
    site_node: ResourceNode,
    known_node_ids: Set[str],
    materialize_missing: bool,
) -> tuple[List[ResourceRelationship], List[ResourceNode]]:
    """Build relationships for SharePoint sites.

    Args:
        site_node: SharePoint site node
        known_node_ids: Set of known node IDs
        materialize_missing: Whether to create placeholder nodes

    Returns:
        Tuple of (relationships, materialized nodes)
    """
    relationships: List[ResourceRelationship] = []
    materialized: List[ResourceNode] = []

    # SharePoint sites don't have direct owner relationships in the basic site object
    # Owners are retrieved from the site's /owners endpoint
    # For now, we'll extract owner info if it exists in properties

    owner_upn = site_node.properties.get("owner_upn")
    if owner_upn:
        owner_id = f"https://graph.microsoft.com/v1.0/users/{owner_upn}"

        if owner_id not in known_node_ids and materialize_missing:
            # Materialize missing user
            materialized_user = ResourceNode(
                id=owner_id,
                name=owner_upn,
                type="Microsoft.Graph/users",
                subscription_id=site_node.subscription_id,
                location="global",
                properties={"user_principal_name": owner_upn, "materialized": True},
            )
            materialized.append(materialized_user)
            known_node_ids.add(owner_id)

        if owner_id in known_node_ids:
            relationships.append(
                ResourceRelationship(
                    source_id=owner_id,
                    target_id=site_node.id,
                    relation_type="owns",
                    weight=1.0,
                )
            )

    return relationships, materialized


def _build_team_relationships(
    team_node: ResourceNode,
    known_node_ids: Set[str],
    materialize_missing: bool,
) -> tuple[List[ResourceRelationship], List[ResourceNode]]:
    """Build relationships for Teams.

    Args:
        team_node: Team node
        known_node_ids: Set of known node IDs
        materialize_missing: Whether to create placeholder nodes

    Returns:
        Tuple of (relationships, materialized nodes)
    """
    relationships: List[ResourceRelationship] = []
    materialized: List[ResourceNode] = []

    # Teams members are retrieved from the /members endpoint
    # If member UPNs are stored in properties, create relationships

    member_upns = team_node.properties.get("member_upns", [])
    for upn in member_upns:
        user_id = f"https://graph.microsoft.com/v1.0/users/{upn}"

        if user_id not in known_node_ids and materialize_missing:
            # Materialize missing user
            materialized_user = ResourceNode(
                id=user_id,
                name=upn,
                type="Microsoft.Graph/users",
                subscription_id=team_node.subscription_id,
                location="global",
                properties={"user_principal_name": upn, "materialized": True},
            )
            materialized.append(materialized_user)
            known_node_ids.add(user_id)

        if user_id in known_node_ids:
            relationships.append(
                ResourceRelationship(
                    source_id=user_id,
                    target_id=team_node.id,
                    relation_type="member_of",
                    weight=1.0,
                )
            )

    return relationships, materialized


def _build_onedrive_relationships(
    drive_node: ResourceNode,
    known_node_ids: Set[str],
    materialize_missing: bool,
) -> tuple[List[ResourceRelationship], List[ResourceNode]]:
    """Build relationships for OneDrive drives.

    Args:
        drive_node: OneDrive drive node
        known_node_ids: Set of known node IDs
        materialize_missing: Whether to create placeholder nodes

    Returns:
        Tuple of (relationships, materialized nodes)
    """
    relationships: List[ResourceRelationship] = []
    materialized: List[ResourceNode] = []

    # OneDrive drives have an owner property
    owner_upn = drive_node.properties.get("owner_upn")
    if owner_upn:
        owner_id = f"https://graph.microsoft.com/v1.0/users/{owner_upn}"

        if owner_id not in known_node_ids and materialize_missing:
            # Materialize missing user
            materialized_user = ResourceNode(
                id=owner_id,
                name=owner_upn,
                type="Microsoft.Graph/users",
                subscription_id=drive_node.subscription_id,
                location="global",
                properties={"user_principal_name": owner_upn, "materialized": True},
            )
            materialized.append(materialized_user)
            known_node_ids.add(owner_id)

        if owner_id in known_node_ids:
            relationships.append(
                ResourceRelationship(
                    source_id=owner_id,
                    target_id=drive_node.id,
                    relation_type="owns",
                    weight=1.0,
                )
            )

    return relationships, materialized


def _build_exchange_relationships(
    mailbox_node: ResourceNode,
    known_node_ids: Set[str],
    materialize_missing: bool,
) -> tuple[List[ResourceRelationship], List[ResourceNode]]:
    """Build relationships for Exchange mailboxes.

    Args:
        mailbox_node: Exchange mailbox node
        known_node_ids: Set of known node IDs
        materialize_missing: Whether to create placeholder nodes

    Returns:
        Tuple of (relationships, materialized nodes)
    """
    relationships: List[ResourceRelationship] = []
    materialized: List[ResourceNode] = []

    # Exchange mailboxes have a user_principal_name property
    owner_upn = mailbox_node.properties.get("user_principal_name")
    if owner_upn:
        owner_id = f"https://graph.microsoft.com/v1.0/users/{owner_upn}"

        if owner_id not in known_node_ids and materialize_missing:
            # Materialize missing user
            materialized_user = ResourceNode(
                id=owner_id,
                name=owner_upn,
                type="Microsoft.Graph/users",
                subscription_id=mailbox_node.subscription_id,
                location="global",
                properties={"user_principal_name": owner_upn, "materialized": True},
            )
            materialized.append(materialized_user)
            known_node_ids.add(owner_id)

        if owner_id in known_node_ids:
            relationships.append(
                ResourceRelationship(
                    source_id=owner_id,
                    target_id=mailbox_node.id,
                    relation_type="owns",
                    weight=1.0,
                )
            )

    return relationships, materialized


def get_sharepoint_sites(nodes: List[ResourceNode]) -> List[ResourceNode]:
    """Filter nodes to only SharePoint sites.

    Args:
        nodes: List of all nodes

    Returns:
        List of SharePoint site nodes
    """
    return [node for node in nodes if node.type == "Microsoft.SharePoint/sites"]


def get_teams(nodes: List[ResourceNode]) -> List[ResourceNode]:
    """Filter nodes to only Teams.

    Args:
        nodes: List of all nodes

    Returns:
        List of Team nodes
    """
    return [node for node in nodes if node.type == "Microsoft.Teams/teams"]


def get_onedrive_drives(nodes: List[ResourceNode]) -> List[ResourceNode]:
    """Filter nodes to only OneDrive drives.

    Args:
        nodes: List of all nodes

    Returns:
        List of OneDrive drive nodes
    """
    return [node for node in nodes if node.type == "Microsoft.OneDrive/drives"]


def get_exchange_mailboxes(nodes: List[ResourceNode]) -> List[ResourceNode]:
    """Filter nodes to only Exchange mailboxes.

    Args:
        nodes: List of all nodes

    Returns:
        List of Exchange mailbox nodes
    """
    return [node for node in nodes if node.type == "Microsoft.Exchange/mailboxes"]


def group_m365_by_type(nodes: List[ResourceNode]) -> Dict[str, List[ResourceNode]]:
    """Group M365 nodes by type.

    Args:
        nodes: List of M365 nodes

    Returns:
        Dictionary mapping type to list of nodes
    """
    grouped: Dict[str, List[ResourceNode]] = {}

    for node in nodes:
        if node.type.startswith("Microsoft."):
            grouped.setdefault(node.type, []).append(node)

    return grouped
