#!/usr/bin/python
# -*- coding: utf-8 -*-

"""Start the gransk web UI."""

from __future__ import absolute_import, unicode_literals

import argparse
import os
import logging
import json
from functools import wraps
import requests

from flask import Flask, Response, request, abort

import gransk.api
import gransk.core.helper as helper
import gransk.core.pipeline as pipeline
import gransk.core.document as document
import gransk.core.injector as injector
import gransk.boot.run

_root = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', '..')
app = Flask(
    __name__,
    template_folder=os.path.join(_root, 'gransk', 'web', 'app'),
    static_url_path='/static',
    static_folder=os.path.join(_root, 'gransk', 'web', 'app'))


_globals = {}

def check_auth(username, password):
  if not _globals['config'].get('auth'):
    return True

  return username == _globals['config']['auth']['user'] and password == _globals['config']['auth']['pass']

def authenticate():
    return Response(
    'Could not verify your access level for that URL.\n'
    'You have to login with proper credentials', 401,
    {'WWW-Authenticate': 'Basic realm="Login Required"'})

def requires_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        auth = request.authorization
        if not auth or not check_auth(auth.username, auth.password):
          if _globals.get('test'):
            return f(*args, **kwargs)
          return authenticate()
        return f(*args, **kwargs)
    return decorated

@app.after_request
@requires_auth
def add_header(response):
  """Set HTTP headers on each response."""
  response.headers['Access-Control-Allow-Origin'] = '*'
  response.headers[
      'Access-Control-Allow-Headers'] = 'Accept, Authorization, Content-Type, X-Requested-With, Range'
  response.headers[
      'Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0'
  response.headers[
      'Access-Control-Allow-Methods'] = 'GET,HEAD,PUT,PATCH,POST,DELETE'
  response.headers['Access-Control-Expose-Headers'] = 'Content-Length'
  response.headers['Pragma'] = 'no-cache'
  response.headers['Expires'] = '-1'
  return response


@app.route('/upload', methods=['POST'])
def upload():
  """Receive and process an uploaded file."""
  _file = request.files.get('file')

  doc = document.get_document(
      _file.filename,
      parent=document.get_document('root'),
      need_secure_path=True)

  doc.tag = 'upload'

  _globals['gransk'].add_file(doc, file_object=_file)

  return Response('ok')


@app.route('/data', methods=['DELETE'])
def delete_data():
  """Clear all processed data."""
  _globals['gransk'].clear_all()

  return Response('ok')


@app.route('/file', methods=['GET'])
def get_file():
  """Get original file."""
  filename = document.secure_path(request.args['filename'])
  ext = document.secure_path(request.args['ext'])
  mediatype = request.args['mediatype']

  root = os.path.join(_globals['gransk'].config[helper.DATA_ROOT], 'files')
  file_path = os.path.join(root, ext, filename)

  if not os.path.exists(file_path):
    abort(404)

  with open(file_path, 'rb') as inp:
    return Response(inp.read(), mimetype=mediatype, status=200)


@app.route('/search')
def search():
  query = json.loads(request.args['q'])
  if 'type' in query:
    url = 'http://%s:9200/gransk/%s/_search?' % (_globals['config']['es_host'][0], query['type'])
  else:
    url = 'http://%s:9200/gransk/_search' % _globals['config']['es_host'][0]

  r = requests.get(url, data=json.dumps(query['body']))

  return Response(r.text, status=200, mimetype='application/json')


@app.route('/picture', methods=['GET'])
def picture():
  """Get document content as picture."""
  name = document.secure_path(request.args['name'])
  mediatype = request.args['mediatype']

  root = os.path.join(_globals['gransk'].config[helper.DATA_ROOT], 'pictures')
  image_path = os.path.join(root, name)

  if not os.path.exists(image_path):
    abort(404)

  with open(image_path, 'rb') as fp:
    return Response(fp.read(), mimetype=mediatype, status=200)


@app.route('/related', methods=['GET'])
def related():
  """Get related documents or entities."""
  if request.args['type'] == 'document':
    service = 'related_documents'
  elif request.args['type'] == 'entity':
    service = 'related_entities'
  else:
    return Response('Invalid type %s' % request.args['type'])

  if not _globals['gransk'].pipeline.get_service(service):
      return Response('{"error": "service not found"}', status=200, mimetype='application/json')

  result = _globals['gransk'].pipeline.get_service(service).get_related_to(
      request.args['id'])

  return Response(json.dumps(result), status=200, mimetype='application/json')


@app.route('/network', methods=['GET'])
def entity_network():
  """Get the generated network around a given entitiy ID."""
  hops = int(request.args.get('hops', 1))

  result = _globals['gransk'].pipeline.get_service('entity_network').get_for(
      request.args['entity_id'], hops=hops)

  return Response(json.dumps(result), status=200, mimetype='application/json')


@app.route('/saveall', methods=['GET'])
def saveall():
  """Save all related data to the disk."""

  for service in ('related_entities', 'related_documents'):
    if _globals['gransk'].pipeline.get_service(service):
      _globals['gransk'].pipeline.get_service(service).save_all()

  return Response('ok')


@app.route('/', methods=['GET'])
def home():
  """Get main page."""
  return app.send_static_file('index.html')


def parse_args():
  """Parse arguments provided from the command line."""
  parser = argparse.ArgumentParser()
  parser.add_argument('--host', default='127.0.0.1')
  parser.add_argument('--port', default=8084, type=int)
  parser.add_argument('--config', '-c', default=None)
  parser.add_argument('--debug', dest='debug', action='store_true')
  parser.set_defaults(debug=False)
  return parser.parse_args()


def setup(args, pipeline, runmod, injector):
  """Load configuration"""
  logging.basicConfig(
      format='[%(asctime)s] [%(levelname)s] %(name)s: %(message)s',
      level=logging.INFO,
      datefmt='%Y-%m-%d %H:%M:%S')

  _globals['gransk'] = gransk.api.API(injector)
  _globals['config'] = _globals['gransk'].config

  if pipeline:
    _globals['gransk'].pipeline = pipeline

  if _globals['gransk'].pipeline.get_service('related_entities'):
    _globals['gransk'].pipeline.get_service('related_entities').load_all(_globals['config'])

  if _globals['gransk'].pipeline.get_service('related_documents'):
    _globals['gransk'].pipeline.get_service('related_documents').load_all(_globals['config'])


if __name__ == '__main__':
  args = parse_args()
  setup(args, None, gransk.boot.run, gransk.core.injector.Injector())

  #context = ('/etc/letsencrypt/live/gransk.com/cert.pem', '/etc/letsencrypt/live/gransk.com/privkey.pem')
  context = None

  app.run(host=args.host, port=args.port, debug=args.debug, threaded=True, ssl_context=context)
  _globals['gransk'].pipeline.stop()