from io import BytesIO
import os
import tempfile

from django.db.models import Q
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.http import HttpResponse
from django.template.loader import render_to_string

from decimal import Decimal

from .models import NormalizedInvoice, TauxDeChange
from .forms import ExtractInvoiceUploadForm
from .services import ingest_invoice_file
from .pdf_utils import make_qr_overlay_pdf


def _to_usd(amount_cdf, rate):
    """Convertit un montant CDF en USD (amount_cdf / rate). Retourne None si rate invalide."""
    if amount_cdf is None:
        return None
    if not rate or rate <= 0:
        return None
    try:
        return (Decimal(str(amount_cdf)) / Decimal(str(rate))).quantize(Decimal("0.01"))
    except Exception:
        return None


def _invoice_display_currency(invoice, devise):
    """
    Construit le contexte d'affichage des montants selon la devise (cdf ou usd).
    Si devise == 'usd' et que invoice.exchange_rate est renseigné, convertit tous les montants en USD.
    Sinon affichage en CDF.
    Retourne: dict avec lines_display, total_ttc_display, ht_taxable_16_display, tva_16_display,
    currency_label, phrase_currency, devise (réel utilisé).
    """
    rate = None
    if getattr(invoice, "exchange_rate", None) and float(invoice.exchange_rate) > 0:
        rate = float(invoice.exchange_rate)
    if devise == "usd" and (not rate or rate <= 0):
        taux_obj = TauxDeChange.objects.filter(actif=True).first()
        if not taux_obj:
            taux_obj = TauxDeChange.objects.order_by("-date_taux", "-id").first()
        if taux_obj and taux_obj.taux and taux_obj.taux > 0:
            rate = float(taux_obj.taux)
    use_usd = devise == "usd" and rate and rate > 0

    if not use_usd:
        lines_display = [
            {
                "designation": line.designation,
                "quantity": line.quantity,
                "unit_price": line.unit_price_ttc,
                "amount": line.amount_ttc,
            }
            for line in invoice.lines.all()
        ]
        return {
            "lines_display": lines_display,
            "total_ttc_display": invoice.total_ttc_cdf,
            "ht_taxable_16_display": invoice.ht_taxable_16,
            "tva_16_display": invoice.tva_16,
            "currency_label": "CDF",
            "phrase_currency": "francs congolais",
            "devise": "cdf",
        }

    lines_display = []
    for line in invoice.lines.all():
        lines_display.append({
            "designation": line.designation,
            "quantity": line.quantity,
            "unit_price": _to_usd(line.unit_price_ttc, rate),
            "amount": _to_usd(line.amount_ttc, rate),
        })
    return {
        "lines_display": lines_display,
        "total_ttc_display": _to_usd(invoice.total_ttc_cdf, rate),
        "ht_taxable_16_display": _to_usd(invoice.ht_taxable_16, rate),
        "tva_16_display": _to_usd(invoice.tva_16, rate),
        "currency_label": "USD",
        "phrase_currency": "dollars américains",
        "devise": "usd",
    }


def _write_qr_temp_file(invoice):
    """Génère l'image QR et la sauvegarde dans un fichier temporaire. Retourne (uri pour le template, path absolu pour link_callback) ou (None, None)."""
    if not getattr(invoice, "qr_raw", None):
        return None, None
    try:
        import qrcode
        qr = qrcode.QRCode(version=1, box_size=4, border=2)
        qr.add_data(invoice.qr_raw)
        qr.make(fit=True)
        img = qr.make_image(fill_color="black", back_color="white")
        with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as tmp:
            img.save(tmp, format="PNG")
            path = tmp.name
        # Normaliser le chemin (slashes) pour que xhtml2pdf/LocalFileURI le lise correctement
        abs_path = os.path.abspath(path).replace("\\", "/")
        return f"qr:{invoice.pk}", abs_path
    except Exception:
        return None, None


def extract_upload(request):
    """Formulaire : société, client, upload PDF/image → extraction → redirection vers impression."""
    form = ExtractInvoiceUploadForm(request.POST or None, request.FILES or None)
    if form.is_valid():
        f = form.cleaned_data["file"]
        if not f.name.lower().endswith((".pdf", ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff")):
            messages.error(request, "Format accepté : PDF ou image (PNG, JPG, etc.).")
            return render(request, "extratfile/upload.html", {"form": form})

        try:
            with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(f.name)[1]) as tmp:
                for chunk in f.chunks():
                    tmp.write(chunk)
                tmp_path = tmp.name

            try:
                client_type_obj = form.cleaned_data.get("client_type")
                invoice = ingest_invoice_file(
                    tmp_path,
                    societe_id=form.cleaned_data["societe"].pk,
                    client_type=str(client_type_obj) if client_type_obj else "",
                    payment_mode=(form.cleaned_data.get("payment_mode") or "").strip(),
                    client_nom=form.cleaned_data.get("client_nom") or "",
                    client_nif=form.cleaned_data.get("client_nif") or "",
                    client_telephone=form.cleaned_data.get("client_telephone") or "",
                    client_adresse=form.cleaned_data.get("client_adresse") or "",
                    source_file_field=f,
                )
                messages.success(request, "Facture extraite. Vous pouvez imprimer.")
                return redirect("extratfile:print", pk=invoice.pk)
            finally:
                try:
                    os.unlink(tmp_path)
                except Exception:
                    pass

        except Exception as e:
            err_detail = str(e)
            if "no such table" in err_detail.lower() or "extratfile" in err_detail.lower():
                err_detail = f"{err_detail} — Exécutez : python manage.py migrate"
            messages.error(request, f"Erreur lors de l'extraction : {err_detail}")

    return render(request, "extratfile/upload.html", {"form": form})


def extract_print(request, pk):
    """Aperçu facture extraite + bouton Imprimer PDF. ?devise=cdf|usd pour afficher en CDF ou USD."""
    invoice = get_object_or_404(NormalizedInvoice.objects.prefetch_related("lines"), pk=pk)
    devise = (request.GET.get("devise") or "cdf").strip().lower()
    if devise not in ("cdf", "usd"):
        devise = "cdf"
    display = _invoice_display_currency(invoice, devise)
    return render(request, "extratfile/print.html", {
        "invoice": invoice,
        "devise": display["devise"],
        "display": display,
    })


def extract_pdf(request, pk):
    """Génère le PDF à partir du HTML (xhtml2pdf). ?devise=usd pour imprimer en USD (taux dans l'admin)."""
    invoice = get_object_or_404(NormalizedInvoice.objects.prefetch_related("lines"), pk=pk)
    filename = f"facture-{invoice.invoice_number or invoice.id}.pdf"
    devise = (request.GET.get("devise") or "cdf").strip().lower()
    if devise not in ("cdf", "usd"):
        devise = "cdf"
    display = _invoice_display_currency(invoice, devise)

    qr_src, qr_temp_path = _write_qr_temp_file(invoice)
    logo_path = None
    if getattr(invoice, "societe", None) and getattr(invoice.societe, "logo", None) and invoice.societe.logo:
        try:
            logo_path = os.path.abspath(invoice.societe.logo.path).replace("\\", "/")
        except (ValueError, OSError):
            logo_path = None

    def link_callback(uri, _basepath):
        if uri == qr_src and qr_temp_path:
            return qr_temp_path
        if uri == "logo:header" and logo_path:
            return logo_path
        return None

    context = {
        "invoice": invoice,
        "qr_src": qr_src,
        "has_logo": bool(logo_path),
        "display": display,
    }
    html_string = render_to_string(
        "extratfile/invoice_print.html",
        context,
        request=request,
    )
    result = BytesIO()
    try:
        from xhtml2pdf import pisa
        pisa_status = pisa.CreatePDF(
            html_string,
            dest=result,
            encoding="utf-8",
            link_callback=link_callback,
        )
        if pisa_status.err:
            err_log = getattr(pisa_status, "log", None) or []
            err_msg = " ; ".join(str(e) for e in err_log[:5]) if err_log else "Erreur inconnue (vérifier le HTML/CSS)."
            messages.error(request, f"Erreur PDF : {err_msg}")
            return redirect("extratfile:print", pk=pk)
    except Exception as e:
        messages.error(request, f"Impossible de générer le PDF : {e}")
        return redirect("extratfile:print", pk=pk)
    finally:
        if qr_temp_path and os.path.isfile(qr_temp_path):
            try:
                os.unlink(qr_temp_path)
            except OSError:
                pass
    result.seek(0)
    pdf_bytes = result.getvalue()
    if not pdf_bytes:
        messages.error(request, "Le PDF généré est vide.")
        return redirect("extratfile:print", pk=pk)

    # Superposer le QR avec ReportLab (xhtml2pdf n'affiche pas toujours l'image dans le PDF)
    if getattr(invoice, "qr_raw", None):
        try:
            from pypdf import PdfReader, PdfWriter
            base_reader = PdfReader(BytesIO(pdf_bytes))
            overlay_buf = make_qr_overlay_pdf(invoice.qr_raw, y_mm=100)
            overlay_reader = PdfReader(overlay_buf)
            writer = PdfWriter()
            for i, page in enumerate(base_reader.pages):
                if i == 0 and overlay_reader.pages:
                    page.merge_page(overlay_reader.pages[0])
                writer.add_page(page)
            out = BytesIO()
            writer.write(out)
            out.seek(0)
            pdf_bytes = out.getvalue()
        except Exception:
            pass  # garder le PDF sans QR en cas d'erreur

    response = HttpResponse(pdf_bytes, content_type="application/pdf")
    response["Content-Disposition"] = f'inline; filename="{filename}"'
    return response


def extract_list(request):
    """Liste des factures extraites avec actions Réimprimer et Supprimer."""
    q = (request.GET.get("q") or "").strip()
    qs = NormalizedInvoice.objects.select_related("societe").prefetch_related("lines").order_by("-created_at")
    if q:
        qs = qs.filter(
            Q(invoice_number__icontains=q)
            | Q(client_nom__icontains=q)
            | Q(source_filename__icontains=q)
        )
    return render(request, "extratfile/invoice_list.html", {"invoice_list": qs, "q": q})


def extract_delete(request, pk):
    """Suppression d'une facture extraite (confirmation requise)."""
    invoice = get_object_or_404(NormalizedInvoice, pk=pk)
    if request.method == "POST":
        invoice.delete()
        messages.success(request, "Facture extraite supprimée.")
        return redirect("extratfile:list")
    return render(request, "extratfile/invoice_confirm_delete.html", {"invoice": invoice})


SYGDEF_LOGIN_URL = "https://sygdef.dgirdc.cd/Account/Login"
SYGDEF_DEFAULT_USER = "A2424493A"
SYGDEF_DEFAULT_PASSWORD = "12345"


def sygdef_login(request):
    """Page SYGDEF : bouton pour ouvrir la connexion dans une nouvelle fenêtre."""
    return render(
        request,
        "extratfile/sygdef_login.html",
        {
            "sygdef_url": SYGDEF_LOGIN_URL + "?ReturnUrl=%2F",
            "sygdef_user": SYGDEF_DEFAULT_USER,
            "sygdef_password": SYGDEF_DEFAULT_PASSWORD,
        },
    )


def extract_qr_image(request, pk):
    """Retourne l'image PNG du QR régénéré à partir de qr_raw (pour l'aperçu)."""
    invoice = get_object_or_404(NormalizedInvoice, pk=pk)
    if not invoice.qr_raw:
        return HttpResponse(status=404)
    try:
        import qrcode
        qr = qrcode.QRCode(version=1, box_size=4, border=2)
        qr.add_data(invoice.qr_raw)
        qr.make(fit=True)
        img = qr.make_image(fill_color="black", back_color="white")
        buffer = BytesIO()
        img.save(buffer, format="PNG")
        buffer.seek(0)
        return HttpResponse(buffer.getvalue(), content_type="image/png")
    except Exception:
        return HttpResponse(status=500)
