import datetime
import os
import base64

from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import HTMLResponse, FileResponse
from fastapi.templating import Jinja2Templates

from weasyprint import HTML, CSS
from weasyprint.text.fonts import FontConfiguration

from pathlib import Path

from app.entity.models import Entity
from app.template.models import ProvisionTemplate
from ..models import ContractDraft, Contract, ContractStatus, replace_variables_in_value


async def build_model(model):
    parties = []
    for p in model.parties:
        party = {
            "entity": await Entity.get(p.entity_id),
            "part": p.part
        }
        if p.representative_id:
            party['representative'] = await Entity.get(p.representative_id)

        parties.append(party)

    model.parties = parties

    provisions = []
    for p in model.provisions:
        if p.provision.type == "template":
            provision = await ProvisionTemplate.get(p.provision.provision_template_id)
        else:
            provision = p.provision

        provision.title = replace_variables_in_value(model.variables, provision.title)
        provision.body = replace_variables_in_value(model.variables, provision.body)
        provisions.append(provision)

    model.provisions = provisions

    model = model.dict()
    model['location'] = "Los Santos, SA"
    model['date'] = datetime.date(1970, 1, 1)
    model['lawyer'] = {'entity_data': {
        "firstname": "prénom avocat",
        "lastname": "nom avocat",
    }}
    return model


BASE_PATH = Path(__file__).resolve().parent

print_router = APIRouter()


templates = Jinja2Templates(directory=str(BASE_PATH / "templates"))


async def render_print(root_url, contract):
    template = templates.get_template("print.html")
    return template.render({
        "contract": contract,
        "root_url": root_url
    })


async def render_css(root_url, contract):
    template = templates.get_template("styles.css")
    return template.render({
        "contract": contract,
        "root_url": root_url
    })


@print_router.get("/preview/draft/{draft_id}", response_class=HTMLResponse)
async def preview_draft(draft_id: str, request: Request) -> str:
    draft = await build_model(await ContractDraft.get(draft_id))

    return await render_print('', draft)


@print_router.get("/preview/signature/{signature_id}", response_class=HTMLResponse)
async def preview_contract_by_signature(signature_id: str, request: Request) -> str:
    contract = await Contract.find_by_signature_id(signature_id)
    for p in contract.parties:
        if p.signature_affixed:
            p.signature_png = retrieve_signature_png(f'media/signatures/{p.signature_uuid}.png')

    return await render_print('', contract)


@print_router.get("/preview/{contract_id}", response_class=HTMLResponse)
async def preview_contract(contract_id: str, request: Request) -> str:
    contract = await Contract.get(contract_id)
    for p in contract.parties:
        if p.signature_affixed:
            p.signature_png = retrieve_signature_png(f'media/signatures/{p.signature_uuid}.png')

    return await render_print('', contract)


@print_router.get("/pdf/{contract_id}", response_class=FileResponse)
async def create_pdf(contract_id: str) -> str:
    contract = await Contract.get(contract_id)
    contract_path = "media/contracts/{}.pdf".format(contract_id)
    if not os.path.isfile(contract_path):
        if contract.status != ContractStatus.signed:
            raise HTTPException(status_code=400, detail="Contract is not in a printable state")

        for p in contract.parties:
            signature_path = f'media/signatures/{p.signature_uuid}.png'
            p.signature_png = retrieve_signature_png(signature_path)
            # os.remove(signature_path)

        font_config = FontConfiguration()
        html = HTML(string=await render_print('http://nginx', contract))
        css = CSS(string=await render_css('http://nginx', contract), font_config=font_config)

        html.write_pdf(contract_path, stylesheets=[css], font_config=font_config)
        update_query = {"$set": {
            'status': 'printed'
        }}
        await contract.update(update_query)

    return FileResponse(
        contract_path,
        media_type="application/pdf",
        filename=contract.name)


def retrieve_signature_png(filepath):
    with open(filepath, "rb") as f:
        b_content = f.read()
    base64_utf8_str = base64.b64encode(b_content).decode('utf-8')
    ext = filepath.split('.')[-1]
    return f'data:image/{ext};base64,{base64_utf8_str}'
