# Copyright (C) 2022 Fujitsu
# 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.

import os
import re
import tempfile
from urllib.parse import urlparse
import urllib.request as urllib2

from kubernetes import client
from oslo_log import log as logging
import yaml

from tacker.sol_refactored.common import exceptions as sol_ex
from tacker.sol_refactored.common import oidc_utils
from tacker.sol_refactored.infra_drivers.kubernetes import helm_utils
from tacker.sol_refactored.infra_drivers.kubernetes import kubernetes_resource


LOG = logging.getLogger(__name__)

SUPPORTED_NAMESPACE_KIND = {
    "Binding",
    "ConfigMap",
    "ControllerRevision",
    "DaemonSet",
    "Deployment",
    "HorizontalPodAutoscaler",
    "Job",
    "Lease",
    "LimitRange",
    "LocalSubjectAccessReview",
    "NetworkPolicy",
    "PersistentVolumeClaim",
    "Pod",
    "PodTemplate",
    "ReplicaSet",
    "ResourceQuota",
    "Role"
    "RoleBinding",
    "Secret",
    "Service",
    "ServiceAccount",
    "StatefulSet",
}
SCALABLE_KIND = {"Deployment", "ReplicaSet", "StatefulSet"}
TARGET_KIND = {"Pod", "Deployment", "DaemonSet", "StatefulSet", "ReplicaSet"}
UNLABELED_KIND = {"SubjectAccessReview", "LocalSubjectAccessReview",
                  "SelfSubjectAccessReview", "SelfSubjectRulesReview",
                  "TokenReview"}


def get_k8s_reses_from_json_files(target_k8s_files, vnfd, k8s_api_client,
        namespace, inst_id):

    k8s_resources = []

    for target_k8s_file in target_k8s_files:
        if ((urlparse(target_k8s_file).scheme == 'file') or
                (bool(urlparse(target_k8s_file).scheme) and
                 bool(urlparse(target_k8s_file).netloc))):
            with urllib2.urlopen(target_k8s_file) as file_object:
                file_content = file_object.read()
        else:
            file_path = os.path.join(vnfd.csar_dir, target_k8s_file)
            with open(file_path, 'rb') as file_object:
                file_content = file_object.read()

        k8s_resources.extend(list(yaml.safe_load_all(file_content)))

    for k8s_res in k8s_resources:
        if not k8s_res.get('kind'):
            raise sol_ex.K8sInvalidManifestFound()
        if k8s_res['kind'] in SUPPORTED_NAMESPACE_KIND:
            k8s_res.setdefault('metadata', {})
            if namespace is None:
                k8s_res['metadata'].setdefault('namespace', 'default')
            else:
                k8s_res['metadata']['namespace'] = namespace
        # Set label to identify the VnfInstance
        if k8s_res['kind'] not in UNLABELED_KIND:
            k8s_res.setdefault('metadata', {})
            k8s_res['metadata'].setdefault('labels', {})
            k8s_res['metadata']['labels'][
                kubernetes_resource.VNF_INSTANCE_ID_LABEL] = inst_id

    # check namespace
    if namespace is None:
        namespaces = {k8s_res['metadata']['namespace']
                      for k8s_res in k8s_resources
                      if k8s_res['kind'] in SUPPORTED_NAMESPACE_KIND}
        if len(namespaces) > 1:
            raise sol_ex.NamespaceNotUniform()
        namespace = namespaces.pop() if namespaces else 'default'

    k8s_reses = []
    for k8s_res in k8s_resources:
        try:
            cls = getattr(kubernetes_resource, k8s_res['kind'])
            k8s_reses.append(cls(k8s_api_client, k8s_res))
        except AttributeError:
            LOG.info("Not support kind %s. ignored.", k8s_res['kind'])

    return k8s_reses, namespace


def list_namespaced_pods(k8s_api_client, namespace, isall=False):
    k8s_client = client.CoreV1Api(api_client=k8s_api_client)
    all_pods = k8s_client.list_namespaced_pod(namespace=namespace).items
    if isall:
        return all_pods

    # return 'Running' pods only, if isall == False
    return [pod for pod in all_pods if pod.status.phase == 'Running']


class AuthContextManager:
    def __init__(self, vim_info):
        self.vim_info = vim_info
        self.ca_cert_file = None

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, exc_traceback):
        if self.ca_cert_file:
            os.remove(self.ca_cert_file)

    def _create_ca_cert_file(self, ca_cert_str):
        if self.ca_cert_file:
            return
        file_descriptor, self.ca_cert_file = tempfile.mkstemp()
        ca_cert = re.sub(r'\s', '\n', ca_cert_str)
        ca_cert = re.sub(r'BEGIN\nCERT', r'BEGIN CERT', ca_cert)
        ca_cert = re.sub(r'END\nCERT', r'END CERT', ca_cert)
        # write ca cert file
        os.write(file_descriptor, ca_cert.encode())
        os.close(file_descriptor)

    def init_k8s_api_client(self):
        k8s_config = client.Configuration()
        k8s_config.host = self.vim_info.interfaceInfo['endpoint']

        if 'ssl_ca_cert' in self.vim_info.interfaceInfo:
            self._create_ca_cert_file(
                self.vim_info.interfaceInfo['ssl_ca_cert'])

        if 'oidc_token_url' in self.vim_info.accessInfo:
            # Obtain a openid token from openid provider
            id_token = oidc_utils.get_id_token_with_password_grant(
                self.vim_info.accessInfo.get('oidc_token_url'),
                self.vim_info.accessInfo.get('username'),
                self.vim_info.accessInfo.get('password'),
                self.vim_info.accessInfo.get('client_id'),
                client_secret=self.vim_info.accessInfo.get('client_secret'),
                ssl_ca_cert=self.ca_cert_file
            )
            k8s_config.api_key_prefix['authorization'] = 'Bearer'
            k8s_config.api_key['authorization'] = id_token
        else:
            if ('username' in self.vim_info.accessInfo and
                    self.vim_info.accessInfo.get('password') is not None):
                k8s_config.username = self.vim_info.accessInfo['username']
                k8s_config.password = self.vim_info.accessInfo['password']
                basic_token = k8s_config.get_basic_auth_token()
                k8s_config.api_key['authorization'] = basic_token

            if 'bearer_token' in self.vim_info.accessInfo:
                k8s_config.api_key_prefix['authorization'] = 'Bearer'
                k8s_config.api_key['authorization'] = self.vim_info.accessInfo[
                    'bearer_token']

        if self.ca_cert_file:
            k8s_config.ssl_ca_cert = self.ca_cert_file
            k8s_config.verify_ssl = True
        else:
            k8s_config.verify_ssl = False

        return client.api_client.ApiClient(configuration=k8s_config)

    def _get_helm_auth_params(self):
        kube_apiserver = self.vim_info.interfaceInfo['endpoint']
        kube_token = self.vim_info.accessInfo['bearer_token']
        self._create_ca_cert_file(
            self.vim_info.interfaceInfo['ssl_ca_cert'])
        helm_auth_params = ["--kube-apiserver", kube_apiserver,
                            "--kube-ca-file", self.ca_cert_file,
                            "--kube-token", kube_token]

        return helm_auth_params

    def init_helm_client(self):
        return helm_utils.HelmClient(self._get_helm_auth_params())
