from datetime import timedelta
from typing import Dict

from azure.core.exceptions import (
    ClientAuthenticationError,
    HttpResponseError,
    ResourceNotFoundError,
)
from azure.mgmt.security import SecurityCenter
from pydantic import BaseModel

from prowler.lib.logger import logger
from prowler.providers.azure.azure_provider import AzureProvider
from prowler.providers.azure.lib.service.service import AzureService


class Defender(AzureService):
    def __init__(self, provider: AzureProvider):
        super().__init__(SecurityCenter, provider)

        self.pricings = self._get_pricings()
        self.auto_provisioning_settings = self._get_auto_provisioning_settings()
        self.assessments = self._get_assessments()
        self.settings = self._get_settings()
        self.security_contacts = self._get_security_contacts()
        self.iot_security_solutions = self._get_iot_security_solutions()

    def _get_pricings(self):
        logger.info("Defender - Getting pricings...")
        pricings = {}
        for subscription_name, client in self.clients.items():
            try:
                pricings_list = client.pricings.list(
                    scope_id=f"subscriptions/{self.subscriptions[subscription_name]}"
                )
                pricings.update({subscription_name: {}})
                for pricing in pricings_list.value:
                    pricings[subscription_name].update(
                        {
                            pricing.name: Pricing(
                                resource_id=pricing.id,
                                pricing_tier=getattr(pricing, "pricing_tier", None),
                                free_trial_remaining_time=pricing.free_trial_remaining_time,
                                extensions=dict(
                                    [
                                        (extension.name, extension.is_enabled)
                                        for extension in (
                                            pricing.extensions
                                            if getattr(pricing, "extensions", None)
                                            else []
                                        )
                                    ]
                                ),
                            )
                        }
                    )
            except ResourceNotFoundError as error:
                if "Subscription Not Registered" in error.message:
                    logger.error(
                        f"Subscription name: {subscription_name} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: Subscription Not Registered - Please register to Microsoft.Security in order to view your security status"
                    )
            except Exception as error:
                logger.error(
                    f"Subscription name: {subscription_name} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
                )
        return pricings

    def _get_auto_provisioning_settings(self):
        logger.info("Defender - Getting auto provisioning settings...")
        auto_provisioning = {}
        for subscription_name, client in self.clients.items():
            try:
                auto_provisioning_settings = client.auto_provisioning_settings.list()
                auto_provisioning.update({subscription_name: {}})
                for ap in auto_provisioning_settings:
                    auto_provisioning[subscription_name].update(
                        {
                            ap.name: AutoProvisioningSetting(
                                resource_id=ap.id,
                                resource_name=ap.name,
                                resource_type=ap.type,
                                auto_provision=ap.auto_provision,
                            )
                        }
                    )
            except ClientAuthenticationError as error:
                if "Subscription Not Registered" in error.message:
                    logger.error(
                        f"Subscription name: {subscription_name} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: Subscription Not Registered - Please register to Microsoft.Security in order to view your security status"
                    )
            except Exception as error:
                logger.error(
                    f"Subscription name: {subscription_name} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
                )
        return auto_provisioning

    def _get_assessments(self):
        logger.info("Defender - Getting assessments...")
        assessments = {}
        for subscription_name, client in self.clients.items():
            try:
                assessments_list = client.assessments.list(
                    f"subscriptions/{self.subscriptions[subscription_name]}"
                )
                assessments.update({subscription_name: {}})
                for assessment in assessments_list:
                    assessments[subscription_name].update(
                        {
                            assessment.display_name: Assesment(
                                resource_id=assessment.id,
                                resource_name=assessment.name,
                                status=assessment.status.code,
                            )
                        }
                    )
            except Exception as error:
                logger.error(
                    f"Subscription name: {subscription_name} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
                )
        return assessments

    def _get_settings(self):
        logger.info("Defender - Getting settings...")
        settings = {}
        for subscription_name, client in self.clients.items():
            try:
                settings_list = client.settings.list()
                settings.update({subscription_name: {}})
                for setting in settings_list:
                    settings[subscription_name].update(
                        {
                            setting.name: Setting(
                                resource_id=setting.id,
                                resource_type=setting.type,
                                kind=setting.kind,
                                enabled=setting.enabled,
                            )
                        }
                    )
            except ClientAuthenticationError as error:
                if "Subscription Not Registered" in error.message:
                    logger.error(
                        f"Subscription name: {subscription_name} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: Subscription Not Registered - Please register to Microsoft.Security in order to view your security status"
                    )
            except Exception as error:
                logger.error(
                    f"Subscription name: {subscription_name} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
                )
        return settings

    def _get_security_contacts(self):
        logger.info("Defender - Getting security contacts...")
        security_contacts = {}
        for subscription_name, client in self.clients.items():
            try:
                security_contacts.update({subscription_name: {}})
                # TODO: List all security contacts. For now, the list method is not working.
                security_contact_default = client.security_contacts.get("default")
                security_contacts[subscription_name].update(
                    {
                        security_contact_default.name: SecurityContacts(
                            resource_id=security_contact_default.id,
                            name=security_contact_default.name,
                            emails=security_contact_default.emails,
                            phone=security_contact_default.phone,
                            alert_notifications_minimal_severity=security_contact_default.alert_notifications.minimal_severity,
                            alert_notifications_state=security_contact_default.alert_notifications.state,
                            notified_roles=security_contact_default.notifications_by_role.roles,
                            notified_roles_state=security_contact_default.notifications_by_role.state,
                        )
                    }
                )
            except HttpResponseError as error:
                if error.status_code == 404:
                    security_contacts[subscription_name].update(
                        {
                            "default": SecurityContacts(
                                resource_id=f"/subscriptions/{self.subscriptions[subscription_name]}/providers/Microsoft.Security/securityContacts/default",
                                name="",
                                emails="",
                                phone="",
                                alert_notifications_minimal_severity="",
                                alert_notifications_state="",
                                notified_roles=[""],
                                notified_roles_state="",
                            )
                        }
                    )
                else:
                    logger.error(
                        f"Subscription name: {subscription_name} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
                    )
            except Exception as error:
                logger.error(
                    f"Subscription name: {subscription_name} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
                )
        return security_contacts

    def _get_iot_security_solutions(self):
        logger.info("Defender - Getting IoT Security Solutions...")
        iot_security_solutions = {}
        for subscription_name, client in self.clients.items():
            try:
                iot_security_solutions_list = (
                    client.iot_security_solution.list_by_subscription()
                )
                iot_security_solutions.update({subscription_name: {}})
                for iot_security_solution in iot_security_solutions_list:
                    iot_security_solutions[subscription_name].update(
                        {
                            iot_security_solution.id: IoTSecuritySolution(
                                resource_id=iot_security_solution.id,
                                name=iot_security_solution.name,
                                status=iot_security_solution.status,
                            )
                        }
                    )
            except Exception as error:
                logger.error(
                    f"Subscription name: {subscription_name} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
                )
        return iot_security_solutions


class Pricing(BaseModel):
    resource_id: str
    pricing_tier: str
    free_trial_remaining_time: timedelta
    extensions: Dict[str, bool] = {}


class AutoProvisioningSetting(BaseModel):
    resource_id: str
    resource_name: str
    resource_type: str
    auto_provision: str


class Assesment(BaseModel):
    resource_id: str
    resource_name: str
    status: str


class Setting(BaseModel):
    resource_id: str
    resource_type: str
    kind: str
    enabled: bool


class SecurityContacts(BaseModel):
    resource_id: str
    name: str
    emails: str
    phone: str
    alert_notifications_minimal_severity: str
    alert_notifications_state: str
    notified_roles: list[str]
    notified_roles_state: str


class IoTSecuritySolution(BaseModel):
    resource_id: str
    name: str
    status: str
