# (c) Copyright IBM Corp. 2021
# (c) Copyright Instana Inc. 2020

"""
AWS Fargate Collector: Manages the periodic collection of metrics & snapshot data
"""

import json
import os
from time import time

import requests

from instana.collector.base import BaseCollector
from instana.collector.helpers.fargate.container import ContainerHelper
from instana.collector.helpers.fargate.docker import DockerHelper
from instana.collector.helpers.fargate.process import FargateProcessHelper
from instana.collector.helpers.fargate.task import TaskHelper
from instana.collector.helpers.runtime import RuntimeHelper
from instana.collector.utils import format_span
from instana.log import logger
from instana.util import DictionaryOfStan, validate_url


class AWSFargateCollector(BaseCollector):
    """Collector for AWS Fargate"""

    def __init__(self, agent):
        super(AWSFargateCollector, self).__init__(agent)
        logger.debug("Loading AWS Fargate Collector")

        # Indicates if this Collector has all requirements to run successfully
        self.ready_to_start = True

        # Prepare the URLS that we will collect data from
        self.ecmu = os.environ.get("ECS_CONTAINER_METADATA_URI", "")

        if self.ecmu == "" or validate_url(self.ecmu) is False:
            logger.warning(
                "AWSFargateCollector: ECS_CONTAINER_METADATA_URI not in environment or invalid URL.  "
                "Instana will not be able to monitor this environment"
            )
            self.ready_to_start = False

        self.ecmu_url_root = self.ecmu
        self.ecmu_url_task = self.ecmu + "/task"
        self.ecmu_url_stats = self.ecmu + "/stats"
        self.ecmu_url_task_stats = self.ecmu + "/task/stats"

        # Timestamp in seconds of the last time we fetched all ECMU data
        self.last_ecmu_full_fetch = 0

        # How often to do a full fetch of ECMU data
        self.ecmu_full_fetch_interval = 304

        # HTTP client with keep-alive
        self.http_client = requests.Session()

        # This is the collecter thread querying the metadata url
        self.ecs_metadata_thread = None

        # The fully qualified ARN for this process
        self._fq_arn = None

        # Response from the last call to
        # ${ECS_CONTAINER_METADATA_URI}/
        self.root_metadata = None

        # Response from the last call to
        # ${ECS_CONTAINER_METADATA_URI}/task
        self.task_metadata = None

        # Response from the last call to
        # ${ECS_CONTAINER_METADATA_URI}/stats
        self.stats_metadata = None

        # Response from the last call to
        # ${ECS_CONTAINER_METADATA_URI}/task/stats
        self.task_stats_metadata = None

        # Populate the collection helpers
        self.helpers.append(TaskHelper(self))
        self.helpers.append(DockerHelper(self))
        self.helpers.append(FargateProcessHelper(self))
        self.helpers.append(RuntimeHelper(self))
        self.helpers.append(ContainerHelper(self))

    def start(self):
        if self.ready_to_start is False:
            logger.warning(
                "AWS Fargate Collector is missing requirements and cannot monitor this environment."
            )
            return

        super(AWSFargateCollector, self).start()

    def get_ecs_metadata(self):
        """
        Get the latest data from the ECS metadata container API and store on the class
        @return: Boolean
        """
        try:
            self.fetching_start_time = int(time())
            delta = self.fetching_start_time - self.last_ecmu_full_fetch
            if delta > self.ecmu_full_fetch_interval:
                # Refetch the ECMU snapshot data
                self.last_ecmu_full_fetch = int(time())

                # Response from the last call to
                # ${ECS_CONTAINER_METADATA_URI}/
                json_body = self.http_client.get(self.ecmu_url_root, timeout=1).content
                self.root_metadata = json.loads(json_body)

                # Response from the last call to
                # ${ECS_CONTAINER_METADATA_URI}/task
                json_body = self.http_client.get(self.ecmu_url_task, timeout=1).content
                self.task_metadata = json.loads(json_body)

            # Response from the last call to
            # ${ECS_CONTAINER_METADATA_URI}/stats
            json_body = self.http_client.get(self.ecmu_url_stats, timeout=2).content
            self.stats_metadata = json.loads(json_body)

            # Response from the last call to
            # ${ECS_CONTAINER_METADATA_URI}/task/stats
            json_body = self.http_client.get(
                self.ecmu_url_task_stats, timeout=1
            ).content
            self.task_stats_metadata = json.loads(json_body)
        except Exception:
            logger.debug("AWSFargateCollector.get_ecs_metadata", exc_info=True)

    def should_send_snapshot_data(self):
        return int(time()) - self.snapshot_data_last_sent > self.snapshot_data_interval

    def prepare_payload(self):
        payload = DictionaryOfStan()
        payload["spans"] = []
        payload["metrics"]["plugins"] = []

        try:
            if not self.span_queue.empty():
                payload["spans"] = format_span(self.queued_spans())

            with_snapshot = self.should_send_snapshot_data()

            # Fetch the latest metrics
            self.get_ecs_metadata()

            plugins = []
            for helper in self.helpers:
                plugins.extend(helper.collect_metrics(with_snapshot=with_snapshot))

            payload["metrics"]["plugins"] = plugins

            if with_snapshot is True:
                self.snapshot_data_last_sent = int(time())
        except Exception:
            logger.debug("collect_snapshot error", exc_info=True)

        return payload

    def get_fq_arn(self):
        if self._fq_arn is not None:
            return self._fq_arn

        task_arn = ""
        if self.root_metadata is not None:
            labels = self.root_metadata.get("Labels", None)
            if labels is not None:
                task_arn = labels.get("com.amazonaws.ecs.task-arn", "")

            container_name = self.root_metadata.get("Name", "")

            self._fq_arn = task_arn + "::" + container_name
            return self._fq_arn
        else:
            return "Missing ECMU metadata"
