"""Billing email worker tasks - auditable, deduped, structured logging.

Uses notification_templates for subject/body; notification_repository for dedupe.
"""
from __future__ import annotations
from uuid import UUID

import structlog

from src.config import get_settings
from src.domain.billing import notification_templates as templates

logger = structlog.get_logger()


def _build_dedupe_key(notification_type: str, **kwargs: str) -> str:
    """Stable dedupe keys for replay safety."""
    if notification_type == "invoice_paid":
        return f"invoice_paid:{kwargs.get('stripe_invoice_id', '')}"
    if notification_type == "payment_failed":
        return f"payment_failed:{kwargs.get('stripe_invoice_id', '')}"
    if notification_type == "subscription_canceled":
        return f"subscription_canceled:{kwargs.get('stripe_subscription_id', '')}:{kwargs.get('period_end_iso', '')}"
    if notification_type == "usage_topup_confirmation":
        return f"usage_topup_confirmation:{kwargs.get('stripe_checkout_session_id', '')}"
    if notification_type == "wallet_threshold":
        return (
            f"wallet_threshold:{kwargs.get('wallet_type', '')}:"
            f"{kwargs.get('pct', '')}:{kwargs.get('period_start', '')}"
        )
    return f"{notification_type}:{kwargs.get('source_object_id', '')}"


async def _send_billing_notification(
    ctx: dict,
    workspace_id: UUID,
    notification_type: str,
    source_object_type: str,
    source_object_id: str,
    dedupe_key: str,
    subject: str,
    text_body: str,
    html_body: str,
    include_invoices_read: bool = False,
) -> dict:
    """
    Send billing notification with dedupe and audit.
    Returns {sent, skipped_duplicate, failed, total_recipients, provider, message_ids}.
    """
    from src.core.database import async_session_factory
    from src.domain.billing.notification_repository import BillingNotificationRepository
    from src.domain.billing.recipient_resolver import BillingRecipientResolver

    db_factory = ctx.get("db_factory", async_session_factory)
    provider = None
    try:
        from src.integrations.email_provider import get_email_provider
        provider = get_email_provider()
    except Exception as e:
        logger.warning("billing_email_provider_unavailable", error=str(e), notification_type=notification_type)
        return {
            "sent": 0,
            "skipped_duplicate": 0,
            "failed": 0,
            "total_recipients": 0,
            "provider": "none",
            "message_ids": [],
        }

    provider_name = get_settings().email_provider or "unknown"
    async with db_factory() as db:
        resolver = BillingRecipientResolver(db)
        notif_repo = BillingNotificationRepository(db)
        recipients = await resolver.get_recipients_with_diagnostics(
            workspace_id,
            notification_type=notification_type,
            source_object_id=source_object_id,
            include_invoices_read=include_invoices_read,
        )

        if not recipients:
            logger.info(
                "billing_notification_skipped_zero_recipients",
                notification_type=notification_type,
                workspace_id=str(workspace_id),
                source_object_id=source_object_id,
            )
            await db.commit()
            return {"sent": 0, "skipped_duplicate": 0, "failed": 0, "total_recipients": 0, "provider": provider_name, "message_ids": []}

        sent = 0
        skipped = 0
        failed = 0
        message_ids = []

        for to in recipients:
            rec, is_new = await notif_repo.insert_queued_or_get_duplicate(
                workspace_id=workspace_id,
                notification_type=notification_type,
                source_object_type=source_object_type,
                source_object_id=source_object_id,
                dedupe_key=dedupe_key,
                recipient_email=to,
            )
            if not is_new:
                skipped += 1
                continue

            result = await provider.send(to=to, subject=subject, html=html_body, text=text_body)
            if result.success:
                await notif_repo.mark_sent(rec.id, result.message_id)
                sent += 1
                if result.message_id:
                    message_ids.append(result.message_id)
            else:
                await notif_repo.mark_failed(rec.id, result.error or "unknown")
                failed += 1
                logger.warning(
                    "billing_email_send_failed",
                    to=to,
                    notification_type=notification_type,
                    workspace_id=str(workspace_id),
                    error=result.error,
                )

        logger.info(
            "billing_notification_completed",
            notification_type=notification_type,
            workspace_id=str(workspace_id),
            source_object_id=source_object_id,
            total_recipients=len(recipients),
            sent=sent,
            skipped_duplicate=skipped,
            failed=failed,
            provider=provider_name,
            message_ids=message_ids[:5],
        )
        await db.commit()
        return {
            "sent": sent,
            "skipped_duplicate": skipped,
            "failed": failed,
            "total_recipients": len(recipients),
            "provider": provider_name,
            "message_ids": message_ids,
        }


async def send_invoice_paid_email(
    ctx: dict,
    workspace_id: str,
    stripe_invoice_id: str,
    invoice_url: str,
    amount_paid_minor: int,
    currency: str = "usd",
) -> dict:
    """Send invoice paid notification - deduped by stripe_invoice_id."""
    w_id = UUID(workspace_id)
    dedupe_key = _build_dedupe_key("invoice_paid", stripe_invoice_id=stripe_invoice_id)
    subject = templates.invoice_paid_subject(amount_paid_minor, currency)
    text_body, html_body = templates.invoice_paid_body(invoice_url, amount_paid_minor, currency)
    return await _send_billing_notification(
        ctx, w_id, "invoice_paid", "invoice", stripe_invoice_id, dedupe_key,
        subject, text_body, html_body,
        include_invoices_read=True,
    )


async def send_usage_topup_confirmation_email(
    ctx: dict,
    workspace_id: str,
    stripe_checkout_session_id: str,
    package_name: str,
    units: int,
    amount_minor: int,
    currency: str = "usd",
) -> dict:
    """Send usage top-up confirmation - deduped by stripe_checkout_session_id."""
    w_id = UUID(workspace_id)
    dedupe_key = _build_dedupe_key("usage_topup_confirmation", stripe_checkout_session_id=stripe_checkout_session_id)
    subject = templates.usage_topup_confirmation_subject(package_name)
    text_body, html_body = templates.usage_topup_confirmation_body(package_name, units, amount_minor, currency)
    return await _send_billing_notification(
        ctx, w_id, "usage_topup_confirmation", "usage_credit_purchase", stripe_checkout_session_id, dedupe_key,
        subject, text_body, html_body,
    )


async def send_payment_failed_email(
    ctx: dict,
    workspace_id: str,
    stripe_invoice_id: str,
    invoice_url: str,
) -> dict:
    """Send payment failed notification - deduped by stripe_invoice_id."""
    w_id = UUID(workspace_id)
    dedupe_key = _build_dedupe_key("payment_failed", stripe_invoice_id=stripe_invoice_id)
    subject = templates.payment_failed_subject()
    text_body, html_body = templates.payment_failed_body(invoice_url)
    return await _send_billing_notification(
        ctx, w_id, "payment_failed", "invoice", stripe_invoice_id, dedupe_key,
        subject, text_body, html_body,
    )


async def send_wallet_threshold_email(
    ctx: dict,
    workspace_id: str,
    wallet_type: str,
    pct: int,
    credits_used: float,
    period_start: str,
) -> dict:
    """80% / 100% wallet emails — billing.manage members only."""
    w_id = UUID(workspace_id)
    dedupe_key = _build_dedupe_key(
        "wallet_threshold",
        wallet_type=wallet_type,
        pct=str(pct),
        period_start=period_start,
    )
    subject = templates.wallet_threshold_subject(wallet_type, pct)
    text_body, html_body = templates.wallet_threshold_body(wallet_type, pct, credits_used)
    return await _send_billing_notification(
        ctx,
        w_id,
        "wallet_threshold",
        "wallet",
        f"{wallet_type}:{period_start}:{pct}",
        dedupe_key,
        subject,
        text_body,
        html_body,
        include_invoices_read=False,
    )


async def send_subscription_canceled_email(
    ctx: dict,
    workspace_id: str,
    stripe_subscription_id: str,
    plan_slug: str,
    period_end_iso: str,
) -> dict:
    """Send subscription canceled notification - deduped by subscription + period_end."""
    w_id = UUID(workspace_id)
    dedupe_key = _build_dedupe_key(
        "subscription_canceled",
        stripe_subscription_id=stripe_subscription_id,
        period_end_iso=period_end_iso,
    )
    subject = templates.subscription_canceled_subject(plan_slug)
    text_body, html_body = templates.subscription_canceled_body(plan_slug, period_end_iso)
    return await _send_billing_notification(
        ctx, w_id, "subscription_canceled", "subscription", stripe_subscription_id, dedupe_key,
        subject, text_body, html_body,
    )
