"""Azure Defender for Cloud enumeration for security alerts, assessments, and posture."""

from typing import List, Optional, Any
from datetime import datetime

from azure.mgmt.security.aio import SecurityCenter
from azure.core.credentials_async import AsyncTokenCredential
from azure.core.exceptions import HttpResponseError

from ..adt_types.models import ResourceNode
from ..utils.logging import get_logger

LOGGER = get_logger()


class DefenderEnumerator:
    """Enumerate Azure Defender for Cloud security alerts, assessments, and scores."""

    def __init__(self, credential: AsyncTokenCredential, base_url: str):
        self.credential = credential
        self.base_url = base_url

    async def enumerate_security_alerts(
        self,
        subscription_ids: List[str],
        severity_filter: Optional[List[str]] = None,  # ["High", "Medium", "Low", "Informational"]
        status_filter: Optional[List[str]] = None,  # ["Active", "Resolved", "Dismissed"]
    ) -> List[ResourceNode]:
        """Enumerate security alerts from Defender for Cloud.

        Args:
            subscription_ids: List of subscription IDs to enumerate
            severity_filter: Optional list of severities to include
            status_filter: Optional list of statuses to include

        Returns:
            List of security alert ResourceNode objects
        """
        alerts = []

        for subscription_id in subscription_ids:
            try:
                client = SecurityCenter(
                    credential=self.credential,
                    subscription_id=subscription_id,
                    base_url=self.base_url,
                )

                LOGGER.info(
                    "Enumerating Defender security alerts",
                    extra={"context": {"subscription_id": subscription_id}},
                )

                # List all alerts for the subscription
                async for alert in client.alerts.list():
                    # Apply severity filter if provided
                    if severity_filter and alert.properties.severity not in severity_filter:
                        continue

                    # Apply status filter if provided
                    if status_filter and alert.properties.status not in status_filter:
                        continue

                    # Extract alert properties
                    properties = {
                        "alertDisplayName": alert.properties.alert_display_name,
                        "severity": alert.properties.severity,
                        "status": alert.properties.status,
                        "intent": alert.properties.intent,
                        "description": alert.properties.description,
                        "startTimeUtc": (
                            alert.properties.start_time_utc.isoformat()
                            if alert.properties.start_time_utc
                            else None
                        ),
                        "endTimeUtc": (
                            alert.properties.end_time_utc.isoformat()
                            if alert.properties.end_time_utc
                            else None
                        ),
                        "timeGeneratedUtc": (
                            alert.properties.time_generated_utc.isoformat()
                            if alert.properties.time_generated_utc
                            else None
                        ),
                        "compromisedEntity": alert.properties.compromised_entity,
                        "remediationSteps": alert.properties.remediation_steps or [],
                        "vendorName": alert.properties.vendor_name,
                        "alertType": alert.properties.alert_type,
                        "alertUri": alert.properties.alert_uri,
                        "techniques": alert.properties.techniques or [],  # MITRE ATT&CK
                        "tactics": alert.properties.tactics or [],  # MITRE ATT&CK
                    }

                    # Extract affected resources if available
                    if hasattr(alert.properties, "entities") and alert.properties.entities:
                        properties["affectedResources"] = [
                            entity.get("id") or entity.get("resourceId")
                            for entity in alert.properties.entities
                            if entity.get("id") or entity.get("resourceId")
                        ]

                    # Extract extended properties
                    if (
                        hasattr(alert.properties, "extended_properties")
                        and alert.properties.extended_properties
                    ):
                        properties["extendedProperties"] = (
                            alert.properties.extended_properties
                        )

                    alerts.append(
                        ResourceNode(
                            id=alert.id,
                            name=alert.name,
                            type="Microsoft.Security/alerts",
                            subscription_id=subscription_id,
                            location=None,  # Alerts are subscription-scoped
                            properties=properties,
                            tags={},
                        )
                    )

                await client.close()

            except HttpResponseError as exc:
                # Check if it's a "not found" or "not enabled" error
                if exc.status_code == 404:
                    LOGGER.warning(
                        "Defender for Cloud may not be enabled on subscription",
                        extra={
                            "context": {
                                "subscription_id": subscription_id,
                                "error": str(exc),
                            }
                        },
                    )
                else:
                    LOGGER.error(
                        "Failed to enumerate security alerts",
                        extra={
                            "context": {
                                "subscription_id": subscription_id,
                                "error": str(exc),
                            }
                        },
                    )
            except Exception as exc:
                LOGGER.error(
                    "Unexpected error enumerating security alerts",
                    extra={
                        "context": {
                            "subscription_id": subscription_id,
                            "error": str(exc),
                        }
                    },
                )

        LOGGER.info(
            "Security alert enumeration complete",
            extra={"context": {"total_alerts": len(alerts)}},
        )

        return alerts

    async def enumerate_security_assessments(
        self,
        subscription_ids: List[str],
        severity_filter: Optional[List[str]] = None,  # ["High", "Medium", "Low"]
        status_filter: Optional[List[str]] = None,  # ["Healthy", "Unhealthy", "NotApplicable"]
    ) -> List[ResourceNode]:
        """Enumerate security assessments from Defender for Cloud.

        Args:
            subscription_ids: List of subscription IDs to enumerate
            severity_filter: Optional list of severities to include
            status_filter: Optional list of statuses to include

        Returns:
            List of security assessment ResourceNode objects
        """
        assessments = []

        for subscription_id in subscription_ids:
            try:
                client = SecurityCenter(
                    credential=self.credential,
                    subscription_id=subscription_id,
                    base_url=self.base_url,
                )

                LOGGER.info(
                    "Enumerating Defender security assessments",
                    extra={"context": {"subscription_id": subscription_id}},
                )

                # List all assessments for the subscription
                async for assessment in client.assessments.list(
                    scope=f"/subscriptions/{subscription_id}"
                ):
                    # Apply severity filter if provided
                    severity = (
                        assessment.properties.metadata.severity
                        if hasattr(assessment.properties, "metadata")
                        and assessment.properties.metadata
                        else None
                    )
                    if severity_filter and severity not in severity_filter:
                        continue

                    # Apply status filter if provided
                    status_code = (
                        assessment.properties.status.code
                        if hasattr(assessment.properties, "status")
                        else None
                    )
                    if status_filter and status_code not in status_filter:
                        continue

                    # Extract assessment properties
                    properties = {
                        "displayName": (
                            assessment.properties.display_name
                            if hasattr(assessment.properties, "display_name")
                            else assessment.name
                        ),
                        "statusCode": status_code,
                        "statusCause": (
                            assessment.properties.status.cause
                            if hasattr(assessment.properties, "status")
                            and hasattr(assessment.properties.status, "cause")
                            else None
                        ),
                        "statusDescription": (
                            assessment.properties.status.description
                            if hasattr(assessment.properties, "status")
                            and hasattr(assessment.properties.status, "description")
                            else None
                        ),
                    }

                    # Extract metadata if available
                    if (
                        hasattr(assessment.properties, "metadata")
                        and assessment.properties.metadata
                    ):
                        metadata = assessment.properties.metadata
                        properties["severity"] = metadata.severity
                        properties["assessmentType"] = metadata.assessment_type
                        properties["description"] = metadata.description
                        properties["remediationDescription"] = (
                            metadata.remediation_description
                        )
                        properties["categories"] = metadata.categories or []
                        properties["threats"] = metadata.threats or []

                    # Extract resource details if available
                    if hasattr(assessment.properties, "resource_details"):
                        resource_details = assessment.properties.resource_details
                        if hasattr(resource_details, "id"):
                            properties["affectedResourceId"] = resource_details.id

                    # Extract additional data
                    if hasattr(assessment.properties, "additional_data"):
                        properties["additionalData"] = (
                            assessment.properties.additional_data
                        )

                    assessments.append(
                        ResourceNode(
                            id=assessment.id,
                            name=assessment.name,
                            type="Microsoft.Security/assessments",
                            subscription_id=subscription_id,
                            location=None,  # Assessments are subscription-scoped
                            properties=properties,
                            tags={},
                        )
                    )

                await client.close()

            except HttpResponseError as exc:
                if exc.status_code == 404:
                    LOGGER.warning(
                        "Defender for Cloud may not be enabled on subscription",
                        extra={
                            "context": {
                                "subscription_id": subscription_id,
                                "error": str(exc),
                            }
                        },
                    )
                else:
                    LOGGER.error(
                        "Failed to enumerate security assessments",
                        extra={
                            "context": {
                                "subscription_id": subscription_id,
                                "error": str(exc),
                            }
                        },
                    )
            except Exception as exc:
                LOGGER.error(
                    "Unexpected error enumerating security assessments",
                    extra={
                        "context": {
                            "subscription_id": subscription_id,
                            "error": str(exc),
                        }
                    },
                )

        LOGGER.info(
            "Security assessment enumeration complete",
            extra={"context": {"total_assessments": len(assessments)}},
        )

        return assessments

    async def enumerate_secure_scores(
        self,
        subscription_ids: List[str],
    ) -> List[ResourceNode]:
        """Enumerate secure scores from Defender for Cloud.

        Args:
            subscription_ids: List of subscription IDs to enumerate

        Returns:
            List of secure score ResourceNode objects
        """
        scores = []

        for subscription_id in subscription_ids:
            try:
                client = SecurityCenter(
                    credential=self.credential,
                    subscription_id=subscription_id,
                    base_url=self.base_url,
                )

                LOGGER.info(
                    "Enumerating Defender secure scores",
                    extra={"context": {"subscription_id": subscription_id}},
                )

                # Get the ASC Default secure score
                try:
                    score = await client.secure_scores.get(secure_score_name="ascScore")

                    properties = {
                        "displayName": score.properties.display_name,
                        "currentScore": score.properties.score.current,
                        "maxScore": score.properties.score.max,
                        "percentage": score.properties.score.percentage,
                        "weight": score.properties.weight,
                    }

                    scores.append(
                        ResourceNode(
                            id=score.id,
                            name=score.name,
                            type="Microsoft.Security/secureScores",
                            subscription_id=subscription_id,
                            location=None,
                            properties=properties,
                            tags={},
                        )
                    )

                except HttpResponseError as score_exc:
                    # Secure score might not be available
                    if score_exc.status_code != 404:
                        LOGGER.debug(
                            "Secure score not available for subscription",
                            extra={
                                "context": {
                                    "subscription_id": subscription_id,
                                    "error": str(score_exc),
                                }
                            },
                        )

                await client.close()

            except HttpResponseError as exc:
                if exc.status_code == 404:
                    LOGGER.warning(
                        "Defender for Cloud may not be enabled on subscription",
                        extra={
                            "context": {
                                "subscription_id": subscription_id,
                                "error": str(exc),
                            }
                        },
                    )
                else:
                    LOGGER.error(
                        "Failed to enumerate secure scores",
                        extra={
                            "context": {
                                "subscription_id": subscription_id,
                                "error": str(exc),
                            }
                        },
                    )
            except Exception as exc:
                LOGGER.error(
                    "Unexpected error enumerating secure scores",
                    extra={
                        "context": {
                            "subscription_id": subscription_id,
                            "error": str(exc),
                        }
                    },
                )

        LOGGER.info(
            "Secure score enumeration complete",
            extra={"context": {"total_scores": len(scores)}},
        )

        return scores
