#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright 1999-2020 Alibaba Group Holding Ltd.
#
# 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 json
import logging

from .. import kvstore
from ..config import options
from ..worker import VineyardKeyMapActor
from .chunkmeta import ChunkMetaActor
from .custom_log import CustomLogMetaActor
from .kvstore import KVStoreActor
from .node_info import NodeInfoActor
from .resource import ResourceActor
from .session import SessionManagerActor
from .utils import SchedulerClusterInfoActor

logger = logging.getLogger(__name__)


class SchedulerService(object):
    def __init__(self, **kwargs):
        self._cluster_info_ref = None
        self._session_manager_ref = None
        self._assigner_ref = None
        self._resource_ref = None
        self._chunk_meta_ref = None
        self._custom_log_meta_ref = None
        self._kv_store_ref = None
        self._node_info_ref = None
        self._result_receiver_ref = None
        self._vineyard_key_map_ref = None

        options.scheduler.enable_failover = not (kwargs.pop('disable_failover', None) or False)

        if kwargs:  # pragma: no cover
            raise TypeError(f'Keyword arguments {kwargs!r} cannot be recognized.')

    def start(self, endpoint, discoverer, pool, distributed=True):
        """
        there are two way to start a scheduler
        1) if options.kv_store is specified as an etcd address, the endpoint will be written
        into kv-storage to indicate that this scheduler is one the schedulers,
        and the etcd is used as a service discover.
        2) if options.kv_store is not an etcd address, there will be only one scheduler
        """
        kv_store = kvstore.get(options.kv_store)
        kv_store.write(f'/schedulers/{endpoint}', dir=True)

        if not isinstance(kv_store, kvstore.LocalKVStore):
            # set etcd as service discover
            logger.info('Mars Scheduler started with kv store %s.', options.kv_store)
            # create KVStoreActor when there is a distributed KV store
            self._kv_store_ref = pool.create_actor(KVStoreActor, uid=KVStoreActor.default_uid())
        else:
            # single scheduler
            logger.info('Mars Scheduler started in standalone mode.')

        # create ClusterInfoActor
        self._cluster_info_ref = pool.create_actor(
            SchedulerClusterInfoActor, discoverer, distributed=distributed,
            uid=SchedulerClusterInfoActor.default_uid())
        # create ChunkMetaActor
        self._chunk_meta_ref = pool.create_actor(ChunkMetaActor, uid=ChunkMetaActor.default_uid())
        # create CustomLogMetaActor
        self._custom_log_meta_ref = pool.create_actor(
            CustomLogMetaActor, uid=CustomLogMetaActor.default_uid())
        # create SessionManagerActor
        self._session_manager_ref = pool.create_actor(
            SessionManagerActor, uid=SessionManagerActor.default_uid())
        # create ResourceActor
        self._resource_ref = pool.create_actor(ResourceActor, uid=ResourceActor.default_uid())
        # create NodeInfoActor
        self._node_info_ref = pool.create_actor(NodeInfoActor, uid=NodeInfoActor.default_uid())
        kv_store.write(f'/schedulers/{endpoint}/meta',
                       json.dumps(self._resource_ref.get_workers_meta()))
        if options.vineyard.enabled:
            # create global VineyardKeyMapActor
            self._vineyard_key_map_ref = pool.create_actor(VineyardKeyMapActor, uid=VineyardKeyMapActor.default_uid())

    def stop(self, pool):
        pool.destroy_actor(self._resource_ref)
