"""Unit tests for M365 helper functions."""

import pytest

from azure_discovery.adt_types.models import ResourceNode, ResourceRelationship
from azure_discovery.utils.m365_helpers import (
    build_m365_relationships,
    get_sharepoint_sites,
    get_teams,
    get_onedrive_drives,
    get_exchange_mailboxes,
    group_m365_by_type,
)


@pytest.fixture
def mock_sharepoint_site():
    """Create a mock SharePoint site node."""
    return ResourceNode(
        id="sharepoint://sites/site-123",
        name="Engineering Site",
        type="Microsoft.SharePoint/sites",
        subscription_id="Tenant",
        properties={
            "web_url": "https://contoso.sharepoint.com/sites/engineering",
            "description": "Engineering team collaboration",
            "owner_upn": "alice@contoso.com",
        },
        tags={"siteId": "site-123"},
    )


@pytest.fixture
def mock_team():
    """Create a mock Team node."""
    return ResourceNode(
        id="teams://teams/team-456",
        name="Product Development",
        type="Microsoft.Teams/teams",
        subscription_id="Tenant",
        properties={
            "mail": "productdev@contoso.com",
            "visibility": "Private",
            "member_upns": ["bob@contoso.com"],
        },
        tags={"teamId": "team-456"},
    )


@pytest.fixture
def mock_onedrive_drive():
    """Create a mock OneDrive drive node."""
    return ResourceNode(
        id="onedrive://drives/drive-789",
        name="Alice's OneDrive",
        type="Microsoft.OneDrive/drives",
        subscription_id="Tenant",
        properties={
            "drive_type": "business",
            "owner_upn": "alice@contoso.com",
        },
        tags={"driveId": "drive-789"},
    )


@pytest.fixture
def mock_exchange_mailbox():
    """Create a mock Exchange mailbox node."""
    return ResourceNode(
        id="exchange://mailboxes/user-dave",
        name="dave@contoso.com",
        type="Microsoft.Exchange/mailboxes",
        subscription_id="Tenant",
        properties={
            "user_principal_name": "dave@contoso.com",
            "mail": "dave@contoso.com",
        },
        tags={"userId": "user-dave"},
    )


@pytest.fixture
def mock_user():
    """Create a mock Entra user node (id must match helper's owner_id format)."""
    return ResourceNode(
        id="https://graph.microsoft.com/v1.0/users/alice@contoso.com",
        name="Alice Anderson",
        type="Microsoft.Graph/users",
        subscription_id="Tenant",
        properties={"user_principal_name": "alice@contoso.com"},
        tags={"graph_id": "user-alice"},
    )


def test_get_sharepoint_sites(mock_sharepoint_site, mock_team):
    """Test filtering nodes to SharePoint sites."""
    nodes = [mock_sharepoint_site, mock_team]
    sites = get_sharepoint_sites(nodes)
    
    assert len(sites) == 1
    assert sites[0].type == "Microsoft.SharePoint/sites"


def test_get_teams(mock_sharepoint_site, mock_team):
    """Test filtering nodes to Teams."""
    nodes = [mock_sharepoint_site, mock_team]
    teams = get_teams(nodes)
    
    assert len(teams) == 1
    assert teams[0].type == "Microsoft.Teams/teams"


def test_get_onedrive_drives(mock_onedrive_drive, mock_team):
    """Test filtering nodes to OneDrive drives."""
    nodes = [mock_onedrive_drive, mock_team]
    drives = get_onedrive_drives(nodes)
    
    assert len(drives) == 1
    assert drives[0].type == "Microsoft.OneDrive/drives"


def test_get_exchange_mailboxes(mock_exchange_mailbox, mock_team):
    """Test filtering nodes to Exchange mailboxes."""
    nodes = [mock_exchange_mailbox, mock_team]
    mailboxes = get_exchange_mailboxes(nodes)
    
    assert len(mailboxes) == 1
    assert mailboxes[0].type == "Microsoft.Exchange/mailboxes"


def test_group_m365_by_type(mock_sharepoint_site, mock_team, mock_onedrive_drive, mock_exchange_mailbox):
    """Test grouping M365 nodes by type."""
    nodes = [mock_sharepoint_site, mock_team, mock_onedrive_drive, mock_exchange_mailbox]
    grouped = group_m365_by_type(nodes)
    
    assert "Microsoft.SharePoint/sites" in grouped
    assert "Microsoft.Teams/teams" in grouped
    assert "Microsoft.OneDrive/drives" in grouped
    assert "Microsoft.Exchange/mailboxes" in grouped
    
    assert len(grouped["Microsoft.SharePoint/sites"]) == 1
    assert len(grouped["Microsoft.Teams/teams"]) == 1
    assert len(grouped["Microsoft.OneDrive/drives"]) == 1
    assert len(grouped["Microsoft.Exchange/mailboxes"]) == 1


def test_build_sharepoint_relationships(mock_sharepoint_site, mock_user):
    """Test building SharePoint -> User relationships."""
    m365_nodes = [mock_sharepoint_site]
    all_nodes = [mock_user]
    
    relationships, materialized = build_m365_relationships(
        m365_nodes=m365_nodes,
        all_nodes=all_nodes,
        materialize_missing=False,
    )
    
    assert len(relationships) == 1
    assert relationships[0].relation_type == "owns"
    assert relationships[0].source_id == "https://graph.microsoft.com/v1.0/users/alice@contoso.com"
    assert relationships[0].target_id == "sharepoint://sites/site-123"
    assert len(materialized) == 0


def test_build_team_relationships(mock_team, mock_user):
    """Test building Team -> User relationships."""
    bob_user = ResourceNode(
        id="https://graph.microsoft.com/v1.0/users/bob@contoso.com",
        name="Bob Brown",
        type="Microsoft.Graph/users",
        subscription_id="Tenant",
        properties={},
        tags={"graph_id": "user-bob"},
    )

    m365_nodes = [mock_team]
    all_nodes = [bob_user]

    relationships, materialized = build_m365_relationships(
        m365_nodes=m365_nodes,
        all_nodes=all_nodes,
        materialize_missing=False,
    )

    assert len(relationships) == 1
    assert relationships[0].relation_type == "member_of"
    assert relationships[0].source_id == "https://graph.microsoft.com/v1.0/users/bob@contoso.com"
    assert relationships[0].target_id == "teams://teams/team-456"


def test_build_onedrive_relationships(mock_onedrive_drive, mock_user):
    """Test building OneDrive -> User relationships."""
    m365_nodes = [mock_onedrive_drive]
    all_nodes = [mock_user]
    
    relationships, materialized = build_m365_relationships(
        m365_nodes=m365_nodes,
        all_nodes=all_nodes,
        materialize_missing=False,
    )
    
    assert len(relationships) == 1
    assert relationships[0].relation_type == "owns"
    assert relationships[0].source_id == "https://graph.microsoft.com/v1.0/users/alice@contoso.com"
    assert relationships[0].target_id == "onedrive://drives/drive-789"


def test_build_exchange_relationships(mock_exchange_mailbox):
    """Test building Exchange -> User relationships."""
    dave_user = ResourceNode(
        id="https://graph.microsoft.com/v1.0/users/dave@contoso.com",
        name="Dave Davis",
        type="Microsoft.Graph/users",
        subscription_id="Tenant",
        properties={},
        tags={"graph_id": "user-dave"},
    )

    m365_nodes = [mock_exchange_mailbox]
    all_nodes = [dave_user]

    relationships, materialized = build_m365_relationships(
        m365_nodes=m365_nodes,
        all_nodes=all_nodes,
        materialize_missing=False,
    )

    assert len(relationships) == 1
    assert relationships[0].relation_type == "owns"
    assert relationships[0].source_id == "https://graph.microsoft.com/v1.0/users/dave@contoso.com"
    assert relationships[0].target_id == "exchange://mailboxes/user-dave"


def test_build_relationships_with_materialization(mock_sharepoint_site):
    """Test materialization of missing users."""
    m365_nodes = [mock_sharepoint_site]
    all_nodes = []

    relationships, materialized = build_m365_relationships(
        m365_nodes=m365_nodes,
        all_nodes=all_nodes,
        materialize_missing=True,
    )

    assert len(relationships) == 1
    assert len(materialized) == 1

    materialized_user = materialized[0]
    assert materialized_user.type == "Microsoft.Graph/users"
    assert materialized_user.id == "https://graph.microsoft.com/v1.0/users/alice@contoso.com"
    assert materialized_user.name == "alice@contoso.com"
    assert materialized_user.properties.get("materialized") is True


def test_build_relationships_without_materialization(mock_sharepoint_site):
    """Test skipping relationships when users missing and materialization disabled."""
    m365_nodes = [mock_sharepoint_site]
    all_nodes = []  # No existing users
    
    relationships, materialized = build_m365_relationships(
        m365_nodes=m365_nodes,
        all_nodes=all_nodes,
        materialize_missing=False,
    )
    
    # Should not create relationship or materialize user
    assert len(relationships) == 0
    assert len(materialized) == 0


def test_build_relationships_all_workloads(
    mock_sharepoint_site,
    mock_team,
    mock_onedrive_drive,
    mock_exchange_mailbox,
    mock_user,
):
    """Test building relationships for all M365 workload types."""
    bob_user = ResourceNode(
        id="https://graph.microsoft.com/v1.0/users/bob@contoso.com",
        name="Bob Brown",
        type="Microsoft.Graph/users",
        subscription_id="Tenant",
        properties={},
        tags={"graph_id": "user-bob"},
    )
    dave_user = ResourceNode(
        id="https://graph.microsoft.com/v1.0/users/dave@contoso.com",
        name="Dave Davis",
        type="Microsoft.Graph/users",
        subscription_id="Tenant",
        properties={},
        tags={"graph_id": "user-dave"},
    )

    m365_nodes = [
        mock_sharepoint_site,
        mock_team,
        mock_onedrive_drive,
        mock_exchange_mailbox,
    ]
    all_nodes = [mock_user, bob_user, dave_user]

    relationships, materialized = build_m365_relationships(
        m365_nodes=m365_nodes,
        all_nodes=all_nodes,
        materialize_missing=False,
    )

    assert len(relationships) == 4
    assert len(materialized) == 0

    rel_types = {r.relation_type for r in relationships}
    assert "owns" in rel_types
    assert "member_of" in rel_types


def test_build_relationships_empty_input():
    """Test building relationships with empty inputs."""
    relationships, materialized = build_m365_relationships(
        m365_nodes=[],
        all_nodes=[],
        materialize_missing=False,
    )
    
    assert len(relationships) == 0
    assert len(materialized) == 0


def test_build_relationships_no_owner_tags(mock_sharepoint_site):
    """Test handling M365 nodes without owner tags."""
    # Remove owner tags
    site_no_owner = ResourceNode(
        id="sharepoint://sites/site-orphan",
        name="Orphan Site",
        type="Microsoft.SharePoint/sites",
        subscription_id="Tenant",
        properties={},
        tags={"siteId": "site-orphan"},  # No ownerUserId
    )
    
    m365_nodes = [site_no_owner]
    all_nodes = []
    
    relationships, materialized = build_m365_relationships(
        m365_nodes=m365_nodes,
        all_nodes=all_nodes,
        materialize_missing=True,
    )
    
    # Should not create relationships for orphan resources
    assert len(relationships) == 0
    assert len(materialized) == 0


def test_team_multiple_owners(mock_team):
    """Test Teams with multiple owners creates multiple relationships."""
    mock_team.properties["member_upns"] = ["bob@contoso.com", "charlie@contoso.com"]
    bob_user = ResourceNode(
        id="https://graph.microsoft.com/v1.0/users/bob@contoso.com",
        name="Bob Brown",
        type="Microsoft.Graph/users",
        subscription_id="Tenant",
        properties={},
        tags={"graph_id": "user-bob"},
    )
    charlie_user = ResourceNode(
        id="https://graph.microsoft.com/v1.0/users/charlie@contoso.com",
        name="Charlie Chen",
        type="Microsoft.Graph/users",
        subscription_id="Tenant",
        properties={},
        tags={"graph_id": "user-charlie"},
    )

    m365_nodes = [mock_team]
    all_nodes = [bob_user, charlie_user]

    relationships, materialized = build_m365_relationships(
        m365_nodes=m365_nodes,
        all_nodes=all_nodes,
        materialize_missing=False,
    )

    assert len(relationships) == 2
    assert all(r.relation_type == "member_of" for r in relationships)

    source_ids = {r.source_id for r in relationships}
    assert "https://graph.microsoft.com/v1.0/users/bob@contoso.com" in source_ids
    assert "https://graph.microsoft.com/v1.0/users/charlie@contoso.com" in source_ids


def test_filtering_helpers_empty_input():
    """Test filtering helpers with empty input."""
    assert len(get_sharepoint_sites([])) == 0
    assert len(get_teams([])) == 0
    assert len(get_onedrive_drives([])) == 0
    assert len(get_exchange_mailboxes([])) == 0


def test_group_m365_by_type_empty_input():
    """Test grouping with empty input."""
    grouped = group_m365_by_type([])
    assert len(grouped) == 0
