"""Proposal PDF generator using ReportLab.

Generates a formal cemetery proposal PDF matching the letterhead layout
from the INDL-27 PRD. Uploads to S3 under proposals/{tenant_id}/{quote_number}.pdf.
Falls back gracefully when S3 credentials are absent.
"""
from __future__ import annotations

import asyncio
import io
import logging
from decimal import Decimal
from typing import TYPE_CHECKING, Optional

if TYPE_CHECKING:
    from sqlalchemy.ext.asyncio import AsyncSession
    from src.apps.sales.models.proposal import Proposal

logger = logging.getLogger(__name__)


def _build_proposal_pdf_bytes(
    proposal: "Proposal",
    tenant_name: str,
    sales_owner_name: str,
    plot_ref: Optional[str] = None,
    section_name: Optional[str] = None,
) -> bytes:
    """Build proposal PDF bytes using ReportLab."""
    from reportlab.lib import colors
    from reportlab.lib.pagesizes import A4
    from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
    from reportlab.lib.units import cm
    from reportlab.platypus import (
        HRFlowable,
        Paragraph,
        SimpleDocTemplate,
        Spacer,
        Table,
        TableStyle,
    )

    buf = io.BytesIO()
    doc = SimpleDocTemplate(
        buf,
        pagesize=A4,
        rightMargin=2 * cm,
        leftMargin=2 * cm,
        topMargin=2 * cm,
        bottomMargin=2 * cm,
    )

    PRIMARY = colors.HexColor("#1a4a6e")
    styles = getSampleStyleSheet()
    body = ParagraphStyle("Body", parent=styles["Normal"], fontSize=10, leading=14)
    small = ParagraphStyle("Small", parent=styles["Normal"], fontSize=8, leading=12, textColor=colors.HexColor("#6B7280"))
    heading = ParagraphStyle("Heading", parent=styles["Normal"], fontSize=11, fontName="Helvetica-Bold", textColor=PRIMARY)
    title_style = ParagraphStyle("Title", parent=styles["Normal"], fontSize=18, fontName="Helvetica-Bold", textColor=PRIMARY, spaceAfter=4)

    from datetime import timezone
    sent = proposal.sent_at
    if sent and sent.tzinfo is None:
        sent = sent.replace(tzinfo=timezone.utc)
    from datetime import timedelta
    valid_until = None
    if sent:
        valid_until = sent + timedelta(days=proposal.valid_days)
        issued_str = sent.strftime("%B %d, %Y")
        valid_str = valid_until.strftime("%B %d, %Y")
    else:
        from datetime import datetime
        now = datetime.now(timezone.utc)
        issued_str = now.strftime("%B %d, %Y")
        valid_str = (now + timedelta(days=proposal.valid_days)).strftime("%B %d, %Y")

    story = []

    # Letterhead
    story.append(Paragraph(tenant_name, title_style))
    story.append(Paragraph(f"Quote {proposal.quote_number}  ·  Issued {issued_str}  ·  Valid until {valid_str}", small))
    story.append(HRFlowable(width="100%", thickness=2, color=PRIMARY, spaceAfter=12))

    story.append(Paragraph(proposal.subject or "Sales Proposal", heading))
    story.append(Paragraph(f"Reference {proposal.quote_number} · {sales_owner_name}", small))
    story.append(Spacer(1, 12))

    # Prepared for
    story.append(Paragraph(f"<b>Prepared for:</b> {proposal.to_email}", body))
    if plot_ref:
        price_str = (
            f"${float(proposal.plot_price):,.2f}" if proposal.plot_price is not None else "—"
        )
        section_str = f" · {section_name}" if section_name else ""
        story.append(Paragraph(f"<b>Plot:</b> {plot_ref}{section_str} · {price_str}", body))
    story.append(Spacer(1, 8))

    # Cover note
    if proposal.cover_note:
        note_lines = proposal.cover_note.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
        for line in note_lines.split("\n"):
            story.append(Paragraph(line or " ", body))
    story.append(Spacer(1, 14))

    # Quotation table
    story.append(HRFlowable(width="100%", thickness=0.5, color=colors.HexColor("#E5E7EB"), spaceAfter=6))
    story.append(Paragraph("QUOTATION", heading))
    story.append(Spacer(1, 4))

    tdata = [["Item", "Qty", "Unit (CAD)", "Amount"]]
    for li in sorted(proposal.line_items, key=lambda x: x.sort_order):
        tdata.append([
            li.description,
            str(int(li.quantity)),
            f"${float(li.unit_price):,.2f}",
            f"${float(li.line_total):,.2f}",
        ])

    subtotal = float(proposal.subtotal)
    tax = float(proposal.tax_amount)
    total = float(proposal.total_amount)

    tdata.append(["", "", "Subtotal", f"${subtotal:,.2f}"])
    tdata.append(["", "", "HST (13%)", f"${tax:,.2f}"])
    tdata.append(["", "", "Total CAD", f"${total:,.2f}"])

    col_widths = [9 * cm, 2 * cm, 3.5 * cm, 3.5 * cm]
    table = Table(tdata, colWidths=col_widths)
    table.setStyle(TableStyle([
        ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
        ("FONTSIZE", (0, 0), (-1, -1), 9),
        ("TEXTCOLOR", (0, 0), (-1, 0), PRIMARY),
        ("ALIGN", (1, 0), (-1, -1), "RIGHT"),
        ("LINEBELOW", (0, 0), (-1, 0), 0.5, colors.HexColor("#E5E7EB")),
        ("FONTNAME", (-2, -3), (-1, -1), "Helvetica-Bold"),
        ("FONTNAME", (-2, -1), (-1, -1), "Helvetica-Bold"),
        ("TEXTCOLOR", (-2, -1), (-1, -1), PRIMARY),
        ("LINEABOVE", (-2, -3), (-1, -3), 0.5, colors.HexColor("#E5E7EB")),
        ("LINEABOVE", (-2, -1), (-1, -1), 1.5, PRIMARY),
        ("ROWBACKGROUNDS", (0, 1), (-1, -4), [colors.white, colors.HexColor("#F9FAFB")]),
        ("TOPPADDING", (0, 0), (-1, -1), 6),
        ("BOTTOMPADDING", (0, 0), (-1, -1), 6),
    ]))
    story.append(table)
    story.append(Spacer(1, 14))

    # Terms
    story.append(HRFlowable(width="100%", thickness=0.5, color=colors.HexColor("#E5E7EB"), spaceAfter=6))
    story.append(Paragraph("TERMS", heading))
    terms = [
        "This quotation is non-binding and does not reserve the plot.",
        f"Valid for {proposal.valid_days} days from the issue date above.",
        "A 20% deposit secures the plot and converts this proposal into a binding contract.",
        "All sales subject to applicable cemetery by-laws and the Funeral, Burial and Cremation Services Act.",
    ]
    for t in terms:
        story.append(Paragraph(f"• {t}", body))
    story.append(Spacer(1, 20))

    # Signature blocks
    sig_data = [
        ["Purchaser acceptance", "For " + tenant_name],
        ["", ""],
        ["__________________________", "__________________________"],
        ["Signature · Date", f"{sales_owner_name} · Date"],
    ]
    sig_table = Table(sig_data, colWidths=[9 * cm, 9 * cm])
    sig_table.setStyle(TableStyle([
        ("FONTSIZE", (0, 0), (-1, -1), 8),
        ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
        ("TEXTCOLOR", (0, 0), (-1, 0), colors.HexColor("#6B7280")),
        ("TEXTCOLOR", (0, 3), (-1, 3), colors.HexColor("#6B7280")),
        ("TOPPADDING", (0, 0), (-1, -1), 4),
        ("BOTTOMPADDING", (0, 0), (-1, -1), 4),
    ]))
    story.append(sig_table)
    story.append(Spacer(1, 14))

    story.append(HRFlowable(width="100%", thickness=0.5, color=colors.HexColor("#E5E7EB"), spaceAfter=4))
    story.append(Paragraph("Generated by INDELIS Cemetery Management Platform  ·  Page 1 of 1", small))

    doc.build(story)
    return buf.getvalue()


async def generate_and_store_proposal_pdf(
    proposal: "Proposal",
    db: "AsyncSession",
    tenant_id: str,
) -> tuple[Optional[str], bytes]:
    """Generate proposal PDF, upload to S3, and return (s3_key, pdf_bytes).

    ``s3_key`` is None when S3 isn't configured — callers that only need the
    bytes (e.g. to attach the PDF to the proposal email) still get them back
    even though nothing was persisted to S3.
    """
    from src.core.config import settings

    # Resolve tenant name
    tenant_name = "Cemetery"
    try:
        from sqlalchemy import select as _select
        from src.apps.tenants.models.account import Account
        result = await db.execute(_select(Account).where(Account.id == tenant_id))
        account = result.scalar_one_or_none()
        if account:
            tenant_name = account.organization_name or tenant_name
    except Exception:
        pass

    # Resolve sales owner name
    sales_owner_name = "Cemetery Admin"
    if proposal.sales_owner_id:
        try:
            from sqlalchemy import select as _select
            from src.apps.auth.models.user import User
            result = await db.execute(_select(User).where(User.id == proposal.sales_owner_id))
            user = result.scalar_one_or_none()
            if user:
                sales_owner_name = f"{user.first_name} {user.last_name}".strip() or user.email
        except Exception:
            pass

    # Resolve plot_ref/section_name — proposal.plot_id is a raw FK UUID.
    plot_ref, section_name = None, None
    if proposal.plot_id:
        from src.worker.arq_app import resolve_plot_and_section_labels
        plot_ref, section_name = await resolve_plot_and_section_labels(
            db, tenant_id, proposal.plot_id, None,
        )

    # Build PDF bytes in a thread (CPU-bound)
    pdf_bytes = await asyncio.to_thread(
        _build_proposal_pdf_bytes, proposal, tenant_name, sales_owner_name,
        plot_ref or None, section_name or None,
    )

    # Upload to S3
    if not (settings.AWS_ACCESS_KEY_ID and settings.AWS_SECRET_ACCESS_KEY):
        logger.info("S3 not configured; proposal PDF not stored (quote_number=%s)", proposal.quote_number)
        return None, pdf_bytes

    s3_key = f"proposals/{tenant_id}/{proposal.quote_number}.pdf"

    def _upload() -> None:
        import boto3
        s3 = boto3.client(
            "s3",
            region_name=getattr(settings, "AWS_REGION", "ca-central-1"),
            aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
            aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
        )
        s3.put_object(
            Bucket=settings.S3_BUCKET,
            Key=s3_key,
            Body=pdf_bytes,
            ContentType="application/pdf",
        )

    await asyncio.to_thread(_upload)
    logger.info("Proposal PDF uploaded: %s", s3_key)
    return s3_key, pdf_bytes
