from typing import Any, Optional

from flask import current_app

from app.extensions import db
from app.models.mirrors import Proxy
from app.terraform.proxy import ProxyAutomation


class ProxyFastlyAutomation(ProxyAutomation):
    short_name = "proxy_fastly"
    description = "Deploy proxies to Fastly"
    provider = "fastly"

    template_parameters = [
        "aws_access_key",
        "aws_secret_key",
        "fastly_api_key"
    ]

    template = """
    terraform {
      required_providers {
        aws = {
          version = "~> 4.4.0"
        }
        fastly = {
          source  = "fastly/fastly"
          version = ">= 1.1.1"
        }
      }
    }

    provider "aws" {
      access_key = "{{ aws_access_key }}"
      secret_key = "{{ aws_secret_key }}"
      region = "us-east-2"
    }

    provider "fastly" {
      api_key = "{{ fastly_api_key }}"
    }

    {% 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"]
    }

    module "log_bucket_{{ group.id }}" {
      source = "cloudposse/s3-log-storage/aws"
      version = "0.28.0"
      context = module.label_{{ group.id }}.context
      name = "logs"
      attributes = ["fastly"]
      acl                      = "private"
      standard_transition_days = 30
      glacier_transition_days  = 60
      expiration_days          = 90
    }

    {% for subgroup in subgroups[group.id] %}
    resource "fastly_service_vcl" "service_{{ group.id }}_{{ subgroup }}" {
      name = "${module.label_{{ group.id }}.id}-{{ subgroup }}"

      {% for origin in group.origins %}
      {% for proxy in origin.proxies %}
      {% if proxy.destroyed == None and proxy.provider == "fastly" and proxy.psg == subgroup %}
      domain {
        name    = "{{ proxy.slug }}.global.ssl.fastly.net"
        comment = "Mirror"
      }

      backend {
        address           = "{{ proxy.origin.domain_name }}"
        name              = "{{ proxy.origin.domain_name.replace(".", "_").replace("-", "_") }}_{{ proxy.id }}"
        port              = 443
        use_ssl           = true
        ssl_cert_hostname = "{{ proxy.origin.domain_name }}"
        ssl_sni_hostname  = "{{ proxy.origin.domain_name }}"
        override_host     = "{{ proxy.origin.domain_name }}"
      }
      {% endif %}
      {% endfor %}{# proxy #}
      {% endfor %}{# origin #}

      snippet {
        name = "director"
        content = <<EOV
            if (req.http.host == "") {
                error 400 "No host header";
            }
            {% for origin in group.origins %}
            {% for proxy in origin.proxies %}
            {% if proxy.destroyed == None and proxy.provider == "fastly" and proxy.psg == subgroup %}
            else if (req.http.host == "{{ proxy.slug }}.global.ssl.fastly.net") {
              set req.backend = F_{{ origin.domain_name.replace(".", "_").replace("-", "_") }}_{{ proxy.id }};
              if (req.request != "HEAD" && req.request != "GET" && req.request != "FASTLYPURGE") {
                return(pass);
              }
              return(lookup);
            }
            {% endif %}{% endfor %}{% endfor %}
            else {
                error 400 "Unknown host header";
            }
            EOV
        type = "recv"
      }
    }
    {% endfor %}{# subgroup #}
    {% endfor %}{# group #}
    """

    def __init__(self) -> None:
        """
        Constructor method.
        """
        # Requires Flask application context to read configuration
        self.subgroup_max = min(current_app.config.get("FASTLY_MAX_BACKENDS", 5), 20)
        super().__init__()

    def import_state(self, state: Optional[Any]) -> None:
        proxies = Proxy.query.filter(
            Proxy.provider == self.provider,
            Proxy.destroyed.is_(None)
        ).all()
        for proxy in proxies:
            proxy.url = f"https://{proxy.slug}.global.ssl.fastly.net"
        db.session.commit()
