#!/usr/bin/python3
"""
This is a simple HTTP file server which runs on uWSGI.

It serves files out of /tftpboot, and hooks into Redis to run Jinja2
templating for device-specific variables on the files it serves.
"""

from jinja2 import Environment, FileSystemLoader
from jinja2.exceptions import TemplateNotFound

from remote_dop.provd import load_config
from remote_dop.target_device import TargetDevice, from_ip, from_mac
from remote_dop.ordered_provisioning import dependencies_satisfied
from remote_dop.util import normalise_host_addr
import remote_dop.file_serve
import os
import redis

# Global handle for the redis connection.
redis_port = 6379
if 'REDIS_PORT' in os.environ:
    redis_port = os.environ['REDIS_PORT']

REDIS_HANDLE = redis.StrictRedis(host="localhost", port=redis_port, db=0)


def application(env, start_response):
    """Read the file specified in the request path and return the contents,
    performing jinja2 templating if possible."""

    env_path = env['PATH_INFO']
    original_uri = env.get('REQUEST_URI', env['PATH_INFO'])
    response = None

    if original_uri.startswith("/files/") or original_uri.startswith("/tftpboot/"):
        request_prefix = "/tftpboot"
        serve_dir = '/tftpboot'
        jinja2_loader = FileSystemLoader('/tftpboot')
        rel_path = env_path.split("/", 2)[-1]
    else:
        request_prefix = "/"
        serve_dir = '/var/www/html'
        jinja2_loader = FileSystemLoader('/var/www/html')
        rel_path = env_path.lstrip("/")

    # Resolve the actual file path to serve
    jinja2_env = Environment(loader=jinja2_loader)
    actual_filename = remote_dop.file_serve.get_filename_to_serve(
        rel_path, serve_dir=serve_dir
    )

    if not actual_filename:
        start_response('404 Not Found', [])
        return []

    ipv4_address = None
    if 'HTTP_X_FORWARDED_FOR' in env:
        ipv4_address = env['HTTP_X_FORWARDED_FOR']
    elif 'HTTP_X_REAL_IP' in env:
        ipv4_address = env['HTTP_X_REAL_IP']
    else:
        start_response('500 Empty address', [])
        return []

    normalised_address = normalise_host_addr(ipv4_address)

    target_device = from_ip(TargetDevice(REDIS_HANDLE), normalised_address)
    config = load_config('/etc/provd.conf')

    # Provisioning dependencies only apply to tftpboot paths
    if request_prefix == "/tftpboot" and not dependencies_satisfied(REDIS_HANDLE, target_device, actual_filename, config):
        start_response('428 Provisioning Dependencies not Satisfied', [])
        return []

    # If file has .j2 extension, try to open it as a template. If this fails (e.g. binary file), return an error
    if actual_filename.endswith(".j2"):
        try:
            template = jinja2_env.get_template(actual_filename)
            if template is None:
                start_response('500 Bad Template', [])
                return []

            response = bytes(
                template.render(
                    nom_device_ipv4_address=target_device.ip,
                    nom_device_mac_address=target_device.mac,
                    nom_device_hostname=target_device.hostname,
                ),
                "utf-8",
            )
            start_response('200 OK', [('Content-Type', 'application/octet-stream'), ('Content-Length', str(len(response)))])
            return [response]

        except UnicodeDecodeError:
            start_response('500 Failed To Decode Template', [])
            return []

        except TemplateNotFound:
            start_response('404 Not Found', [])
            return []

    return remote_dop.file_serve.serve_file(start_response, os.path.join(serve_dir, actual_filename))
