import sys
import os
from typing import Dict, List
from importlib import import_module

from flask import Flask, request, session

from .sams_app import SAMSApp
from .exceptions import (
  ManifestDefaultLanguageMissing,
  DefaultLanguageDictMissing,
  AppNotExist,
  FunctionNotExists
)

def eprint(*args, **kwargs):
  print(*args, file=sys.stderr, **kwargs)

class SAMSHub:

  def __init__(self, name: str, config: dict):
    self._config = config;
    if not self._config.get('default_language'):
      raise TypeError(
        'Parameter config musst contain a value for "default_language"')
    self._apps = {}
    self._appList = []
    self._flaskApp = Flask(name)
    self._flaskApp.context_processor(self._context_processor)
    self._flaskApp.secret_key = os.urandom(24)
  
  def addApp(self, app: SAMSApp = None, alias = None):
    if not isinstance(app, SAMSApp):
      raise TypeError('Argument app has to be a SAMSApp.')
    appDict = {'app': app, 'urlPrefix': (alias or app.name)}
    self._apps[app.name] = appDict
    self._appList.append(appDict)
    if app.name == self._config.get('main_app'):
      self._flaskApp.register_blueprint(app.blueprint)
    self._flaskApp.register_blueprint(
      app.blueprint, url_prefix = '/' + appDict['urlPrefix'])
  
  @property
  def appKeys(self):
    return self._apps.keys()
  
  @property
  def flaskApp(self):
    return self._flaskApp
  
  def app(self, name):
    try:
      return self._apps[name]['app']
    except:
      raise AppNotExist
  
  def __add_urls(self, module):
    for view in self.__manifests[module].get('views', []):
      pathElements = [module]
      if view['url']:
        pathElements.append(view['url'])
      rule = '/' + '/'.join(pathElements)
      endpoint = '_'.join(view['function'].split('.'))
      view_func = self.__get_attr(self.__modules[module], view['function'])
      self.__blueprints[module].add_url_rule(rule = rule, endpoint = endpoint
        , view_func = view_func)
  
  def _context_processor(self):
    return {
      'app_lang': self.app(request.blueprint).lang(
        session.get('language', self._config['default_language'])
      ),
      'menu': self.menu(
        session.get('language', self._config['default_language']))
    }
  
  def menu(self, langCode):
    menu = []
    for appDict in self._appList:
      if self._config.get('main_app') == appDict['app'].name:
        menu.extend(appDict['app'].menu(langCode = langCode))
      else:
        menu.extend(appDict['app'].menu(langCode = langCode,
          urlPrefix = appDict['urlPrefix']))
    return menu
  
  @staticmethod
  def _get_module_rule(module, path):
    pathElements = [module]
    if path:
      pathElements.append(path)
    return '/' + '/'.join(pathElements)

  @staticmethod
  def _get_attr(object, attrString):
    attr = None
    try:
      return getattr(object, attrString)
    except AttributeError:
      pass
    attrList = attrString.split('.')
    if len(attrList) <= 1:
      raise FunctionNotExists('The function ' + attrString + ' does not exist')
    return(
      SAMSHub._get_attr(getattr(object, attrList[0]), '.'.join(attrList[1:]))
    )