# AUTOGENERATED! DO NOT EDIT! File to edit: 00_local.ipynb (unless otherwise specified).

__all__ = ['load_run_handler', 'handle_file_response', 'read_root', 'server_app', 'parse_cli_args', 'local_server']

# Cell
import os
import mimetypes
from io import BytesIO
import json
import importlib.util

import argparse

try:
    import fastapi
except:
    raise Exception(f"Dependancies missing.To use the tightai.local dev server, please install:\n\npip install fastapi uvicorn python-multipart\n\nor\n\npipenv install fastapi uvicorn python-multipart")

from fastapi import FastAPI, Body, File, UploadFile, Response, status
from starlette.requests import Request
from starlette.responses import StreamingResponse
from typing import List
from urllib.parse import parse_qs
from pathlib import Path
import uvicorn

global app_run

def load_run_handler(base_dir):
    global app_run
    entry_file='entry.py'
    base_dir = Path(base_dir)
    if base_dir.is_file():
        raise Exception(f"{base_dir} must be a directory")
    path = Path(base_dir) / entry_file
    if not path.exists() and not path.is_file():
        raise Exception(f"{path} not found. You must have `entry.py` in the root of your project.")
    spec = importlib.util.spec_from_file_location("entry", path)
    entry_module = importlib.util.module_from_spec(spec)
    try:
        spec.loader.exec_module(entry_module)
    except ModuleNotFoundError as e:
        # print(e.name, e.args)
        print(f"The package `{e.name}` is not found in your project's local path.\n\nIs it installed? \nDid you activate your virtual environment?\n\nAre you running the tight localserver in the root of your project?")
        return
    app_run = entry_module.run

# Cell
def handle_file_response(app_response):
    file_type = None
    file_obj = app_response['file']
    filename = app_response['filename']
    if  'file_type' in app_response:
        file_type = app_response['file_type']
    else:
        file_type, _ = mimetypes.guess_type(filename)
    if not isinstance(file_obj, BytesIO):
        return {
            "status": 500,
            "message": "File responses must be a `BytesIO` object."
        }
    file_obj.seek(0)
    headers = {
        'Content-Disposition': f'attachment; filename="{filename}"'
    }
    return StreamingResponse(file_obj, media_type=file_type, headers=headers)

# Cell
server_app = FastAPI()

@server_app.get("{path:path}")
@server_app.post("{path:path}")
@server_app.put("{path:path}")
@server_app.delete("{path:path}")
async def read_root(path: str,
        request: Request,
        response: Response
    ):
    headers = request.headers
    query_params = parse_qs(str(request.query_params))
    method = request.method
    json_data = {}
    try:
        json_data = await request.json()
    except:
        pass
    form = await request.form()
    form_data = {}
    file = None
    metadata = {}
    if form != None:
        form_data = dict(form)
        if 'file' in form_data:
            file = form['file'].file.read()
            metadata['filename'] = form['file'].filename
            metadata['file_size'] = len(file)
            del form_data['file']
    app_run_data = {
        "json_data": json_data,
        "query_params": query_params,
        "path": path,
        "form_data": form_data,
        "file": file,
        "headers": headers,
        "method": method,
        "metadata": metadata
    }
    try:
        app_response =  app_run(**app_run_data)
    except Exception as e:
        app_response = {
            "status": 500,
            "message": "An error occured with your application.",
            "exception": f"{e}"
        }
    if isinstance(app_response, dict):
        '''
        Return application/json data
        '''
        response.media_type = 'application/json'
        if 'file' in app_response and 'filename' in app_response and 'force_download' in app_response:
            '''
            Return a file download response.
            '''
            return handle_file_response(app_response)
        if "status" in app_response:
            '''
            Adjust the response status code based on app's response.
            '''
            status = app_response['status']
            response.status_code = status
            del app_response['status']
        if "media_type" in app_response:
            '''
            Change reponse media_type based on on app's response.
            '''
            media_type = app_response['media_type']
            response.media_type = media_type
            del app_response['media_type']
        return app_response
    else:
        app_response = {
            "status": 500,
            "message": "An error occured with your application. It was created incorrectly."
        }
    return app_response

# Cell
def parse_cli_args():
    parser = argparse.ArgumentParser(description='Process some integers.')
    parser.add_argument('--port', type=int, default=5007, help='Default port for the local development server')
    parser.add_argument(
        "--dir",
        type=Path,
        default=Path("."),
        help="Directory path to the the location for your `entry.py` module; typically in the root of your project.",
    )
    path = Path(".")
    port = 5007
    try:
        args = parser.parse_args()
        path = args.dir
        port = args.port
    except:
        pass
    return path, port

def local_server(path=".", port='5007'):
    load_run_handler(path)
    uvicorn.run(server_app, host="localhost", port=port)

# Cell
if __name__ == "__main__":
    path, port = parse_cli_args()
    if path and port:
        local_server(path=path, port=port)