import logging
import os
from datetime import datetime
from typing import List, Any

from flask import current_app

from app.extensions import db
from app.models.base import Group
from app.models.cloud import CloudProvider, CloudAccount
from app.models.mirrors import StaticOrigin
from app.terraform.terraform import TerraformAutomation


def import_state(state: Any) -> None:
    if not isinstance(state, dict):
        raise RuntimeError("The Terraform state object returned was not a dict.")
    if "values" not in state or "child_modules" not in state['values']['root_module']:
        # There are no CloudFront origins deployed to import state for
        return
    # CloudFront distributions (origins)
    for mod in state['values']['root_module']['child_modules']:
        if mod['address'].startswith('module.static_'):
            static_id = mod['address'][len('module.static_'):]
            logging.debug("Found static module in state: %s", static_id)
            for res in mod['resources']:
                if res['address'].endswith('aws_cloudfront_distribution.this'):
                    logging.debug("and found related cloudfront distribution")
                    static = StaticOrigin.query.filter(StaticOrigin.id == static_id).first()
                    static.origin_domain_name = res['values']['domain_name']
                    logging.debug("and found static origin: %s to update with domain name: %s", static.id,
                                  static.origin_domain_name)
                    static.terraform_updated = datetime.utcnow()
                    break
    db.session.commit()


class StaticAWSAutomation(TerraformAutomation):
    short_name = "static_aws"
    description = "Deploy static origins to AWS"
    provider = CloudProvider.AWS
    cloud_name = "aws"

    template_parameters: List[str] = []

    template = """
    terraform {
      {{ backend_config }}
      required_providers {
        aws = {
          version = "~> 4.67.0"
        }
        gitlab = {
          source = "gitlabhq/gitlab"
          version = "~> 15.11.0"
        }
      }
    }

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

    {% for account in source_cloud_accounts -%}
    provider "gitlab" {
      token = "{{ account.credentials['gitlab_token'] }}"
      alias = "account_{{ account.id }}"
    }
    {% endfor -%}

    {% for account in storage_cloud_accounts -%}
    provider "aws" {
      access_key = "{{ account.credentials['aws_access_key'] }}"
      secret_key = "{{ account.credentials['aws_secret_key'] }}"
      region     = "{{ account.credentials['aws_region'] }}"
      alias      = "account_{{ account.id }}"
    }

    provider "aws" {
      access_key = "{{ account.credentials['aws_access_key'] }}"
      secret_key = "{{ account.credentials['aws_secret_key'] }}"
      region     = "us-east-1"
      alias      = "account_{{ account.id }}_us_east_1"
    }

    {% for static in account.statics | selectattr("destroyed", "none") %}
    module "static_{{ static.id }}" {
      providers = {
        aws           = aws.account_{{ account.id }}
        aws.us_east_1 = aws.account_{{ account.id }}_us_east_1
        gitlab        = gitlab.account_{{ static.source_cloud_account_id }}
      }
      source = "{{ terraform_modules_path }}/terraform-aws-bc-static-origin"
      name = "static"
      context = module.label_{{ static.group.id }}.context
      {% if static.keanu_convene_path -%}
      keanu_convene_path = "{{ static.keanu_convene_path }}"
      {%- endif %}
      {% if static.keanu_convene_config -%}
      keanu_convene_config = "{{ static.keanu_convene_config  | replace('"', '\\\\"')  }}"
      {%- endif %}
      {% if static.matrix_homeserver -%}
      matrix_homeserver = "{{ static.matrix_homeserver }}"
      {%- endif %}
      gitlab_project = "{{ static.source_project }}"
      attributes = ["{{ static.id }}"]
    }
    {% endfor -%}
    {%- endfor %}
    """

    def tf_generate(self) -> None:
        groups = Group.query.all()
        storage_cloud_accounts = CloudAccount.query.filter(
            CloudAccount.provider == CloudProvider.AWS
        ).all()
        source_cloud_accounts = CloudAccount.query.filter(
            CloudAccount.provider == CloudProvider.GITLAB
        ).all()
        self.tf_write(
            self.template,
            groups=groups,
            storage_cloud_accounts=storage_cloud_accounts,
            source_cloud_accounts=source_cloud_accounts,
            global_namespace=current_app.config['GLOBAL_NAMESPACE'], bypass_token=current_app.config['BYPASS_TOKEN'],
            terraform_modules_path=os.path.join(*list(os.path.split(current_app.root_path))[:-1], 'terraform-modules'),
            backend_config=f"""backend "http" {{
              lock_address = "{current_app.config['TFSTATE_BACKEND']}/{self.short_name}"
              unlock_address = "{current_app.config['TFSTATE_BACKEND']}/{self.short_name}"
              address = "{current_app.config['TFSTATE_BACKEND']}/{self.short_name}"
            }}""",
            **{k: current_app.config[k.upper()] for k in self.template_parameters})

    def tf_posthook(self, *, prehook_result: Any = None) -> None:
        import_state(self.tf_show())
