"""
Generate, save (named PDF), and optionally deliver invoices, quotations, receipts, and statements.
"""
from __future__ import annotations

from dataclasses import dataclass
from datetime import date, datetime
from io import BytesIO
from typing import Any, Dict, Optional

from sqlalchemy.orm import Session

from app.core.config import settings as app_settings
from app.services.document_pdf_storage import public_absolute_url, save_document_pdf
from app.services.whatsapp_service import WhatsAppService


@dataclass
class DocumentDeliveryOptions:
    save_pdf: bool = True
    send_whatsapp: bool = False
    whatsapp_phone: Optional[str] = None
    include_pdf_in_whatsapp: bool = True
    template_id: Optional[str] = None


@dataclass
class DocumentDeliveryResult:
    saved: bool = False
    pdf_path: Optional[str] = None
    pdf_filename: Optional[str] = None
    pdf_url: Optional[str] = None
    whatsapp_sent: bool = False
    whatsapp_error: Optional[str] = None
    whatsapp_response: Optional[Dict[str, Any]] = None

    def to_dict(self) -> Dict[str, Any]:
        return {
            "saved": self.saved,
            "pdf_path": self.pdf_path,
            "pdf_filename": self.pdf_filename,
            "pdf_url": self.pdf_url,
            "whatsapp_sent": self.whatsapp_sent,
            "whatsapp_error": self.whatsapp_error,
            "whatsapp_response": self.whatsapp_response,
        }


class DocumentDeliveryService:
    def __init__(self, db: Session):
        self.db = db
        self.whatsapp = WhatsAppService(db)

    def deliver_invoice(
        self,
        invoice,
        options: DocumentDeliveryOptions,
    ) -> DocumentDeliveryResult:
        from app.services.invoice_service import InvoiceService

        inv_svc = InvoiceService(self.db)
        pdf_buffer = inv_svc.generate_pdf_invoice(invoice.id, template_id=options.template_id)
        pdf_bytes = pdf_buffer.read()
        customer_name = invoice.customer.name if invoice.customer else "Walk-in_Customer"
        created = invoice.invoice_date or invoice.created_at or date.today()
        doc_number = invoice.invoice_number or invoice.id[:8]

        result = self._save_and_maybe_whatsapp(
            document_type="invoice",
            pdf_bytes=pdf_bytes,
            customer_name=customer_name,
            created_date=created,
            document_number=doc_number,
            phone=options.whatsapp_phone or (invoice.customer.phone if invoice.customer else None),
            options=options,
            message_builder=lambda: self._invoice_message(invoice),
            mark_sent_callback=lambda: inv_svc.mark_invoice_sent(invoice.id, "whatsapp"),
        )
        return result

    def deliver_quotation(self, quotation, options: DocumentDeliveryOptions) -> DocumentDeliveryResult:
        from app.services.document_printing_service import DocumentPrintingService

        enriched = self._quotation_content_data(quotation)
        doc_svc = DocumentPrintingService(self.db)
        _, pdf_bytes, _ = doc_svc.generate_document_pdf(
            document_type="quote",
            document_id=enriched.get("quote_number") or quotation.id,
            title=f"Quotation {enriched.get('quote_number', '')}",
            content_data=enriched,
        )
        customer_name = enriched.get("customer_name") or "Walk-in_Customer"
        created = quotation.date or quotation.created_at or date.today()
        doc_number = quotation.quote_number or quotation.id[:8]

        return self._save_and_maybe_whatsapp(
            document_type="quotation",
            pdf_bytes=pdf_bytes,
            customer_name=customer_name,
            created_date=created,
            document_number=doc_number,
            phone=options.whatsapp_phone or (
                quotation.customer.phone if quotation.customer else None
            ),
            options=options,
            message_builder=lambda: self._quotation_message(quotation, enriched),
        )

    def deliver_receipt_pdf(
        self,
        pdf_bytes: bytes,
        customer_name: str,
        created_date,
        receipt_number: str,
        phone: Optional[str],
        options: DocumentDeliveryOptions,
    ) -> DocumentDeliveryResult:
        return self._save_and_maybe_whatsapp(
            document_type="receipt",
            pdf_bytes=pdf_bytes,
            customer_name=customer_name,
            created_date=created_date,
            document_number=receipt_number,
            phone=phone or options.whatsapp_phone,
            options=options,
            message_builder=lambda: (
                f"Thank you for your payment. Receipt {receipt_number} "
                f"from {customer_name} is attached."
            ),
        )

    def deliver_customer_statement(
        self,
        statement_data: Dict[str, Any],
        options: DocumentDeliveryOptions,
    ) -> DocumentDeliveryResult:
        from app.services.document_printing_service import DocumentPrintingService

        customer_name = statement_data.get("customer_name") or "Customer"
        customer_id = statement_data.get("customer_id") or "unknown"
        created = statement_data.get("generated_at") or date.today()
        period_end = statement_data.get("period_end") or created
        period_str = str(period_end)[:10]
        short_cust = str(customer_id).replace("-", "")[:8].upper()
        doc_number = statement_data.get("statement_number") or (
            f"STMT-{period_str}-{short_cust}"
        )
        statement_data = {**statement_data, "statement_number": doc_number}

        doc_svc = DocumentPrintingService(self.db)
        _, pdf_bytes, _ = doc_svc.generate_document_pdf(
            document_type="customer_statement",
            document_id=customer_id,
            title=f"Statement — {customer_name}",
            content_data=statement_data,
        )

        return self._save_and_maybe_whatsapp(
            document_type="statement",
            pdf_bytes=pdf_bytes,
            customer_name=customer_name,
            created_date=created,
            document_number=doc_number,
            phone=options.whatsapp_phone or statement_data.get("customer_phone"),
            options=options,
            message_builder=lambda: (
                f"Account statement for {customer_name} "
                f"(as at {str(period_end)[:10]}). PDF attached."
            ),
        )

    def _save_and_maybe_whatsapp(
        self,
        document_type: str,
        pdf_bytes: bytes,
        customer_name: str,
        created_date,
        document_number: str,
        phone: Optional[str],
        options: DocumentDeliveryOptions,
        message_builder,
        mark_sent_callback=None,
    ) -> DocumentDeliveryResult:
        out = DocumentDeliveryResult()

        if options.save_pdf and pdf_bytes:
            _, public_path, filename = save_document_pdf(
                document_type=document_type,
                pdf_bytes=pdf_bytes,
                customer_name=customer_name,
                created_date=created_date,
                document_number=document_number,
            )
            out.saved = True
            out.pdf_path = public_path
            out.pdf_filename = filename
            out.pdf_url = public_absolute_url(public_path)

        if options.send_whatsapp:
            if not phone:
                out.whatsapp_error = "No customer phone number for WhatsApp delivery"
            elif not self.whatsapp.is_enabled():
                out.whatsapp_error = "WhatsApp is not enabled or configured"
            else:
                message = message_builder()
                pdf_url = out.pdf_url if (options.include_pdf_in_whatsapp and out.pdf_url) else None
                wa = self.whatsapp.send_business_document(
                    phone_number=phone,
                    message=message,
                    document_public_url=pdf_url,
                    document_filename=out.pdf_filename,
                )
                out.whatsapp_response = wa
                if wa.get("success"):
                    out.whatsapp_sent = True
                    if mark_sent_callback:
                        try:
                            mark_sent_callback()
                        except Exception:
                            pass
                else:
                    out.whatsapp_error = wa.get("error") or "WhatsApp send failed"

        return out

    def _invoice_message(self, invoice) -> str:
        currency = getattr(invoice, "currency", None) or app_settings.default_currency
        sym = "P" if currency == "BWP" else f"{currency} "
        customer = invoice.customer.name if invoice.customer else "Customer"
        return (
            f"Invoice {invoice.invoice_number} for {customer}\n"
            f"Date: {invoice.invoice_date}\n"
            f"Total: {sym}{float(invoice.total_amount):,.2f}\n"
            f"Due: {invoice.due_date}"
        )

    def _quotation_message(self, quotation, enriched: Dict) -> str:
        return (
            f"Quotation {quotation.quote_number} for {enriched.get('customer_name', 'you')}\n"
            f"Date: {quotation.date}\n"
            f"Total: P{float(quotation.total):,.2f}\n"
            f"Valid until: {quotation.valid_until or 'N/A'}"
        )

    @staticmethod
    def _quotation_content_data(quotation) -> Dict[str, Any]:
        items_list = []
        for item in quotation.items:
            desc = item.description or (item.product.name if item.product else "")
            items_list.append({
                "product_id": item.product_id,
                "product_name": item.product.name if item.product else None,
                "description": desc,
                "quantity": float(item.quantity),
                "price": float(item.price),
                "discount": float(item.discount),
                "line_total": float(item.line_total) if item.line_total else 0.0,
            })
        return {
            "id": quotation.id,
            "document_number": quotation.quote_number,
            "quote_number": quotation.quote_number,
            "customer_id": quotation.customer_id,
            "customer_name": quotation.customer.name if quotation.customer else None,
            "date": quotation.date.isoformat() if quotation.date else None,
            "valid_until": quotation.valid_until.isoformat() if quotation.valid_until else None,
            "reference": quotation.reference,
            "notes": quotation.notes,
            "status": quotation.status,
            "subtotal": float(quotation.subtotal),
            "vat": float(quotation.vat),
            "total": float(quotation.total),
            "items": items_list,
        }
