import zipfile
import os
from pathlib import Path
from dataclasses import dataclass, asdict

from django.conf import settings
from django.template.loader import render_to_string

from weasyprint import HTML, CSS

from ..utils.static import find_static
from ..utils.pdf import has_libreoffice, doc_to_pdf, merge_pdfs


@dataclass(frozen=True)
class FormFieldValue:
    type: str
    label: str
    value: str


@dataclass(frozen=True)
class SummaryContext:

    logo_path: str
    first_name: str
    last_name: str
    email: str
    call_title: str
    call_closing_date: str
    call_coordinator_email: str
    submission_date: str
    facilities: list[str]
    dates_flexible: str
    fields: list[FormFieldValue]
    chosen_facility: str | None = None
    start_date: str | None = None
    end_date: str | None = None


def get_summary_context(instance) -> SummaryContext:

    fields = []
    for v in instance.form.values.all():
        match (v.field.field_type):
            case "BOOLEAN":
                value = "Yes" if v.value == "TRUE" else "No"
            case "FILE":
                suffix = Path(v.asset.name).suffix
                value = f"Included file: {v.field.label}{suffix}"
            case _:
                value = v.value

        fields.append(
            FormFieldValue(type=v.field.field_type, label=v.field.label, value=value)
        )

    return SummaryContext(
        logo_path=str(find_static("logo.png")),
        first_name=instance.applicant.first_name,
        last_name=instance.applicant.last_name,
        email=instance.applicant.email,
        call_title=instance.call.title,
        call_coordinator_email=instance.call.coordinator.email,
        call_closing_date=instance.call.closing_date.date(),
        submission_date=instance.submitted.date(),
        facilities=[f.name for f in instance.facilities.all()],
        chosen_facility=(
            instance.chosen_facility.name if instance.chosen_facility else None
        ),
        start_date=(
            instance.request_start_date.date() if instance.request_start_date else None
        ),
        end_date=(
            instance.request_end_date.date() if instance.request_end_date else None
        ),
        dates_flexible="Yes" if instance.dates_flexible else "No",
        fields=fields,
    )


def get_summary_files(instance) -> list[tuple[str, str]]:

    files = []
    for value in instance.form.values.all():
        if value.field.field_type == "FILE" and value.asset:
            suffix = Path(value.asset.name).suffix
            files.append(
                (
                    Path(settings.MEDIA_ROOT) / value.asset.name,
                    f"{value.field.label}{suffix}",
                )
            )
    return files


def make_zip_archive(path: Path, pdf, file_paths: list):

    os.makedirs(path.parent, exist_ok=True)
    with zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED, False) as f:
        for path, arcname in file_paths:
            f.write(path, arcname=arcname)
        f.writestr("application_summary.pdf", pdf)


def make_combined_pdf(output_path: Path, pdf, file_paths: list):

    os.makedirs(output_path.parent, exist_ok=True)
    tmp_path = output_path.parent / (str(output_path.stem) + "_temp.pdf")
    with open(tmp_path, "wb") as f:
        f.write(pdf)
        f.flush()

    to_merge = [tmp_path]
    for path, _ in file_paths:
        if path.suffix.lower() in (".doc", ".docx", ".odt"):
            doc_to_pdf(path)
            to_merge.append(Path(str(path).split(".")[0] + ".pdf"))
        elif path.suffix.lower() == ".pdf":
            to_merge.append(path)
    merge_pdfs(output_path, to_merge)
    # tmp_path.unlink()


def generate_summary(instance, output_field: str = "summary"):

    html = render_to_string(
        "marinerg_test_access/application_summary.html",
        asdict(get_summary_context(instance)),
    )

    pdf = HTML(string=html).write_pdf(stylesheets=[CSS(find_static("style.css"))])

    field_path = f"{instance._meta.model_name}_{output_field}"
    if has_libreoffice():
        filename = f"{field_path}_{instance.id}.pdf"
        make_combined_pdf(
            Path(settings.MEDIA_ROOT) / field_path / filename,
            pdf,
            get_summary_files(instance),
        )
    else:
        filename = f"{field_path}_{instance.id}.zip"
        make_zip_archive(
            Path(settings.MEDIA_ROOT) / field_path / filename,
            pdf,
            get_summary_files(instance),
        )

    instance.summary.name = os.path.join(field_path, filename)
