# -*- coding: utf-8 -*-

##
# Copyright 2016-2019 VMware Inc.
# This file is part of ETSI OSM
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# For those usages not covered by the Apache License, Version 2.0 please
# contact:  osslegalrouting@vmware.com
##

# pylint: disable=E1101

import logging
from typing import List
from xml.etree import ElementTree as XmlElementTree

import requests
from pyvcloud.vcd.client import BasicLoginCredentials
from pyvcloud.vcd.client import Client

from osm_mon.collector.infra_collectors.base_vim import BaseVimInfraCollector
from osm_mon.collector.metric import Metric
from osm_mon.core.common_db import CommonDbClient
from osm_mon.core.config import Config

log = logging.getLogger(__name__)
API_VERSION = "27.0"


class VMwareInfraCollector(BaseVimInfraCollector):
    def __init__(self, config: Config, vim_account_id: str):
        super().__init__(config, vim_account_id)
        self.vim_account_id = vim_account_id
        self.common_db = CommonDbClient(config)
        vim_account = self.get_vim_account(vim_account_id)
        self.vcloud_site = vim_account["vim_url"]
        self.admin_username = vim_account["admin_username"]
        self.admin_password = vim_account["admin_password"]
        self.vim_uuid = vim_account["vim_uuid"]
        self.org_name = vim_account["orgname"]
        self.vim_project_id = vim_account["project_id"]

    def connect_vim_as_admin(self):
        """Method connect as pvdc admin user to vCloud director.
        There are certain action that can be done only by provider vdc admin user.
        Organization creation / provider network creation etc.

        Returns:
            The return client object that letter can be used to connect to vcloud direct as admin for provider vdc
        """

        log.info("Logging into vCD org as admin.")

        admin_user = None
        try:
            host = self.vcloud_site
            admin_user = self.admin_username
            admin_passwd = self.admin_password
            org = "System"
            client = Client(host, verify_ssl_certs=False)
            client.set_highest_supported_version()
            client.set_credentials(BasicLoginCredentials(admin_user, org, admin_passwd))
            return client

        except Exception as e:
            log.info(
                "Can't connect to a vCloud director as: {} with exception {}".format(
                    admin_user, e
                )
            )

    def get_vim_account(self, vim_account_id: str):
        """
        Method to get VIM account details by its ID
        arg - VIM ID
        return - dict with vim account details
        """
        vim_account = {}
        vim_account_info = self.common_db.get_vim_account(vim_account_id)

        vim_account["name"] = vim_account_info["name"]
        vim_account["vim_tenant_name"] = vim_account_info["vim_tenant_name"]
        vim_account["vim_type"] = vim_account_info["vim_type"]
        vim_account["vim_url"] = vim_account_info["vim_url"]
        vim_account["org_user"] = vim_account_info["vim_user"]
        vim_account["vim_uuid"] = vim_account_info["_id"]
        if vim_account_info["_admin"]["projects_read"]:
            vim_account["project_id"] = vim_account_info["_admin"]["projects_read"][0]
        else:
            vim_account["project_id"] = ""

        vim_config = vim_account_info["config"]
        vim_account["admin_username"] = vim_config["admin_username"]
        vim_account["admin_password"] = vim_config["admin_password"]

        if vim_config["orgname"] is not None:
            vim_account["orgname"] = vim_config["orgname"]

        return vim_account

    def check_vim_status(self):
        try:
            client = self.connect_vim_as_admin()
            if client._session:
                org_list = client.get_org_list()
                for org in org_list.Org:
                    if org.get("name") == self.org_name:
                        org_uuid = org.get("href").split("/")[-1]

                url = "{}/api/org/{}".format(self.vcloud_site, org_uuid)

                headers = {
                    "Accept": "application/*+xml;version=" + API_VERSION,
                    "x-vcloud-authorization": client._session.headers[
                        "x-vcloud-authorization"
                    ],
                }

                response = requests.get(url=url, headers=headers, verify=False)

                if (
                    response.status_code != requests.codes.ok
                ):  # pylint: disable=no-member
                    log.info("check_vim_status(): failed to get org details")
                else:
                    org_details = XmlElementTree.fromstring(response.content)
                    vdc_list = {}
                    for child in org_details:
                        if "type" in child.attrib:
                            if (
                                child.attrib["type"]
                                == "application/vnd.vmware.vcloud.vdc+xml"
                            ):
                                vdc_list[
                                    child.attrib["href"].split("/")[-1:][0]
                                ] = child.attrib["name"]

                if vdc_list:
                    return True
                else:
                    return False
        except Exception as e:
            log.info("Exception occured while checking vim status {}".format(str(e)))

    def check_vm_status(self, vapp_id):
        try:
            client = self.connect_vim_as_admin()
            if client._session:
                url = "{}/api/vApp/vapp-{}".format(self.vcloud_site, vapp_id)

                headers = {
                    "Accept": "application/*+xml;version=" + API_VERSION,
                    "x-vcloud-authorization": client._session.headers[
                        "x-vcloud-authorization"
                    ],
                }

                response = requests.get(url=url, headers=headers, verify=False)

                if (
                    response.status_code != requests.codes.ok
                ):  # pylint: disable=no-member
                    log.info("check_vm_status(): failed to get vApp details")
                else:
                    vapp_details = XmlElementTree.fromstring(response.content)
                    vm_list = []
                    for child in vapp_details:
                        if child.tag.split("}")[1] == "Children":
                            for item in child.getchildren():
                                vm_list.append(item.attrib)
                return vm_list
        except Exception as e:
            log.info("Exception occured while checking vim status {}".format(str(e)))

    def collect(self) -> List[Metric]:
        metrics = []
        vim_status = self.check_vim_status()
        vim_account_id = self.vim_account_id
        vim_project_id = self.vim_project_id
        vim_tags = {"vim_account_id": vim_account_id, "project_id": vim_project_id}
        vim_status_metric = Metric(vim_tags, "vim_status", vim_status)
        metrics.append(vim_status_metric)
        vnfrs = self.common_db.get_vnfrs(vim_account_id=vim_account_id)
        for vnfr in vnfrs:
            nsr_id = vnfr["nsr-id-ref"]
            ns_name = self.common_db.get_nsr(nsr_id)["name"]
            vnf_member_index = vnfr["member-vnf-index-ref"]
            if vnfr["_admin"]["projects_read"]:
                vnfr_project_id = vnfr["_admin"]["projects_read"][0]
            else:
                vnfr_project_id = ""
            for vdur in vnfr["vdur"]:
                resource_uuid = vdur["vim-id"]
                tags = {
                    "vim_account_id": self.vim_account_id,
                    "resource_uuid": resource_uuid,
                    "nsr_id": nsr_id,
                    "ns_name": ns_name,
                    "vnf_member_index": vnf_member_index,
                    "vdur_name": vdur["name"],
                    "project_id": vnfr_project_id,
                }
                try:
                    vm_list = self.check_vm_status(resource_uuid)
                    for vm in vm_list:
                        if vm["status"] == "4" and vm["deployed"] == "true":
                            vm_status = 1
                        else:
                            vm_status = 0
                        vm_status_metric = Metric(tags, "vm_status", vm_status)
                except Exception:
                    log.exception("VM status is not OK!")
                    vm_status_metric = Metric(tags, "vm_status", 0)
                metrics.append(vm_status_metric)
        return metrics
