import datetime
import string
import random

from azure.identity import ClientSecretCredential
from azure.mgmt.alertsmanagement import AlertsManagementClient
import tldextract

from app import app
from app.alarms import get_proxy_alarm
from app.extensions import db
from app.models.base import Group
from app.models.mirrors import Proxy
from app.models.alarms import AlarmState
from app.terraform.proxy import ProxyAutomation


class ProxyAzureCdnAutomation(ProxyAutomation):
    short_name = "proxy_azure_cdn"
    provider = "azure_cdn"

    template_parameters = [
        "azure_resource_group_name",
        "azure_storage_account_name",
        "azure_location",
        "azure_client_id",
        "azure_client_secret",
        "azure_subscription_id",
        "azure_tenant_id"
    ]

    template = """
    terraform {
      required_providers {
        azurerm = {
          source  = "hashicorp/azurerm"
          version = "=2.99.0"
        }
      }
    }
    
    provider "azurerm" {
      features {}
      
      client_id = "{{ azure_client_id }}"
      client_secret = "{{ azure_client_secret }}"
      subscription_id = "{{ azure_subscription_id }}"
      tenant_id = "{{ azure_tenant_id }}"
      skip_provider_registration = true
    }
    
    data "azurerm_resource_group" "this" {
      name = "{{ azure_resource_group_name }}"
    }
    
    resource "azurerm_storage_account" "this" {
      name                     = "{{ azure_storage_account_name }}"
      resource_group_name      = data.azurerm_resource_group.this.name
      location                 = "{{ azure_location }}"
      account_tier             = "Standard"
      account_replication_type = "RAGRS"
    }
    
    {% for group in groups %}
    module "label_{{ group.id }}" {
      source  = "cloudposse/label/null"
      version = "0.25.0"
      namespace = "{{ global_namespace }}"
      tenant = "{{ group.group_name }}"
      label_order = ["namespace", "tenant", "name", "attributes"]
    }
    
    {% for subgroup in subgroups[group.id] %}
    resource "azurerm_cdn_profile" "profile_{{ group.id }}_{{ subgroup }}" {
      name                = "${module.label_{{ group.id }}.id}-sub{{ subgroup }}"
      location            = "{{ azure_location }}"
      resource_group_name = data.azurerm_resource_group.this.name
      sku                 = "Standard_Microsoft"
    
      tags = module.label_{{ group.id }}.tags
    }
    
    resource "azurerm_monitor_diagnostic_setting" "profile_diagnostic_{{ group.id }}_{{ subgroup }}" {
      name               = "cdn-diagnostics"
      target_resource_id = azurerm_cdn_profile.profile_{{ group.id }}_{{ subgroup }}.id
      storage_account_id = azurerm_storage_account.this.id
    
      log {
        category = "AzureCDNAccessLog"
        enabled  = true
    
        retention_policy {
          enabled = true
          days = 90
        }
      }
      
      metric {
        category = "AllMetrics"
        enabled  = true
    
        retention_policy {
          enabled = true
          days = 90
        }
      }
    }
    
    resource "azurerm_monitor_metric_alert" "response_alert_{{ group.id }}_{{ subgroup }}" {
      name                = "bandwidth-out-high-${module.label_{{ group.id }}.id}-sub{{ subgroup }}"
      resource_group_name = data.azurerm_resource_group.this.name
      scopes              = [azurerm_cdn_profile.profile_{{ group.id }}_{{ subgroup }}.id]
      description         = "Action will be triggered when response size is too high."
    
      criteria {
        metric_namespace = "Microsoft.Cdn/profiles"
        metric_name      = "ResponseSize"
        aggregation      = "Total"
        operator         = "GreaterThan"
        threshold        = 21474836481
      }
      
      window_size = "PT1H"
    }
    {% endfor %}
    {% endfor %}
    
    {% for proxy in proxies %}
    resource "azurerm_cdn_endpoint" "endpoint_{{ proxy.id }}" {
      name                = "{{ proxy.slug }}"
      profile_name        = azurerm_cdn_profile.profile_{{ proxy.origin.group.id }}_{{ proxy.psg }}.name
      location            = "{{ azure_location }}"
      resource_group_name = data.azurerm_resource_group.this.name
    
      origin_host_header  = "{{ proxy.origin.domain_name }}"
    
      origin {
        name      = "upstream"
        host_name = "{{ proxy.origin.domain_name }}"
      }
    
      global_delivery_rule {
        modify_request_header_action {
          action = "Append"
          name = "Bypass-Rate-Limit-Token"
          value = "{{ bypass_token }}"
        }
      }
    }
    
    resource "azurerm_monitor_diagnostic_setting" "diagnostic_{{ proxy.id }}" {
      name               = "cdn-diagnostics"
      target_resource_id = azurerm_cdn_endpoint.endpoint_{{ proxy.id }}.id
      storage_account_id = azurerm_storage_account.this.id
    
      log {
        category = "CoreAnalytics"
        enabled  = true
    
        retention_policy {
          enabled = true
          days = 90
        }
      }
    }
    {% endfor %}
    """

    def create_missing_proxies(self):
        groups = Group.query.all()
        subgroups = self.get_subgroups()
        for group in groups:
            subgroup = 0
            for origin in group.origins:
                while True:
                    if subgroups[group.id][subgroup] >= 25:
                        subgroup += 1
                    else:
                        break
                azure_cdn_proxies = [
                    x for x in origin.proxies
                    if x.provider == "azure_cdn" and x.deprecated is None and x.destroyed is None
                ]
                if not azure_cdn_proxies:
                    subgroups[group.id][subgroup] += 1
                    proxy = Proxy()
                    proxy.origin_id = origin.id
                    proxy.provider = "azure_cdn"
                    proxy.psg = subgroup
                    proxy.slug = tldextract.extract(origin.domain_name).domain[:5] + ''.join(
                        random.choices(string.ascii_lowercase, k=random.randint(10, 15)))
                    proxy.url = f"https://{proxy.slug}.azureedge.net"
                    proxy.added = datetime.datetime.utcnow()
                    proxy.updated = datetime.datetime.utcnow()
                    db.session.add(proxy)
            db.session.commit()


def set_urls():
    proxies = Proxy.query.filter(
        Proxy.provider == 'azure_cdn',
        Proxy.destroyed == None
    ).all()
    for proxy in proxies:
        proxy.url = f"https://{proxy.slug}.azureedge.net"
    db.session.commit()


def import_monitor_alerts():
    credential = ClientSecretCredential(
        tenant_id=app.config['AZURE_TENANT_ID'],
        client_id=app.config['AZURE_CLIENT_ID'],
        client_secret=app.config['AZURE_CLIENT_SECRET'])
    client = AlertsManagementClient(
        credential,
        app.config['AZURE_SUBSCRIPTION_ID']
    )
    firing = [x.name[len("bandwidth-out-high-bc-"):]
              for x in client.alerts.get_all()
              if x.name.startswith("bandwidth-out-high-bc-") and x.properties.essentials.monitor_condition == "Fired"]
    for proxy in Proxy.query.filter(
        Proxy.provider == "azure_cdn",
        Proxy.destroyed == None
    ):
        alarm = get_proxy_alarm(proxy.id, "bandwidth-out-high")
        if proxy.origin.group.group_name.lower() not in firing:
            alarm.update_state(AlarmState.OK, "Azure monitor alert not firing")
        else:
            alarm.update_state(AlarmState.CRITICAL, "Azure monitor alert firing")


if __name__ == "__main__":
    with app.app_context():
        auto = ProxyAzureCdnAutomation()
        auto.create_missing_proxies()
        auto.destroy_expired_proxies()
        auto.generate_terraform()
        auto.terraform_init()
        auto.terraform_apply(refresh=False, parallelism=1)  # Rate limits are problem
        set_urls()
        import_monitor_alerts()
