#!/bin/python3
'''
Web app to offer a frontend to the doc2tosca and tosca2doc tools

'''

import os
import shutil
import tempfile
import logging
import traceback
import sys
sys.dont_write_bytecode = True
from zipfile import ZipFile

from flask import Flask, flash, request, redirect, send_file, render_template, g
from werkzeug.utils import secure_filename
import docx

import config
import tosca2doc
import doc2tosca


class ReverseProxied(object):

    def __init__(self, the_app, script_name=None, scheme=None, server=None):
        self.app = the_app
        self.script_name = script_name
        self.scheme = scheme
        self.server = server

    def __call__(self, environ, start_response):
        script_name = environ.get('HTTP_X_SCRIPT_NAME', '') or self.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', '') or self.scheme
        if scheme:
            environ['wsgi.url_scheme'] = scheme
        server = environ.get('HTTP_X_FORWARDED_HOST', '') or self.server
        if server:
            environ['HTTP_HOST'] = server
        return self.app(environ, start_response)


app = Flask(__name__)
app.wsgi_app = ReverseProxied(app.wsgi_app, script_name='/tosca-ie')

UPLOAD_FOLDER = '/tmp/upload'

ALLOWED_EXTENSIONS = set(['txt', 'yaml', 'docx'])

app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.secret_key = config.SECRET
app.config['SESSION_TYPE'] = 'filesystem'

TOSCA_DEFS = ['VNFD', 'NSD', 'PNFD']

MISSING_MODULE_ERR_MSG = \
    'Error: some TOSCA module missing. Make sure to \
    upload definitions for NSD, VNFD, PNFD.'


def allowed_file(filename):
    '''
    Check filename is in the list of allowed extensions
    '''
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


@app.route("/")
def hello():
    '''
    Render home page
    '''
    if config.LAST_COMMIT is not False:
        link = config.REPO_URL+config.LAST_COMMIT.split(" ")[0]
    else:
        link = "#"

    return render_template(
        "./home.html",
        VERSION=config.VERSION,
        doc_allowed_versions=doc2tosca.allowed_versions,
        default_tosca_version=doc2tosca.DEFAULT_TOSCA_VERSION,
        last_rev=config.LAST_COMMIT,
        rev_link=link
    )


@app.route("/doc2tosca-info")
def doc2tosca_info():
    '''
    Render home page
    '''
    return render_template("./doc2tosca-details.html", VERSION=config.VERSION)


@app.route("/tosca2doc-info")
def tosca2doc_info():
    '''
    Render home page
    '''
    return render_template("./tosca2doc-details.html", VERSION=config.VERSION)


@app.route("/tosca2doc", methods=['POST'])
def mk_tosca2doc():
    '''
    Execute tosca2doc on the uploaded files
    '''

    if 'file' not in request.files:
        flash('Error: No file uploaded.')
        return redirect("/tosca-ie")

    ufiles = request.files.getlist("file")

    for tosca_def in TOSCA_DEFS:
        found = False
        for uploaded_file in ufiles:
            if tosca_def in uploaded_file.filename or \
               tosca_def.lower() in uploaded_file.filename:
                found = True
        if not found:
            flash(MISSING_MODULE_ERR_MSG)
            print('Error: TOSCA module missing.')
            return redirect("/tosca-ie")

    for file in ufiles:
        if file.filename == '':
            print('Error: No selected file')
            flash('Error: No file uploaded or wrong name.')
            return redirect("/tosca-ie")

    tmp_dir = tempfile.mkdtemp()
    g.tmp_dir = tmp_dir

    doc = docx.Document()

    for tosca_def in TOSCA_DEFS:
        for file in ufiles:
            if tosca_def in uploaded_file.filename or \
                tosca_def.lower() in uploaded_file.filename:
                filename = secure_filename(file.filename)
                filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
                file.save(filepath)
                file.close()

                tosca2doc.generate_from_file(tosca_def, doc, filepath)

    outfilename = os.path.join(tmp_dir, 'myfile.docx')

    doc.save(outfilename)
    return send_file(outfilename)

    #return ("No content found")


def get_all_yaml_file_paths_and_logs(directory):
    '''
    Finds yaml files within a directory and sub directories and
    returns the list of paths
    '''

    file_paths = []

    for root, _, files in os.walk(directory):
        for filename in files:
            if ".yaml" in filename or ".log" in filename:
                filepath = os.path.join(root, filename)
                file_paths.append(filepath)

    return file_paths


@app.after_request
def after_request(response):
    '''
    Clean up files created by doc2tosca
    '''
    if request.path != '/doc2tosca':
        return response
    if g.get('tmp_dir') is None:
        return response
    shutil.rmtree(g.tmp_dir, ignore_errors=True)
    logging.debug("Deleted {}\n\n".format(g.tmp_dir))
    return response


@app.route("/doc2tosca", methods=['POST'])
def mk_doc2tosca():
    '''
    Executes doc2tosca on the uploaded file
    '''

    tmp_dir = tempfile.mkdtemp()

    logfilename = os.path.join(tmp_dir, 'toscatools.log')
    logging.basicConfig(filename=logfilename, force=True, level=logging.INFO)

    logging.info("Starting")
    logging.debug("TMP DIR: " + tmp_dir)

    zip_fn = "tosca_defs.zip"
    zip_path = os.path.join(tmp_dir, zip_fn)

    if 'file' not in request.files:
        flash('No file uploaded')
        return redirect("/tosca-ie")

    ufiles = request.files.getlist("file")

    try:
        logging.debug(request.form)
        doc_version = request.form.get('doc-version')
        custom_doc_version = request.form.get('custom-doc-version')
        custom_tosca_version = request.form.get('custom-tosca-version')
        yaml_root_path = request.form.get('yaml-root-path')
    except:
        flash("Something went wrong :/")
        return redirect("/tosca-ie")

    if doc_version not in doc2tosca.allowed_versions:
        flash('Incorrect doc version selected')
        return redirect("/tosca-ie")

    sol001_file = ufiles[0]

    tosca_version = doc2tosca.DEFAULT_TOSCA_VERSION

    if custom_doc_version != "":
        doc_version = custom_doc_version

    if custom_tosca_version != "":
        tosca_version = custom_tosca_version

    try:
        doc2tosca.generate_templates(
            sol001_file, doc_version, yaml_root_path, tosca_version
        )
        doc2tosca.print_to_files(tmp_dir)

    except ValueError as e:
        flash(str(e))
        return redirect("/tosca-ie")
    except BaseException as e:
        print(e)
        # logging.error(traceback.print_exc())
        flash(str(e))
        flash("Unknown error in the generation of the files. \
               Please contact the support.")
    finally:
        file_paths = get_all_yaml_file_paths_and_logs(tmp_dir)

        with ZipFile(zip_path, 'w') as archive:
            for myfile in file_paths:
                print(myfile)
                logging.info(myfile)
                archive.write(myfile, arcname=os.path.basename(myfile))

        g.tmp_dir = tmp_dir
        return send_file(zip_path, as_attachment=True)


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