import logging
import os
import json
import yaml

from flask import Flask as BaseFlask, Config as BaseConfig, request, Request
from flask.ext.principal import Principal
from flask.ext.login import LoginManager
from flask.ext.mail import Mail

import features

from storage import Storage
from data import model
from data import database
from data.userfiles import Userfiles
from data.users import UserAuthentication
from util.analytics import Analytics
from util.exceptionlog import Sentry
from util.queuemetrics import QueueMetrics
from util.names import urn_generator
from data.billing import Billing
from data.buildlogs import BuildLogs
from data.archivedlogs import LogArchive
from data.queue import WorkQueue
from data.userevent import UserEventsBuilderModule


class Config(BaseConfig):
  """ Flask config enhanced with a `from_yamlfile` method """
   
  def from_yamlfile(self, config_file):
    with open(config_file) as f:
      c = yaml.load(f)
      if not c:
        logger.debug('Empty YAML config file')
        return

      if isinstance(c, str):
        raise Exception('Invalid YAML config file: ' + str(c))

      for key in c.iterkeys():
        if key.isupper():
          self[key] = c[key]

class Flask(BaseFlask):
  """ Extends the Flask class to implement our custom Config class. """

  def make_config(self, instance_relative=False):
    root_path = self.instance_path if instance_relative else self.root_path
    return Config(root_path, self.default_config)


OVERRIDE_CONFIG_YAML_FILENAME = 'conf/stack/config.yaml'
OVERRIDE_CONFIG_PY_FILENAME = 'conf/stack/config.py'

OVERRIDE_CONFIG_KEY = 'QUAY_OVERRIDE_CONFIG'
LICENSE_FILENAME = 'conf/stack/license.enc'


app = Flask(__name__)
logger = logging.getLogger(__name__)
profile = logging.getLogger('profile')


if 'TEST' in os.environ:
  from test.testconfig import TestConfig
  logger.debug('Loading test config.')
  app.config.from_object(TestConfig())
else:
  from config import DefaultConfig
  logger.debug('Loading default config.')
  app.config.from_object(DefaultConfig())

  if os.path.exists(OVERRIDE_CONFIG_PY_FILENAME):
    logger.debug('Applying config file: %s', OVERRIDE_CONFIG_PY_FILENAME)
    app.config.from_pyfile(OVERRIDE_CONFIG_PY_FILENAME)

  if os.path.exists(OVERRIDE_CONFIG_YAML_FILENAME):
    logger.debug('Applying config file: %s', OVERRIDE_CONFIG_YAML_FILENAME)
    app.config.from_yamlfile(OVERRIDE_CONFIG_YAML_FILENAME)

  environ_config = json.loads(os.environ.get(OVERRIDE_CONFIG_KEY, '{}'))
  app.config.update(environ_config)

  app.teardown_request(database.close_db_filter)


class RequestWithId(Request):
  request_gen = staticmethod(urn_generator(['request']))

  def __init__(self, *args, **kwargs):
    super(RequestWithId, self).__init__(*args, **kwargs)
    self.request_id = self.request_gen()


@app.before_request
def _request_start():
  profile.debug('Starting request: %s', request.path)


@app.after_request
def _request_end(r):
  profile.debug('Ending request: %s', request.path)
  return r


class InjectingFilter(logging.Filter):
  def filter(self, record):
    record.msg = '[%s] %s' % (request.request_id, record.msg)
    return True

profile.addFilter(InjectingFilter())

app.request_class = RequestWithId

features.import_features(app.config)

Principal(app, use_sessions=False)

login_manager = LoginManager(app)
mail = Mail(app)
storage = Storage(app)
userfiles = Userfiles(app, storage)
log_archive = LogArchive(app, storage)
analytics = Analytics(app)
billing = Billing(app)
sentry = Sentry(app)
build_logs = BuildLogs(app)
queue_metrics = QueueMetrics(app)
authentication = UserAuthentication(app)
userevents = UserEventsBuilderModule(app)

tf = app.config['DB_TRANSACTION_FACTORY']
image_diff_queue = WorkQueue(app.config['DIFFS_QUEUE_NAME'], tf)
dockerfile_build_queue = WorkQueue(app.config['DOCKERFILE_BUILD_QUEUE_NAME'], tf,
                                   reporter=queue_metrics.report)
notification_queue = WorkQueue(app.config['NOTIFICATION_QUEUE_NAME'], tf)

database.configure(app.config)
model.config.app_config = app.config
model.config.store = storage

def get_app_url():
  return '%s://%s' % (app.config['PREFERRED_URL_SCHEME'], app.config['SERVER_HOSTNAME'])
