# -*- coding: utf-8

import yaml
import json
import re
import collections
from etcd import EtcdKeyNotFound
from .specs import render_app_spec, AppType
from lain_sdk.yaml.parser import ProcType, LainConf
from commons.settings import PRIVATE_REGISTRY
from .utils import (
    normalize_meta_version,
    search_images_from_registry,
    get_meta_from_registry,
    read_from_etcd,
    delete_from_etcd,
    set_value_to_etcd,
    get_meta_version_from_tag,
    get_current_time,
    get_domains,
)
from commons.miscs import (
    InvalidMetaVersion,
    InvalidLainYaml,
    InvalidStoreData,
    DoesNotExist,
)
from log import logger


APP_RUNNING_STATE = {
    'REPO': 'reposited',
    'DEPLOYING': 'deploying',
    'DEPLOY': 'deployed'
}


class BaseApp:

    ETCD_PREFIX = '/lain/fake'

    appname = ''
    giturl = ''
    meta_version = ''
    meta = ''
    default_image = ''
    running_state = ''
    app_type = ''
    last_error = ''
    last_update = ''

    # property saved in memory but not in etcd
    _registry_tags = []
    _latest_meta_version = ''
    _app_spec = None
    _lain_config = None

    @classmethod
    def etcd_app_key(cls, appname):
        assert appname
        return "%s/%s" % (cls.ETCD_PREFIX, appname)

    @classmethod
    def render_app_from_etcd_value(cls, etcd_value):
        try:
            app_info = json.loads(etcd_value)
            appname = app_info.get('appname', '')
            giturl = app_info.get('giturl', '')
            meta_version = app_info.get('meta_version', '')
            meta = app_info.get('meta', '')
            default_image = app_info.get('default_image', '')
            running_state = app_info.get('running_state', None)
            app_type = app_info.get('app_type', None)
            last_update = app_info.get('last_update', '')
            last_error = app_info.get('last_error', '')
            if appname == '':
                raise InvalidStoreData("appname should not be empty")
            app = cls()
            app.appname = appname
            app.giturl = giturl
            app.meta_version = meta_version
            app.meta = meta
            app.default_image = default_image
            app.running_state = running_state
            app.app_type = app_type
            app.last_update = last_update
            app.last_error = last_error

            app._registry_tags = []
            app._latest_meta_version = ''
            app._app_spec = None
            app._lain_config = None

            return app
        except ValueError, e:
            raise InvalidStoreData(e)

    @classmethod
    def get_or_none(cls, appname):
        app = None
        try:
            app = cls.get(appname)
        except DoesNotExist:
            pass
        return app

    @classmethod
    def get(cls, appname):
        try:
            etcd_r = read_from_etcd(cls.etcd_app_key(appname))
            if etcd_r.dir:
                raise InvalidStoreData("Store Data should not be dir")
        except EtcdKeyNotFound, e:
            raise DoesNotExist(e)
        return cls.render_app_from_etcd_value(etcd_r.value)  # pylint: disable=E1103

    @classmethod
    def all(cls):
        try:
            apps_root_r = read_from_etcd(cls.ETCD_PREFIX)
        except EtcdKeyNotFound, e:
            logger.warn("call App.all() fail: %s" % e)
            return []
        apps = []
        for l in apps_root_r.leaves:
            appname = l.key[len(cls.ETCD_PREFIX) + 1:]  # FIXME: ugly
            try:
                app = cls.get(appname)
                apps.append(app)
            except:
                logger.error("error getting app %s from etcd" % appname)
        return apps

    @classmethod
    def create(cls, appname):
        app = cls()
        app.appname = appname
        app.running_state = APP_RUNNING_STATE['REPO']
        app.save()
        return app

    def clear(self):
        self.giturl = ''
        self.meta_version = ''
        self.meta = ''
        self.app_type = ''
        self.last_error = ''
        self.running_state = APP_RUNNING_STATE['REPO']
        self.save()

    def delete(self):
        delete_from_etcd(self.etcd_key)

    def save(self):
        self.last_update = get_current_time()
        etcd_value = json.dumps({
            'appname': self.appname,
            'giturl': self.giturl,
            'meta_version': self.meta_version,
            'meta': self.meta,
            'default_image': self.default_image,
            'running_state': self.running_state,
            'app_type': self.app_type,
            'last_update': self.last_update,
            'last_error': self.last_error,
        })
        set_value_to_etcd(self.etcd_key, etcd_value)

    @property
    def lain_config(self):
        if self._lain_config is None:
            self._lain_config = self._get_lain_config()
        return self._lain_config

    def _get_lain_config(self):
        if self.meta == '' or self.meta_version == '':
            return None
        config = LainConf()
        config.load(self.meta, self.meta_version, self.default_image,