#!/usr/bin/env python

import os
from flask import Flask, g, abort, jsonify, render_template
from time import time, strftime
from functools import wraps
from flask import request, send_from_directory, redirect, url_for, Response
from werkzeug.routing import BaseConverter
from passlib.hash import sha256_crypt
from itsdangerous import (TimedJSONWebSignatureSerializer
  as Serializer, BadSignature, SignatureExpired)
import requests

# initialization
base = os.path.dirname(os.path.realpath(__file__))

app = Flask(__name__)
app.config['SECRET_KEY'] = 'h&#2Fvi9>4@_Cw/j/*)\_K81~.dN$f5!r%<Z$63|;1l=_'
app.config['METADATA_URL'] = 'http://192.168.92.7:8084/mtpublic/%s'
app.config['TOKEN_URL'] = 'http://192.168.92.7:8084/mtpublic/_design/clients/_view/all/?key="%s"'

class RegexConverter(BaseConverter):
  def __init__(self, url_map, *items):
    super(RegexConverter, self).__init__(url_map)
    self.regex = items[0]

app.url_map.converters['regex'] = RegexConverter

def check_auth(username, password=None, hash=None):
  """Check if (username, password) or (username, hash) is valid."""
  print "Checking auth...", username, password, hash
  try:
#    resp = requests.get(app.config['TOKEN_URL'] % username, timeout=3)
    digest = sha256_crypt.encrypt(password) if password else hash
#    if resp.ok and \
#       len(resp.json()['rows'])==1 and \
#       sha256_crypt.verify(str(resp.json()['rows'][0]['value']['password']), digest):
#      return generate_token(str(resp.json()['rows'][0]['value']['_id']), username, digest)
    if username=="sohrabjk" and sha256_crypt.verify('nnch', digest):
      return generate_token(1, username, digest)
    return None
  except:
    return None

def generate_token(id, username, hash, expiration=600):
  s = Serializer(app.config['SECRET_KEY'], expires_in=expiration)
  return s.dumps({'id': id, 'username': username, 'hash': hash})

def untoken(token, headers=False):
  s = Serializer(app.config['SECRET_KEY']);
  (token, header) = s.loads(token if token is not None else "", return_header=True)
  return (token, header) if headers else token

def check_token(token):
  print "Checking token..."
  try:
    s = Serializer(app.config['SECRET_KEY']);
    (token, header) = untoken(token, True)
    if header['exp'] - int(time()) < 60:
     return check_auth(token['username'], hash=token['hash'])
    return generate_token(token['id'], token['username'], token['hash'])
  except SignatureExpired:
    print "Signature expired"
    return False  # Valid token, but expired
  except BadSignature:
    print "Bad signature"
    return False  # Invalid token

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

def requires_auth(f):
  @wraps(f)
  def decorated(*args, **kwargs):
    auth = request.authorization
    print 'Authorization: ', auth
    token = check_token(request.cookies.get('token')) or \
      (auth and check_auth(auth.username, auth.password))
    if token:
      g._token = token
      return f(*args, **kwargs)
    else:
      return authenticate()
  return decorated

@app.before_request
def before_request():
  g.start = time()

@app.after_request
def apply_caching(response):
  if g.get('_token', None):
    response.set_cookie('token', value=g._token, max_age=600)
  else:
    response.set_cookie('token', value='')
  if g.get('start', None):
    response.headers['X-Runtime'] = "%1.1f ms" % ((time() - g.start)*1000)
  response.headers['Cache-Control'] = "max-age=0, no-cache, no-store, must-revalidate"
  response.headers['Expires'] = "Thu, 29 Nov 1984 19:40:00"
  response.headers['Pragma'] = "no-cache"
  response.headers['Run-in'] = os.path.dirname(os.path.abspath(__file__))
  response.headers['CWD'] = os.path.dirname(os.getcwd())

  return response

@app.route('/logout')
@requires_auth
def logout():
  if g.get('_token', None):
    g._token = None
  return redirect(url_for('index'), 302)

@app.route('/tracking/<identifier>')
#@requires_auth
def index(identifier):
  return render_template('layout.html', identifier=identifier)

@app.route('/id/<identifier>')
def identification(identifier):
  return render_template('identification.html', identifier=identifier)

@app.route('/stocks/')
def query_stocks():
  from json import dumps
  json = dumps([
      { "id": 1, "description": "STOCK PRINCIPAL", "default": True },
      { "id": 2, "description": "STOCK SECONDAIRE", "default": False },
      { "id": 3, "description": "EN COURS OF", "default": False },
      { "id": 4, "description": "STOCK SAV", "default": False },
      { "id": 5, "description": "STOCK VENTE COMPOSANTS", "default": False },
      { "id": 6, "description": "STOCK ENTREE", "default": False },
      { "id": 7, "description": "STOCK RETOUR", "default": False },
      { "id": 8, "description": "STOCK RD", "default": False },
      { "id": 9, "description": "STOCK TRANSIT", "default": False },
      { "id": 10, "description": "STOCK DEMO-OCCASION", "default": False }])

  return Response(json, mimetype='application/json')

def to_csv(path):
  return ",".join(str(path).split("/"))

@app.route('/print/<path:varargs>', methods=['GET', 'POST'])
def print_articles(varargs):
  target = "/tmp/print.xml"
  output = fetch_articles(to_csv(varargs))
  with open(target, "w") as print_file:
    print_file.write(output)

  from subprocess import call, check_call
  from shlex import split
  from os import listdir, path, system

  try:
    xslt = "/var/www/tracking/xslt/items.xslt"
    shell = "xsltproc %s %s > /tmp/items.html" % (xslt, target)
    check_call(shell, shell=True)

    shell = "wkhtmltopdf --page-width 62mm --page-height 25mm -T 0 -R 0 -B 0 -L 0 /tmp/items.html /tmp/items.pdf"
    check_call(shell, shell=True)

    shell = "lpr -P QL720NW1 -o media=om_br-l0-b3-e0193374-a_61.91x24.96mm /tmp/items.pdf"
    check_call(shell, shell=True)

    if request.method == 'GET':
      return send_from_directory('/tmp/', 'items.pdf')
    else:
      return ('', 204)
  except:
    return abort(500)
  

def fetch_articles(articles):
  import shlex, subprocess
  shell = """mysql -u F5VksfQsYW -h 192.168.10.15 -pmWaq9FiGC6\
  --default-character-set=utf8\
  --skip-column-names\
  --raw\
  --execute="CALL article_query('%s', 'GER')"\
  dfnesa
  """ % articles
  args = shlex.split(shell)
  output = subprocess.check_output(args)
  return output

@app.route('/articles/<path:varargs>')
def query_articles(varargs):
  output = fetch_articles(to_csv(varargs))

  return Response(output, mimetype='text/xml')

@app.route('/article/<article>')
def query_article(article):
  output = fetch_articles(article)

  import xml.etree.ElementTree as ET
  root = ET.fromstring(output)

  try:
    out = {
      "identifier": root[0].attrib['identifier'],
      "value": root[0].attrib['value'],
      "currency": root[0].attrib['currency'],
      "quantity": root[0].attrib['stocks'],
      "description": root[0][0].text,
      "stocks": []
    }
    for stock in root[0].findall('stock'):
      out['stocks'].append({
        "id": stock.attrib['id'],
        "quantity": stock.attrib['quantity'],
        "location": stock.attrib['location']
      })
    return jsonify(out)
  except:
    abort(404)

def to_xml(inventory, source):
  import json
  import xml.etree.ElementTree as ET

  def ifnull(key, el, default):
    return default if key not in el else el[key]

  def as_xml(position):
    article = position['article']
    p = ET.Element('position')
    p.set('source', source)
    p.set('active', ifnull('active', position, 'true'))
    p.set('uuid', position['uuid'])
    p.set('createdOn', position['createdOn'])
    p.set('article', str(article['identifier']))
    p.set('stock', str(ifnull('stock', position, '')))
    p.set('location', str(ifnull('location', position, '')))
    p.set('quantity', str(ifnull('quantity', position, '')))
    p.text = article['description']
    return p

  root = ET.Element('inventory')
  for position in inventory:
    p = as_xml(position)
    root.append(p)

  return ET.ElementTree(root)

@app.route("/id/<string:identifier>/", methods=['POST'])
def post_inv(identifier):
  import json
  payload = json.loads(request.data)

  subdir = ['commits'] + [identifier] + [strftime("%Y%m%d")]
  directory = os.path.join(base, *subdir)
  if not os.path.exists(directory):
    os.makedirs(directory)

  filename = "commit_%s.json" % (strftime("%H%M%S"))
  file = open(os.path.join(directory, filename), "wb")
  file.write(request.data)
  file.close()

  xml = to_xml(payload, identifier)
  filename = "commit_%s.xml" % (strftime("%H%M%S"))
  file = open(os.path.join(directory, filename), "wb")
  xml.write(file, encoding="UTF-8", xml_declaration=True)
  file.close()

  import subprocess, shlex
  shell = """%s -i -o -w %s""" % (os.path.join(base, 'sql/xml2sql.sh'), os.path.join(directory, filename))
  subprocess.Popen((shlex.split(shell)))

  return "Ahoy, capt'n"


@app.route('/token', methods=['GET'])
@requires_auth
def token():
  return jsonify({'id': untoken(g._token)['id'] })

@app.route('/metadata/<id>', methods=['GET'])
@requires_auth
def metadata(id):
  if id!=untoken(g._token)['id']:
    abort(403)
  resp = requests.get(app.config['METADATA_URL'] % id, timeout=3)
  if resp.ok:
    return jsonify(resp.json())
  else:
    abort(500)

@app.route("/list/<int:list_type>/<regex('([A-Z0-9]{7,10})'):identifier>/", methods=['GET'])
@app.route("/list/<int:list_type>/<regex('([A-Z0-9]{7,10})'):identifier>/<regex('(\d{4}-\d{2}-\d{2})'):date_from>/<regex('(\d{4}-\d{2}-\d{2})'):date_until>/", methods=['GET'])
def sample_data(list_type, identifier, date_from=None, date_until=None):
  import os, shlex

  def ifnull(var, val):
    if var is None:
      return val
    return var

  shell = "xslt/fetch.sh %s %s %s %s" % (list_type, identifier, ifnull(date_from, ''), ifnull(date_until, ''))
  os.system(os.path.join(os.path.dirname(__file__), shell))

  return send_from_directory(os.path.join(os.path.dirname(__file__), 'xslt'), filename='mt_batches_summary2.json')

@app.route("/<regex('(font|fonts)'):head>/<resource>")
def get_images(head, resource):
  return redirect(url_for('static', filename='fonts/'+resource))

###
# Proxy wrapper class to allow Flask to run behind a reverse
# proxy (such as Apache)
#
class ReverseProxied(object):
  def __init__(self, app):
    self.app = app

  def __call__(self, environ, start_response):
    script_name = environ.get('HTTP_X_SCRIPT_NAME', '')
    if script_name:
      environ['SCRIPT_NAME'] = script_name
      path_info = environ['PATH_INFO']
      if path_info.startswith(script_name):
        environ['PATH_INFO'] = path_info[len(script_name):]

    scheme = environ.get('HTTP_X_SCHEME', '')
    if scheme:
      environ['wsgi.url_scheme'] = scheme
    return self.app(environ, start_response)

app.wsgi_app = ReverseProxied(app.wsgi_app)

if __name__ == '__main__':
  app.run(host='0.0.0.0', port=7000, debug=True)
