import datetime
import os
import sys
from typing import Optional, Any, List, Sequence, Tuple

from sqlalchemy import select, Row

from app import app
from app.extensions import db
from app.models import AbstractResource
from app.models.bridges import Bridge, BridgeConf
from app.models.cloud import CloudAccount, CloudProvider
from app.terraform.terraform import TerraformAutomation

BridgeResourceRow = Row[Tuple[AbstractResource, BridgeConf, CloudAccount]]


def active_bridges_by_provider(provider: CloudProvider) -> Sequence[BridgeResourceRow]:
    stmt = select(Bridge, BridgeConf, CloudAccount).join_from(Bridge, BridgeConf).join_from(Bridge, CloudAccount).where(
        CloudAccount.provider == provider,
        Bridge.destroyed.is_(None),
    )
    bridges: Sequence[BridgeResourceRow] = db.session.execute(stmt).all()
    return bridges


def recently_destroyed_bridges_by_provider(provider: CloudProvider) -> Sequence[BridgeResourceRow]:
    cutoff = datetime.datetime.utcnow() - datetime.timedelta(hours=72)
    stmt = select(Bridge, BridgeConf, CloudAccount).join_from(Bridge, BridgeConf).join_from(Bridge, CloudAccount).where(
        CloudAccount.provider == provider,
        Bridge.destroyed.is_not(None),
        Bridge.destroyed >= cutoff,
    )
    bridges: Sequence[BridgeResourceRow] = db.session.execute(stmt).all()
    return bridges


class BridgeAutomation(TerraformAutomation):
    template: str
    """
    Terraform configuration template using Jinja 2.
    """

    template_parameters: List[str]
    """
    List of parameters to be read from the application configuration for use
    in the templating of the Terraform configuration.
    """

    max_bridges = sys.maxsize

    # TODO: Only enable providers that have details configured
    enabled = True

    def tf_prehook(self) -> Optional[Any]:  # pylint: disable=useless-return
        return None

    def tf_generate(self) -> None:
        self.tf_write(
            self.template,
            active_resources=active_bridges_by_provider(self.provider),
            destroyed_resources=recently_destroyed_bridges_by_provider(self.provider),
            global_namespace=app.config['GLOBAL_NAMESPACE'],
            terraform_modules_path=os.path.join(*list(os.path.split(app.root_path))[:-1], 'terraform-modules'),
            backend_config=f"""backend "http" {{
              lock_address = "{app.config['TFSTATE_BACKEND']}/{self.short_name}"
              unlock_address = "{app.config['TFSTATE_BACKEND']}/{self.short_name}"
              address = "{app.config['TFSTATE_BACKEND']}/{self.short_name}"
            }}""",
            **{
                k: app.config[k.upper()]
                for k in self.template_parameters
            }
        )

    def tf_posthook(self, *, prehook_result: Any = None) -> None:
        outputs = self.tf_output()
        for output in outputs:
            if output.startswith('bridge_hashed_fingerprint_'):
                parts = outputs[output]['value'].split(" ")
                if len(parts) < 2:
                    continue
                bridge = Bridge.query.filter(Bridge.id == output[len('bridge_hashed_fingerprint_'):]).first()
                bridge.nickname = parts[0]
                bridge.hashed_fingerprint = parts[1]
                bridge.terraform_updated = datetime.datetime.utcnow()
            if output.startswith('bridge_bridgeline_'):
                parts = outputs[output]['value'].split(" ")
                if len(parts) < 4:
                    continue
                bridge = Bridge.query.filter(Bridge.id == output[len('bridge_bridgeline_'):]).first()
                del parts[3]
                bridge.bridgeline = " ".join(parts)
                bridge.terraform_updated = datetime.datetime.utcnow()
        db.session.commit()

    @classmethod
    def active_bridges_count(self) -> int:
        active_bridges = Bridge.query.filter(
            Bridge.provider == self.provider,
            Bridge.destroyed.is_(None),
        ).all()
        return len(active_bridges)
