"""RBS REGAL — Notifications Center.

Generates live notifications from app data (low stock, overdue invoices, pending cheques,
GST filing reminders) AND stores user-acknowledged dismissals.

Endpoints:
  GET    /api/notifications?company_id=X        → live + persisted feed
  POST   /api/notifications/{id}/read           → mark single as read
  POST   /api/notifications/read-all            → mark all as read
  GET    /api/notifications/unread-count        → just the badge count
"""
from datetime import datetime, timezone, timedelta
from typing import Optional, List
from fastapi import APIRouter, Depends, Request
from auth import get_current_user

router = APIRouter(prefix="/api/notifications", tags=["notifications"])


def _now_iso():
    return datetime.now(timezone.utc).isoformat()


def _ser(d: dict) -> dict:
    if not d: return d
    out = {k: v for k, v in d.items() if k != "_id"}
    if "_id" in d and not isinstance(d["_id"], str):
        out["id"] = str(d["_id"])
    return out


async def _generate_live(db, user_id: str, company_id: Optional[str]) -> List[dict]:
    """Build live notifications from current data state (not persisted)."""
    out = []
    q = {"company_id": company_id} if company_id else {}

    # Low stock items
    items = await db.items.find(q).to_list(50000)
    low = [i for i in items if (i.get("current_stock") or 0) <= (i.get("low_stock_threshold") or 0) and (i.get("low_stock_threshold") or 0) > 0]
    for it in low[:50]:
        out.append({
            "id": f"low-stock-{it['_id']}",
            "type": "low_stock",
            "severity": "warning",
            "title": f"Low stock: {it.get('name')}",
            "body": f"Only {it.get('current_stock', 0)} {it.get('unit', '')} left (alert at {it.get('low_stock_threshold', 0)})",
            "link": f"/items?q={it.get('name', '')[:30]}",
            "created_at": _now_iso(),
        })

    # Overdue receivables (sale invoices, not paid, older than 30 days)
    cutoff = (datetime.now(timezone.utc) - timedelta(days=30)).isoformat()
    sales = await db.invoices.find({
        **q,
        "type": "sale",
        "payment_status": {"$ne": "paid"},
        "created_at": {"$lt": cutoff},
    }).to_list(200)
    for inv in sales[:30]:
        bal = (inv.get("total") or 0) - (inv.get("amount_received") or 0)
        if bal <= 0: continue
        out.append({
            "id": f"overdue-{inv['_id']}",
            "type": "overdue",
            "severity": "danger",
            "title": f"Overdue: {inv.get('invoice_no')}",
            "body": f"{inv.get('party_name', 'Customer')} owes ₹{bal:,.0f} (over 30 days)",
            "link": f"/sales/{inv['_id']}",
            "created_at": inv.get("created_at") or _now_iso(),
        })

    # Pending cheques (cheques pending more than 7 days)
    cheques = await db.cheques.find({**q, "status": "pending"}).to_list(50)
    week_ago = (datetime.now(timezone.utc) - timedelta(days=7)).isoformat()
    for ch in cheques:
        if (ch.get("cheque_date") or "") < week_ago:
            out.append({
                "id": f"cheque-{ch['_id']}",
                "type": "cheque_pending",
                "severity": "info",
                "title": f"Cheque pending: {ch.get('cheque_number', '')}",
                "body": f"{ch.get('party_name', '')} · ₹{ch.get('amount', 0):,.0f} · {ch.get('direction', 'in')}",
                "link": "/cash-bank/cheques",
                "created_at": ch.get("cheque_date") or _now_iso(),
            })

    # GST filing reminder (around 10th of each month for previous month)
    today = datetime.now(timezone.utc)
    if 5 <= today.day <= 12:
        out.append({
            "id": f"gst-reminder-{today.strftime('%Y-%m')}",
            "type": "gst_reminder",
            "severity": "warning",
            "title": f"GSTR-1 due by 11th {today.strftime('%B')}",
            "body": "File your monthly GST returns for the previous period to avoid penalty.",
            "link": "/gst-filing",
            "created_at": _now_iso(),
        })

    return out


async def _read_set(db, user_id: str) -> set:
    docs = await db.notification_reads.find({"user_id": user_id}).to_list(2000)
    return {d["notification_id"] for d in docs}


@router.get("")
async def list_notifications(
    request: Request,
    company_id: Optional[str] = None,
    user=Depends(get_current_user),
):
    db = request.app.state.db
    live = await _generate_live(db, user["id"], company_id)
    read = await _read_set(db, user["id"])
    enriched = []
    for n in live:
        enriched.append({**n, "read": n["id"] in read})
    # Sort: unread first, then by severity, then by date desc
    sev_rank = {"danger": 0, "warning": 1, "info": 2}
    enriched.sort(key=lambda n: (n["read"], sev_rank.get(n["severity"], 3), n.get("created_at") or ""))
    return enriched


@router.get("/unread-count")
async def unread_count(
    request: Request,
    company_id: Optional[str] = None,
    user=Depends(get_current_user),
):
    db = request.app.state.db
    live = await _generate_live(db, user["id"], company_id)
    read = await _read_set(db, user["id"])
    count = sum(1 for n in live if n["id"] not in read)
    return {"count": count}


@router.post("/{notification_id}/read")
async def mark_read(
    notification_id: str,
    request: Request,
    user=Depends(get_current_user),
):
    db = request.app.state.db
    await db.notification_reads.update_one(
        {"user_id": user["id"], "notification_id": notification_id},
        {"$set": {"read_at": _now_iso()}},
        upsert=True,
    )
    return {"ok": True}


@router.post("/read-all")
async def mark_all_read(
    request: Request,
    company_id: Optional[str] = None,
    user=Depends(get_current_user),
):
    db = request.app.state.db
    live = await _generate_live(db, user["id"], company_id)
    now = _now_iso()
    ops = []
    for n in live:
        ops.append({
            "user_id": user["id"],
            "notification_id": n["id"],
            "read_at": now,
        })
    # Bulk upsert
    for op in ops:
        await db.notification_reads.update_one(
            {"user_id": op["user_id"], "notification_id": op["notification_id"]},
            {"$set": {"read_at": op["read_at"]}},
            upsert=True,
        )
    return {"ok": True, "marked": len(ops)}
