import datetime
import logging

from fastapi import FastAPI, UploadFile
from fastapi.responses import FileResponse
import pypandoc
import json
from fastapi.testclient import TestClient
import os
import shutil
import cv2

from DocumentSpecs import DocumentSpecs
from FormatParameters import FormatParameters
from convert_pdf import convert_pdf
from extract_emojis import replace_emojis
from list_dir import list_dir
from responses import Styles, Formats, App


def convert_video(images_path, output_path, width, height, fps, stilltime):
    """
    Convert images in output_path into a mp4 file usine OpenCV.
    :param images_path:
    :param output_path:
    :param images_path:
    :param width:
    :param height:
    :return:
    """

    # define a frame array
    frame_array = []

    # list all files in images_path
    files = [f for f in os.listdir(images_path) if os.path.isfile(os.path.join(images_path, f))]
    # sort the files
    files.sort()

    # create a video writer object

    for i in range(len(files)):
        file = os.path.join(images_path, files[i])
        logging.log(logging.INFO, f'Converting {file} to mp4')
        img = cv2.imread(file)
        for j in range(fps * stilltime):
            frame_array.append(img)

    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    video_writer = cv2.VideoWriter(output_path,
                                   fourcc,
                                   fps,
                                   (width, height))

    for i in range(len(frame_array)):
        # writing to a image array
        video_writer.write(frame_array[i])

    video_writer.release()
    logging.log(logging.INFO, f'Finished converting {output_path}')


app = FastAPI()


@app.get("/")
async def get_root():
    app = App(app='fabriquedoc')
    return app


@app.get("/styles/")
async def get_styles():
    styles = Styles(styles=list_dir("./styles"))
    return styles


@app.get("/formats/{style}/")
async def get_formats(style: str):
    formats = Formats(formats=list_dir(f"./styles/{style}/"))
    return formats


@app.get("/format_parameters/{style}/{format}/")
async def get_format_parameters(style: str, format: str):
    # open styles/format_parameters.json as a dictionary
    with open(f"./styles/{style}/format_parameters.json", "r") as f:
        format_data = json.load(f).get(format)
        logging.log(logging.INFO, str(format_data))
        # load data from format_data into the FormatParameters object
        parameters = FormatParameters(**format_data)
    return parameters


@app.get("/images/")
async def get_images():
    # list all files in resources/images
    files = [f for f in os.listdir("./resources/images") if os.path.isfile(os.path.join("./resources/images", f))]
    # sort the files
    files.sort()
    return {"images": files}


@app.get("/images/{nom_image}")
async def get_image(nom_image: str):
    return FileResponse(f"./resources/images/{nom_image}")


@app.post("/images/")
async def ajouter_image(file: UploadFile):
    """
    Add an image to the images folder.
    :param file:
    :return:
    """
    image_path = f"{os.getcwd()}/resources/images/{file.filename}"
    try:
        contents = file.file.read()
        with open(image_path, 'wb') as f:
            f.write(contents)
    except Exception as e:
        return {"message": f"There was an error uploading the file: {e}"}
    finally:
        file.file.close()

    return {"message": f"Successfully uploaded all files"}


@app.delete("/images/{nom_image}")
async def supprimer_image(nom_image: str):
    """
    Delete an image from the images folder.
    :param nom_image:
    :return:
    """
    image_path = f"{os.getcwd()}/resources/images/{nom_image}"
    try:
        os.remove(image_path)
    except Exception as e:
        return {"message": f"There was an error deleting the file: {e}"}
    finally:
        return {"message": f"Successfully deleted {nom_image}"}


@app.get("/generer/")
async def generer(specs: DocumentSpecs):
    header_file = f'{os.getcwd()}/styles/{specs.style}/{specs.format}/header.tex'
    cover_file = f'{os.getcwd()}/styles/{specs.style}/{specs.format}/cover.tex'
    datef = datetime.datetime.now().strftime("%Y-%m-%d")
    os.makedirs("out", exist_ok=True)
    filters = ['latex-emoji.lua', 'centered.lua']
    pdoc_args = [
        f'--include-in-header={header_file}',
        f'--include-after-body={cover_file}',
        '--listings',
        '--dpi=300',
        f'--toc-depth={specs.tocdepth}',
        f'--pdf-engine={specs.pdfengine}',
        f'--resource-path={os.getcwd()}/resources/',
        '-V', f'linkcolor={specs.linkcolor}',
        '-V', f'fontsize={specs.fontsize}pt',
        '-V', f'geometry:paperwidth={round(specs.paperwidth * specs.ratio / 100, -1) / 300}in',
        '-V', f'geometry:paperheight={round(specs.paperheight * specs.ratio / 100, -1) / 300}in',
        '-V', f'geometry:left={specs.margin / 300}in',
        '-V', f'geometry:right={specs.margin / 300}in',
        '-V', f'geometry:top={specs.vmargin / 300}in',
        '-V', f'geometry:bottom={specs.vmargin / 300}in'
    ]
    pdf_file_path = f"./out/{specs.style}-{specs.format}-{datef}-output.pdf"
    images_path = f"./out/{specs.style}-{specs.format}-{datef}-images"
    try:
        logging.info("Dossier courant = " + os.getcwd())

        text_to_convert = replace_emojis(specs.content)

        pypandoc.convert_text(source=text_to_convert,
                              to='pdf',
                              format='markdown+implicit_figures+smart+emoji',
                              encoding='utf-8',
                              extra_args=pdoc_args,
                              filters=filters,
                              cworkdir=os.getcwd(),
                              outputfile=pdf_file_path
                              )

    except RuntimeError as rerr:
        logging.exception(rerr)
    except OSError as oerr:
        logging.exception(oerr)
    if specs.extension in ["png", "jpg", "mp4"]:
        filename = os.path.join("out", os.path.splitext(os.path.basename(pdf_file_path))[0])
        if not os.path.exists(images_path):
            os.mkdir(images_path)
        conversion_extension = specs.extension
        output_extension = specs.extension
        if specs.extension in ["mp4"]:
            conversion_extension = "jpg"
        try:
            convert_pdf(pdf_file_path,
                        conversion_extension,
                        images_path,
                        resolution=300)
            if specs.extension in ["png", "jpg"]:
                shutil.make_archive(base_name=filename,
                                    format='zip',
                                    root_dir=images_path)
                output_extension = "zip"
                shutil.rmtree(images_path)
            if specs.extension in ["mp4"]:
                output_extension = "mp4"
                convert_video(images_path=images_path,
                              output_path=f"{filename}.{output_extension}",
                              width=specs.paperwidth,
                              height=specs.paperheight,
                              fps=specs.fps,
                              stilltime=specs.stilltime)
        except Exception as e:
            logging.exception(e)
        return FileResponse(f"{filename}.{output_extension}")
    elif specs.extension == "pdf":
        return FileResponse(pdf_file_path)
    else:
        return 0


client = TestClient(app)


def test_getroot():
    response = client.get("/")
    assert response.status_code == 200
