from __future__ import annotations

from datetime import datetime
from decimal import Decimal
from io import BytesIO
from pathlib import Path
from textwrap import wrap

from reportlab.lib.colors import HexColor, black, white
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import mm
from reportlab.lib.utils import ImageReader
from reportlab.pdfbase.pdfmetrics import stringWidth
from reportlab.pdfgen import canvas

from .models import Intervention, InterventionQuote, InterventionRequest, RequestType


ASSET_ROOT = Path(__file__).resolve().parents[2] / "frontend" / "assets" / "logos"
YELLOW = HexColor("#fff200")
LIGHT_GREY = HexColor("#f1f1f1")
MID_GREY = HexColor("#6a6a6a")

TYPE_LINES = {
    RequestType.public: ["AU SEIN D'UN ESPACE PUBLIC"],
    RequestType.prive_accord: ["AU SEIN D'UN ESPACE PRIVÉ", "AVEC ACCORD DU PROPRIÉTAIRE"],
    RequestType.prive_sans_accord: [
        "AU SEIN D'UN ESPACE PRIVÉ",
        "SANS ACCORD DU PROPRIÉTAIRE",
        "EN PRÉSENCE D'UN DANGER GRAVE ET IMMINENT POUR LA SANTÉ PUBLIQUE",
    ],
}

TARIFF_FALLBACKS = [
    ("perche_fusil", "Utilisation perche et fusil", Decimal("120.00")),
    ("drone", "Utilisation drone", Decimal("170.00")),
]


def _money(value: Decimal | float | int | None) -> str:
    if value is None:
        return ""
    amount = Decimal(str(value)).quantize(Decimal("0.01"))
    formatted = f"{amount:,.2f}".replace(",", " ").replace(".", ",")
    return f"{formatted} €"


def _date(value) -> str:
    if not value:
        return ""
    if isinstance(value, datetime):
        return value.astimezone().strftime("%d/%m/%Y")
    return value.strftime("%d/%m/%Y")


def _time(value) -> str:
    if not value:
        return ""
    return value.astimezone().strftime("%H:%M") if isinstance(value, datetime) else ""


def _draw_logo(c: canvas.Canvas, height: float) -> None:
    logo = ASSET_ROOT / "ternoiscom.png"
    if logo.exists():
        c.drawImage(
            ImageReader(str(logo)),
            18 * mm,
            height - 34 * mm,
            width=50 * mm,
            height=21 * mm,
            preserveAspectRatio=True,
            mask="auto",
        )
    else:
        c.setFont("Helvetica-Bold", 14)
        c.drawString(18 * mm, height - 24 * mm, "TERNOISCOM")


def _center_underlined(c: canvas.Canvas, text: str, y: float, size: float = 13) -> float:
    c.setFillColor(black)
    c.setFont("Helvetica-Bold", size)
    width = stringWidth(text, "Helvetica-Bold", size)
    x = (A4[0] - width) / 2
    c.drawString(x, y, text)
    c.setLineWidth(0.8)
    c.line(x, y - 1.3 * mm, x + width, y - 1.3 * mm)
    return y - 9 * mm


def _center_highlight(c: canvas.Canvas, text: str, y: float, size: float = 13) -> float:
    c.setFont("Helvetica-Bold", size)
    width = stringWidth(text, "Helvetica-Bold", size)
    x = (A4[0] - width) / 2
    c.setFillColor(YELLOW)
    c.rect(x - 1.5 * mm, y - 1.8 * mm, width + 3 * mm, 6.8 * mm, fill=1, stroke=0)
    c.setFillColor(black)
    c.drawString(x, y, text)
    c.setLineWidth(0.7)
    c.line(x, y - 1.1 * mm, x + width, y - 1.1 * mm)
    return y - 8.3 * mm


def _dotted_value(c: canvas.Canvas, label: str, value: str | None, y: float, *, x=15 * mm, end=195 * mm) -> float:
    c.setFont("Helvetica", 10)
    c.setFillColor(black)
    c.drawString(x, y, label)
    label_width = stringWidth(label, "Helvetica", 10)
    start = x + label_width + 2 * mm
    c.setDash(1, 1.7)
    c.line(start, y - 1 * mm, end, y - 1 * mm)
    c.setDash()
    if value:
        c.setFillColor(white)
        c.rect(start - 0.5 * mm, y - 2.2 * mm, end - start + 1 * mm, 4.4 * mm, fill=1, stroke=0)
        c.setFillColor(black)
        c.setFont("Helvetica-Bold", 10)
        c.drawString(start, y, str(value)[:115])
    return y - 15 * mm


def _paragraph(c: canvas.Canvas, text: str, y: float, *, italic=False, bold=False, width=108, leading=5.3 * mm) -> float:
    font = "Helvetica-Bold" if bold else ("Helvetica-Oblique" if italic else "Helvetica")
    c.setFont(font, 9.5)
    c.setFillColor(black)
    for line in wrap(text, width=width):
        c.drawString(15 * mm, y, line)
        y -= leading
    return y


def _tariff_box(c: canvas.Canvas, y: float, tariffs=None) -> float:
    rows = tariffs or TARIFF_FALLBACKS
    box_x = 15 * mm
    box_w = 91 * mm
    box_h = 43 * mm
    c.setStrokeColor(black)
    c.setLineWidth(0.7)
    c.rect(box_x, y - box_h, box_w, box_h, fill=0, stroke=1)
    c.setFont("Helvetica", 9.5)
    c.drawString(box_x + 3 * mm, y - 6 * mm, "Rappel des tarifs pour la prestation de")
    c.line(box_x + 3 * mm, y - 7 * mm, box_x + 72 * mm, y - 7 * mm)
    c.drawString(box_x + 3 * mm, y - 14 * mm, "service FRELONS de TERNOISCOM")
    c.line(box_x + 3 * mm, y - 15 * mm, box_x + 69 * mm, y - 15 * mm)
    line_y = y - 23 * mm
    for _code, label, price in rows[:2]:
        c.circle(box_x + 10 * mm, line_y + 0.8 * mm, 0.8 * mm, fill=1, stroke=0)
        c.drawString(box_x + 16 * mm, line_y, f"{label} : {_money(price)}")
        line_y -= 8 * mm
    c.drawString(box_x + 3 * mm, y - 39 * mm, "facturés à la commune")
    return y - box_h - 10 * mm


def _signature_boxes(c: canvas.Canvas, y: float, commune: str) -> None:
    x1, x2 = 22 * mm, 111 * mm
    w, h = 77 * mm, 43 * mm
    for x in (x1, x2):
        c.rect(x, y - h, w, h, fill=0, stroke=1)
    c.setFont("Helvetica", 10)
    c.drawString(x1 + 3 * mm, y - 7 * mm, "Signature,")
    c.drawString(x1 + 3 * mm, y - 15 * mm, "Le Responsable Technique de")
    c.drawString(x1 + 3 * mm, y - 22 * mm, "TernoisCom :")
    c.drawString(x2 + 3 * mm, y - 7 * mm, "Signature,")
    c.drawString(x2 + 3 * mm, y - 15 * mm, "Le Maire de la commune de :")
    c.setFont("Helvetica-Bold", 10)
    c.drawString(x2 + 3 * mm, y - 23 * mm, commune[:38])


def _request_intro(request: InterventionRequest) -> str:
    if request.type_demande == RequestType.public:
        return (
            "Demande l'intervention des services de TernoisCom en vue de la destruction d'un nid de frelons "
            "asiatiques se situant au sein d'un espace public dont la commune est propriétaire et gestionnaire."
        )
    if request.type_demande == RequestType.prive_accord:
        return (
            "Demande l'intervention des services de TernoisCom en vue de la destruction d'un nid de frelons "
            "asiatiques se situant au sein d'un espace privé avec accord du propriétaire. L'autorisation du "
            "propriétaire figure en page 2 de la présente demande."
        )
    return (
        "Atteste avoir constaté la présence d'un nid de frelons asiatiques. Compte tenu de l'absence "
        "d'autorisation du propriétaire et d'un danger grave et imminent pour la santé publique, demande "
        "l'intervention des services de TernoisCom en vue de sa destruction."
    )


def _draw_request_page_one(c: canvas.Canvas, request: InterventionRequest, tariffs=None) -> None:
    width, height = A4
    _draw_logo(c, height)
    y = height - 43 * mm
    y = _center_underlined(c, "DEMANDE D'INTERVENTION", y)
    y = _center_underlined(c, "DANS LE CADRE DE LA LUTTE CONTRE LES FRELONS ASIATIQUES", y, 12.2)
    for line in TYPE_LINES[request.type_demande]:
        y = _center_highlight(c, line, y, 11.6 if len(line) > 48 else 12.4)

    y -= 6 * mm
    y = _dotted_value(c, "Je soussigné(e),", request.demandeur_nom, y)
    y = _dotted_value(c, "Maire de la Commune de", request.commune, y)
    y = _paragraph(c, _request_intro(request), y, width=112, leading=6 * mm)
    y -= 4 * mm
    y = _dotted_value(c, "Adresse exacte d'intervention", request.adresse, y)

    y = _tariff_box(c, y + 5 * mm, tariffs=tariffs)
    disclaimer = (
        "Les services techniques prendront contact avec la commune dans les meilleurs délais à la réception "
        "de cette demande d'intervention. Durant le laps de temps entre la réception de la demande et "
        "l'intervention, TernoisCom n'engage aucune responsabilité si un accident venait a se produire."
    )
    y = _paragraph(c, disclaimer, y, italic=True, width=118, leading=5.4 * mm)

    c.setFont("Helvetica", 9.5)
    c.drawString(110 * mm, 57 * mm, f"À {request.commune[:35]}, le {_date(request.created_at)}")
    c.drawString(110 * mm, 45 * mm, "Signature du Maire :")
    c.setFont("Helvetica-Bold", 8.5)
    c.drawString(15 * mm, 22 * mm, "Document à transmettre à la COMMUNAUTÉ DE COMMUNES DU TERNOIS - Parc des Moulins")
    c.drawString(15 * mm, 16 * mm, "400, rue de Maisnil - 62130 HERLIN LE SEC - frelon@ternoiscom.fr - contact@ternoiscom.fr")
    c.setFont("Helvetica", 7.5)
    c.setFillColor(MID_GREY)
    c.drawRightString(195 * mm, 10 * mm, request.reference)


def _draw_billing_page(
    c: canvas.Canvas,
    request: InterventionRequest,
    quote: InterventionQuote | None,
    intervention: Intervention | None,
    tariffs=None,
) -> None:
    width, height = A4
    _draw_logo(c, height)
    c.setFont("Helvetica-Bold", 13)
    c.drawString(15 * mm, height - 45 * mm, "FACTURATION À LA COMMUNE DE :")
    c.line(15 * mm, height - 47 * mm, 105 * mm, height - 47 * mm)
    c.setFont("Helvetica-Bold", 12)
    c.drawString(108 * mm, height - 45 * mm, request.commune)

    y = height - 75 * mm
    y = _dotted_value(c, "DATE D'INTERVENTION :", _date(intervention.debut_intervention if intervention else request.intervention_date), y, end=150 * mm)
    y = _dotted_value(c, "HEURE D'INTERVENTION :", _time(intervention.debut_intervention if intervention else request.intervention_date), y, end=150 * mm)
    c.setFont("Helvetica", 12)
    c.drawString(15 * mm, y, "TYPE D'INTERVENTION :")
    c.line(15 * mm, y - 1.5 * mm, 72 * mm, y - 1.5 * mm)
    selected = quote.type_intervention if quote else (intervention.methode if intervention else None)
    row_y = y - 12 * mm
    for code, label, price in (tariffs or TARIFF_FALLBACKS)[:2]:
        c.rect(78 * mm, row_y - 1.2 * mm, 3.2 * mm, 3.2 * mm, fill=0, stroke=1)
        if selected == code:
            c.setFont("Helvetica-Bold", 10)
            c.drawString(78.6 * mm, row_y - 0.3 * mm, "X")
        c.setFont("Helvetica", 11)
        c.drawString(87 * mm, row_y, f"{label} : {_money(price)}")
        row_y -= 10 * mm

    total = quote.montant_total if quote else (intervention.cout_ht if intervention else None)
    y = row_y - 18 * mm
    c.setFont("Helvetica", 12)
    c.drawString(15 * mm, y, "COÛT TOTAL DE L'INTERVENTION :")
    c.line(15 * mm, y - 1.5 * mm, 93 * mm, y - 1.5 * mm)
    if total is not None:
        c.setFont("Helvetica-Bold", 13)
        c.drawString(102 * mm, y, _money(total))

    c.setFont("Helvetica", 11)
    c.drawString(15 * mm, 105 * mm, f"Fait à {request.commune}, le {_date(intervention.fin_intervention if intervention else None)}")
    _signature_boxes(c, 85 * mm, request.commune)
    c.setFont("Helvetica", 7.5)
    c.setFillColor(MID_GREY)
    c.drawRightString(195 * mm, 10 * mm, request.reference)


def _draw_owner_authorisation_page(
    c: canvas.Canvas,
    request: InterventionRequest,
    quote: InterventionQuote | None,
    intervention: Intervention | None,
) -> None:
    width, height = A4
    _draw_logo(c, height)
    y = height - 43 * mm
    y = _center_underlined(c, "DEMANDE D'INTERVENTION", y)
    y = _center_underlined(c, "DANS LE CADRE DE LA LUTTE CONTRE LES FRELONS ASIATIQUES", y, 12.2)
    y = _center_highlight(c, "AU SEIN D'UN ESPACE PRIVÉ", y)
    y = _center_highlight(c, "AUTORISATION DU PROPRIÉTAIRE", y)
    y -= 7 * mm
    y = _dotted_value(c, "Je soussigné(e),", request.proprietaire_nom, y)
    y = _dotted_value(c, "Demeurant à (Adresse exacte) :", request.proprietaire_adresse, y)
    y = _dotted_value(c, "N° Téléphone :", request.proprietaire_telephone, y, end=100 * mm)
    c.setFont("Helvetica", 10)
    c.drawString(105 * mm, y + 15 * mm, "Adresse mail :")
    c.setFont("Helvetica-Bold", 9.5)
    c.drawString(130 * mm, y + 15 * mm, (request.proprietaire_email or "")[:42])
    y = _paragraph(c, "Autorise les services de TernoisCom à pénétrer au sein de ma propriété privée :", y + 2 * mm, width=115)
    y = _dotted_value(c, "Adresse exacte du lieu d'intervention", request.adresse, y - 4 * mm)
    y = _paragraph(c, "afin de procéder à la destruction d'un nid de frelons asiatiques", y, width=115)
    y -= 5 * mm
    y = _dotted_value(c, "Date de début d'intervention :", _date(intervention.debut_intervention if intervention else None), y, end=108 * mm)
    c.setFont("Helvetica", 10)
    c.drawString(112 * mm, y + 15 * mm, "Heure :")
    c.setFont("Helvetica-Bold", 10)
    c.drawString(128 * mm, y + 15 * mm, _time(intervention.debut_intervention if intervention else None))
    y = _dotted_value(c, "Date de fin d'intervention :", _date(intervention.fin_intervention if intervention else None), y, end=108 * mm)
    c.setFont("Helvetica", 10)
    c.drawString(112 * mm, y + 15 * mm, "Heure :")
    c.setFont("Helvetica-Bold", 10)
    c.drawString(128 * mm, y + 15 * mm, _time(intervention.fin_intervention if intervention else None))
    total = quote.montant_total if quote else (intervention.cout_ht if intervention else None)
    y = _dotted_value(c, "PRESTATION EFFECTUÉE - COÛT DE LA PRESTATION :", _money(total), y, end=195 * mm)

    c.rect(15 * mm, 25 * mm, 90 * mm, 43 * mm, fill=0, stroke=1)
    c.rect(105 * mm, 25 * mm, 90 * mm, 43 * mm, fill=0, stroke=1)
    c.setFont("Helvetica-Bold", 9.5)
    c.drawString(17 * mm, 62 * mm, "Signature du Propriétaire AVANT intervention :")
    c.drawString(107 * mm, 62 * mm, "Signature du Propriétaire APRÈS intervention :")
    c.setFont("Helvetica", 9)
    c.drawString(17 * mm, 52 * mm, f"Fait à {request.commune}, le {_date(request.created_at)}")
    c.drawString(107 * mm, 52 * mm, f"Fait à {request.commune}, le {_date(intervention.fin_intervention if intervention else None)}")
    c.drawString(17 * mm, 43 * mm, "Signature :")
    c.drawString(107 * mm, 43 * mm, "Signature :")


def build_request_pdf(
    request: InterventionRequest,
    longitude: float,
    latitude: float,
    quote: InterventionQuote | None = None,
    intervention: Intervention | None = None,
    tariffs=None,
) -> bytes:
    del longitude, latitude  # Le formulaire institutionnel utilise l'adresse officielle.
    buffer = BytesIO()
    c = canvas.Canvas(buffer, pagesize=A4)
    c.setTitle(f"Demande d'intervention {request.reference}")
    _draw_request_page_one(c, request, tariffs=tariffs)
    c.showPage()
    if request.type_demande == RequestType.prive_accord:
        _draw_owner_authorisation_page(c, request, quote, intervention)
    else:
        _draw_billing_page(c, request, quote, intervention, tariffs=tariffs)
    c.save()
    buffer.seek(0)
    return buffer.read()


def build_quote_pdf(request: InterventionRequest, quote: InterventionQuote) -> bytes:
    buffer = BytesIO()
    c = canvas.Canvas(buffer, pagesize=A4)
    width, height = A4
    c.setTitle(f"Devis {quote.reference}")
    _draw_logo(c, height)

    y = height - 43 * mm
    y = _center_underlined(c, "DEVIS D'INTERVENTION", y)
    y = _center_underlined(c, "DANS LE CADRE DE LA LUTTE CONTRE LES FRELONS ASIATIQUES", y, 12.2)
    y = _center_highlight(c, "DESTRUCTION D'UN NID DE FRELONS ASIATIQUES", y, 11.8)

    c.setFont("Helvetica", 9.5)
    c.drawRightString(195 * mm, y - 2 * mm, f"Référence : {quote.reference}")
    c.drawRightString(195 * mm, y - 8 * mm, f"Date : {_date(quote.date_devis)}")
    y -= 18 * mm
    y = _dotted_value(c, "Commune facturée :", quote.commune_facturee, y)
    y = _dotted_value(c, "Demande d'intervention :", request.reference, y)
    y = _dotted_value(c, "Adresse d'intervention :", request.adresse, y)

    table_x = 15 * mm
    table_y = y - 2 * mm
    widths = [92 * mm, 20 * mm, 30 * mm, 34 * mm]
    headers = ["PRESTATION", "QTÉ", "PRIX UNITAIRE", "MONTANT"]
    c.setFillColor(LIGHT_GREY)
    c.rect(table_x, table_y - 10 * mm, sum(widths), 10 * mm, fill=1, stroke=1)
    x = table_x
    c.setFillColor(black)
    c.setFont("Helvetica-Bold", 8.5)
    for header, col_w in zip(headers, widths):
        c.drawCentredString(x + col_w / 2, table_y - 6.5 * mm, header)
        c.line(x, table_y, x, table_y - 30 * mm)
        x += col_w
    c.line(x, table_y, x, table_y - 30 * mm)
    c.line(table_x, table_y - 30 * mm, table_x + sum(widths), table_y - 30 * mm)
    c.setFont("Helvetica", 9.5)
    c.drawString(table_x + 3 * mm, table_y - 20 * mm, quote.libelle_snapshot)
    c.drawCentredString(table_x + widths[0] + widths[1] / 2, table_y - 20 * mm, str(quote.quantite))
    c.drawRightString(table_x + sum(widths[:3]) - 3 * mm, table_y - 20 * mm, _money(quote.prix_unitaire))
    c.setFont("Helvetica-Bold", 9.5)
    c.drawRightString(table_x + sum(widths) - 3 * mm, table_y - 20 * mm, _money(quote.montant_total))

    total_y = table_y - 42 * mm
    c.setFont("Helvetica-Bold", 12)
    c.drawRightString(155 * mm, total_y, "MONTANT TOTAL DU DEVIS :")
    c.drawRightString(195 * mm, total_y, _money(quote.montant_total))
    c.line(108 * mm, total_y - 1.5 * mm, 195 * mm, total_y - 1.5 * mm)

    c.setFont("Helvetica", 9.5)
    validity = _date(quote.validite_jusqu_au) or "Non limitée"
    c.drawString(15 * mm, total_y - 18 * mm, f"Validité du devis : jusqu'au {validity}")
    c.drawString(15 * mm, total_y - 26 * mm, "Tarif forfaitaire de la prestation FRELONS de TernoisCom, facturé à la commune.")
    if quote.observations:
        c.setFont("Helvetica-Bold", 9.5)
        c.drawString(15 * mm, total_y - 39 * mm, "Observations :")
        _paragraph(c, quote.observations, total_y - 46 * mm, width=118, leading=5.2 * mm)

    c.setFont("Helvetica", 10)
    c.drawString(15 * mm, 67 * mm, f"Fait à Herlin-le-Sec, le {_date(quote.date_devis)}")
    _signature_boxes(c, 57 * mm, request.commune)
    c.setFont("Helvetica", 7.5)
    c.setFillColor(MID_GREY)
    c.drawString(15 * mm, 11 * mm, "Devis généré depuis le portail frelons asiatiques de TernoisCom.")
    c.drawRightString(195 * mm, 11 * mm, quote.reference)

    c.save()
    buffer.seek(0)
    return buffer.read()
