from __future__ import annotations

import logging
from decimal import Decimal
from typing import Any

from sqlalchemy import select
from sqlalchemy.orm import Session

from app.modules.core.iam.models import User
from app.modules.email_integration.attachment_service import invoice_attachment, receipt_attachment
from app.modules.email_integration.services import _firm_name, get_email_setting, is_template_allowed_by_preferences, send_template_email

logger = logging.getLogger("audit_firm.email_events")

_TASK_ALERT_TYPES = {"task_assigned", "task_due", "task_overdue", "task_review"}
_BILLING_TEMPLATE_CODES = {"INVOICE_GENERATED", "PAYMENT_REMINDER", "PAYMENT_RECEIVED_RECEIPT", "ONLINE_PAYMENT_SUCCESS", "ONLINE_PAYMENT_FAILED"}


def _money(value: Any) -> str:
    try:
        amount = Decimal(str(value or "0"))
        return f"₹ {amount:,.2f}"
    except Exception:
        return str(value or "")


def _user_display_name(user: User | None) -> str:
    if not user:
        return "User"
    return (getattr(user, "full_name", None) or getattr(user, "email", None) or "User").strip()


def _load_user(db: Session, user_id: int | None) -> User | None:
    if not user_id:
        return None
    try:
        return db.execute(select(User).where(User.id == int(user_id))).scalar_one_or_none()
    except Exception:
        return None


def _can_send_alert_email(db: Session, tenant_id: int | None, branch_id: int | None, alert_type: str | None) -> bool:
    setting = get_email_setting(db, tenant_id, branch_id)
    if not setting or not setting.is_active:
        return False
    if not getattr(setting, "send_alert_emails", False):
        return False
    template_code = _alert_template_code(alert_type)
    if not template_code:
        return False
    allowed, _reason = is_template_allowed_by_preferences(setting, template_code)
    return bool(allowed)


def _can_send_billing_email(db: Session, tenant_id: int | None, branch_id: int | None, template_code: str | None = None) -> bool:
    setting = get_email_setting(db, tenant_id, branch_id)
    if not setting or not setting.is_active or not getattr(setting, "send_billing_emails", False):
        return False
    if template_code:
        allowed, _reason = is_template_allowed_by_preferences(setting, template_code)
        return bool(allowed)
    return True


def _client_email(client: Any) -> str | None:
    if not client:
        return None
    for field in ("email", "alternate_email"):
        value = (getattr(client, field, None) or "").strip()
        if value:
            return value
    return None


def _client_name(client: Any) -> str:
    return (getattr(client, "client_name", None) or getattr(client, "trade_name", None) or "Client").strip()


def _invoice_pay_link(invoice: Any) -> str:
    invoice_id = getattr(invoice, "id", None)
    return f"/client/billing/{invoice_id}/pay-now" if invoice_id else "/client/billing"


def _alert_template_code(alert_type: str | None) -> str | None:
    value = (alert_type or "general").strip().lower()
    if value == "task_assigned":
        return "TASK_ASSIGNED"
    if value == "task_due":
        return "TASK_DUE_TODAY"
    if value == "task_overdue":
        return "TASK_OVERDUE"
    if value == "task_review":
        return "PARTNER_REVIEW_REQUIRED"
    if value == "document_uploaded":
        return "CLIENT_DOCUMENT_RECEIVED"
    if value == "clarification":
        return "CLIENT_CLARIFICATION_REQUEST"
    if value == "attendance":
        return "ATTENDANCE_PUNCH_MISSING"
    if value == "leave":
        return "LEAVE_REQUEST_SUBMITTED"
    if value == "consultant":
        return "CONSULTANT_ASSIGNMENT"
    return None


def send_alert_created_email(db: Session, alert: Any) -> None:
    """Best-effort email notification for any newly created in-app alert.

    This is intentionally non-blocking from business-flow perspective. SMTP
    failure is captured in email_logs by send_template_email and should not
    prevent alert creation, task updates, billing, attendance, etc.
    """
    tenant_id = getattr(alert, "tenant_id", None)
    branch_id = getattr(alert, "branch_id", None)
    alert_type = getattr(alert, "alert_type", None)
    if not _can_send_alert_email(db, tenant_id, branch_id, alert_type):
        return

    template_code = _alert_template_code(alert_type)
    if not template_code:
        return

    user = _load_user(db, getattr(alert, "user_id", None))
    recipient = (getattr(user, "email", None) or "").strip() if user else ""
    if not recipient:
        return

    title = getattr(alert, "title", None) or "Alert"
    message = getattr(alert, "message", None) or ""
    target_url = getattr(alert, "target_url", None) or "/alerts"

    context = {
        "recipient_name": _user_display_name(user),
        "user_name": _user_display_name(user),
        "partner_name": _user_display_name(user),
        "consultant_name": _user_display_name(user),
        "task_title": title,
        "work_title": title,
        "assignment_title": title,
        "service_name": "",
        "client_name": "",
        "engagement_code": "",
        "due_date": "",
        "clarification_text": message,
        "review_note": message,
        "action_url": target_url,
        "login_url": "/login",
    }
    try:
        send_template_email(
            db,
            tenant_id=tenant_id,
            branch_id=branch_id,
            recipient_email=recipient,
            template_code=template_code,
            context=context,
            related_module="alert",
            related_id=getattr(alert, "id", None),
        )
    except Exception:
        logger.exception("Email alert notification failed for alert_id=%s", getattr(alert, "id", None))


def send_invoice_issued_email(db: Session, invoice: Any) -> None:
    tenant_id = getattr(invoice, "tenant_id", None)
    branch_id = getattr(invoice, "branch_id", None)
    if not _can_send_billing_email(db, tenant_id, branch_id, "INVOICE_GENERATED"):
        return
    client = getattr(invoice, "client", None)
    recipient = _client_email(client)
    if not recipient:
        return
    try:
        send_template_email(
            db,
            tenant_id=tenant_id,
            branch_id=branch_id,
            recipient_email=recipient,
            template_code="INVOICE_GENERATED",
            context={
                "client_name": _client_name(client),
                "invoice_number": getattr(invoice, "invoice_no", None) or getattr(invoice, "invoice_number", None) or str(getattr(invoice, "id", "")),
                "invoice_amount": _money(getattr(invoice, "total_amount", None)),
                "outstanding_amount": _money(getattr(invoice, "balance_amount", None)),
                "due_date": getattr(getattr(invoice, "due_date", None), "isoformat", lambda: str(getattr(invoice, "due_date", "")))(),
                "payment_link": _invoice_pay_link(invoice),
                "action_url": _invoice_pay_link(invoice),
            },
            related_module="billing_invoice",
            related_id=getattr(invoice, "id", None),
            attachments=[invoice_attachment(invoice, firm_name=_firm_name(db, tenant_id))],
        )
    except Exception:
        logger.exception("Invoice email failed for invoice_id=%s", getattr(invoice, "id", None))


def send_payment_receipt_email(db: Session, payment: Any) -> None:
    invoice = getattr(payment, "invoice", None)
    tenant_id = getattr(payment, "tenant_id", None) or getattr(invoice, "tenant_id", None)
    branch_id = getattr(payment, "branch_id", None) or getattr(invoice, "branch_id", None)
    if not _can_send_billing_email(db, tenant_id, branch_id, "PAYMENT_RECEIVED_RECEIPT"):
        return
    client = getattr(payment, "client", None) or getattr(invoice, "client", None)
    recipient = _client_email(client)
    if not recipient:
        return
    try:
        send_template_email(
            db,
            tenant_id=tenant_id,
            branch_id=branch_id,
            recipient_email=recipient,
            template_code="PAYMENT_RECEIVED_RECEIPT",
            context={
                "client_name": _client_name(client),
                "invoice_number": getattr(invoice, "invoice_no", None) or str(getattr(invoice, "id", "")),
                "receipt_number": getattr(payment, "receipt_no", None) or str(getattr(payment, "id", "")),
                "payment_amount": _money(getattr(payment, "amount_received", None)),
                "payment_date": getattr(getattr(payment, "payment_date", None), "isoformat", lambda: str(getattr(payment, "payment_date", "")))(),
                "payment_mode": getattr(payment, "mode", None) or "",
                "payment_link": _invoice_pay_link(invoice),
                "action_url": _invoice_pay_link(invoice),
            },
            related_module="billing_payment",
            related_id=getattr(payment, "id", None),
            attachments=[receipt_attachment(payment, firm_name=_firm_name(db, tenant_id))],
        )
    except Exception:
        logger.exception("Payment receipt email failed for payment_id=%s", getattr(payment, "id", None))
