"""Gmail receipt scanner — extracts structured receipt/invoice data from email.

Scans Gmail for receipts, invoices, and payment confirmations.
Parses vendor, amount, date, and category from email content.
Stores structured data in the receipts table.

Usage:
    python -m muleline.connectors.receipts scan [--max 200]
    python -m muleline.connectors.receipts list [--vendor X] [--since 2026-01-01]
    python -m muleline.connectors.receipts summary
"""
from __future__ import annotations

import base64
import hashlib
import re
import time
from datetime import datetime

from muleline.connectors.gmail import get_service, _flatten_parts

RECEIPT_QUERIES = [
    "subject:(receipt OR order confirmation OR payment confirmation)",
    "subject:(invoice) -subject:(action needed)",
    "subject:(your payment OR payment received OR payment processed)",
    "subject:(subscription OR renewal) subject:(receipt OR confirmation OR charged)",
]

VENDOR_PATTERNS = {
    "hetzner": ("Hetzner", "hosting"),
    "cloudflare": ("Cloudflare", "hosting"),
    "apple": ("Apple", "software"),
    "google play": ("Google Play", "software"),
    "google store": ("Google Store", "software"),
    "arlo": ("Arlo Technologies", "security"),
    "xai": ("xAI / Grok", "software"),
    "microsoft": ("Microsoft", "software"),
    "doordash": ("DoorDash", "food"),
    "sroa": ("Storage Rentals of America", "storage"),
    "amazon": ("Amazon", "shopping"),
    "paypal": ("PayPal", "payment"),
    "stripe": ("Stripe", "payment"),
    "nzbgeek": ("NZBGeek", "software"),
    "batteries plus": ("Batteries Plus", "supplies"),
    "irobot": ("iRobot", "home"),
    "costco": ("Costco", "shopping"),
    "cvs": ("CVS Pharmacy", "health"),
    "nest aware": ("Google Nest", "security"),
    "tollway": ("Illinois Tollway", "transport"),
    "getipass": ("Illinois Tollway", "transport"),
}

AMOUNT_PATTERNS = [
    re.compile(r"(?:Amount|Total|Charged|Payment|Price)[:\s]*\$\s*([\d,]+\.?\d*)", re.I),
    re.compile(r"\$\s*([\d,]+\.\d{2})\s*(?:USD|payment|charged|paid|total)", re.I),
    re.compile(r"(?:of|for)\s*\$\s*([\d,]+\.\d{2})", re.I),
    re.compile(r"\$\s*([\d,]+\.\d{2})"),
]


def _extract_body(msg):
    payload = msg.get("payload", {})
    parts = _flatten_parts(payload)
    if not parts:
        data = payload.get("body", {}).get("data", "")
        if data:
            return base64.urlsafe_b64decode(data).decode("utf-8", errors="replace")
        return ""
    for p in parts:
        if p.get("mimeType") == "text/plain":
            data = p.get("body", {}).get("data", "")
            if data:
                return base64.urlsafe_b64decode(data).decode("utf-8", errors="replace")
    for p in parts:
        if p.get("mimeType") == "text/html":
            data = p.get("body", {}).get("data", "")
            if data:
                html = base64.urlsafe_b64decode(data).decode("utf-8", errors="replace")
                return re.sub(r"<[^>]+>", " ", html)
    return ""


def _parse_amount(text):
    for pat in AMOUNT_PATTERNS:
        m = pat.search(text)
        if m:
            val = m.group(1).replace(",", "")
            try:
                cents = int(float(val) * 100)
                if 1 <= cents <= 10_000_00:
                    return cents
            except ValueError:
                continue
    return 0


def _detect_vendor(from_addr, subject, body):
    combined = f"{from_addr} {subject}".lower()
    for key, (name, cat) in VENDOR_PATTERNS.items():
        if key in combined:
            return name, cat
    # Fallback: extract domain from sender
    m = re.search(r"<[^@]+@([^>]+)>", from_addr)
    if m:
        domain = m.group(1).split(".")[-2] if "." in m.group(1) else m.group(1)
        return domain.title(), "other"
    return from_addr.split("<")[0].strip().strip('"'), "other"


def _parse_date(date_str):
    for fmt in ("%a, %d %b %Y %H:%M:%S %z", "%a, %d %b %Y %H:%M:%S", "%d %b %Y %H:%M:%S %z"):
        try:
            return datetime.strptime(date_str.strip()[:31], fmt).strftime("%Y-%m-%d")
        except ValueError:
            continue
    return datetime.now().strftime("%Y-%m-%d")


def _dedup(date, vendor, amount):
    raw = f"{date}|{vendor}|{amount}"
    return hashlib.sha256(raw.encode()).hexdigest()[:32]


def scan_receipts(owner_id: str, max_results: int = 200):
    from muleline.db import get_db

    svc = get_service()
    if not svc:
        return {"error": "Not authenticated. Run: python -m muleline.connectors.gmail auth"}

    conn = get_db()
    stats = {"scanned": 0, "imported": 0, "skipped": 0, "errors": 0}
    seen_ids = set()

    try:
        for query in RECEIPT_QUERIES:
            resp = svc.users().messages().list(
                userId="me", q=query, maxResults=max_results
            ).execute()

            for m_ref in resp.get("messages", []):
                if m_ref["id"] in seen_ids:
                    continue
                seen_ids.add(m_ref["id"])
                stats["scanned"] += 1

                try:
                    msg = svc.users().messages().get(
                        userId="me", id=m_ref["id"], format="full"
                    ).execute()
                    headers = {
                        h["name"]: h["value"]
                        for h in msg["payload"].get("headers", [])
                    }
                    from_addr = headers.get("From", "")
                    subject = headers.get("Subject", "")
                    date_str = headers.get("Date", "")
                    body = _extract_body(msg)

                    vendor, category = _detect_vendor(from_addr, subject, body)
                    amount = _parse_amount(f"{subject} {body[:2000]}")
                    receipt_date = _parse_date(date_str)
                    dh = _dedup(receipt_date, vendor, amount)

                    existing = conn.execute(
                        "SELECT 1 FROM receipts WHERE dedup_hash = ?", (dh,)
                    ).fetchone()
                    if existing:
                        stats["skipped"] += 1
                        continue

                    now = time.time()
                    conn.execute(
                        """INSERT INTO receipts
                           (owner_id, vendor, amount_cents, receipt_date, category,
                            description, source, source_id, dedup_hash, raw_text, created_at)
                           VALUES (?,?,?,?,?,?,?,?,?,?,?)""",
                        (
                            owner_id, vendor, amount, receipt_date, category,
                            subject, "gmail", m_ref["id"], dh,
                            body[:1000], now,
                        ),
                    )
                    stats["imported"] += 1

                    if stats["imported"] % 25 == 0:
                        conn.commit()

                except Exception:
                    stats["errors"] += 1

        conn.commit()
    finally:
        conn.close()

    return stats


def list_receipts(owner_id: str, vendor: str = "", since: str = "", limit: int = 100):
    from muleline.db import get_db

    conn = get_db()
    try:
        query = "SELECT * FROM receipts WHERE owner_id = ?"
        params: list = [owner_id]

        if vendor:
            query += " AND vendor LIKE ?"
            params.append(f"%{vendor}%")
        if since:
            query += " AND receipt_date >= ?"
            params.append(since)

        query += " ORDER BY receipt_date DESC LIMIT ?"
        params.append(limit)

        rows = conn.execute(query, params).fetchall()
        return [dict(r) for r in rows]
    finally:
        conn.close()


def receipt_summary(owner_id: str):
    from muleline.db import get_db

    conn = get_db()
    try:
        total = conn.execute(
            "SELECT COUNT(*), SUM(amount_cents) FROM receipts WHERE owner_id = ?",
            (owner_id,),
        ).fetchone()

        by_vendor = conn.execute(
            """SELECT vendor, COUNT(*) as count, SUM(amount_cents) as total_cents
               FROM receipts WHERE owner_id = ?
               GROUP BY vendor ORDER BY total_cents DESC""",
            (owner_id,),
        ).fetchall()

        by_category = conn.execute(
            """SELECT category, COUNT(*) as count, SUM(amount_cents) as total_cents
               FROM receipts WHERE owner_id = ?
               GROUP BY category ORDER BY total_cents DESC""",
            (owner_id,),
        ).fetchall()

        by_month = conn.execute(
            """SELECT substr(receipt_date, 1, 7) as month, COUNT(*) as count,
                      SUM(amount_cents) as total_cents
               FROM receipts WHERE owner_id = ?
               GROUP BY month ORDER BY month DESC LIMIT 12""",
            (owner_id,),
        ).fetchall()

        return {
            "total_receipts": total[0] or 0,
            "total_amount_cents": total[1] or 0,
            "by_vendor": [dict(r) for r in by_vendor],
            "by_category": [dict(r) for r in by_category],
            "by_month": [dict(r) for r in by_month],
        }
    finally:
        conn.close()


def main():
    import json
    import sys

    if len(sys.argv) < 2:
        print("Usage: python -m muleline.connectors.receipts {scan|list|summary}")
        sys.exit(1)

    cmd = sys.argv[1]

    if cmd == "scan":
        max_results = 200
        for i, arg in enumerate(sys.argv):
            if arg == "--max" and i + 1 < len(sys.argv):
                max_results = int(sys.argv[i + 1])
        result = scan_receipts("default", max_results=max_results)
        print(json.dumps(result, indent=2))

    elif cmd == "list":
        vendor = ""
        since = ""
        for i, arg in enumerate(sys.argv):
            if arg == "--vendor" and i + 1 < len(sys.argv):
                vendor = sys.argv[i + 1]
            if arg == "--since" and i + 1 < len(sys.argv):
                since = sys.argv[i + 1]
        rows = list_receipts("default", vendor=vendor, since=since)
        for r in rows:
            amt = r["amount_cents"] / 100
            print(f"  [{r['receipt_date']}] ${amt:>8.2f}  {r['vendor']:<30s}  {r['description'][:50]}")

    elif cmd == "summary":
        s = receipt_summary("default")
        print(f"Total: {s['total_receipts']} receipts, ${s['total_amount_cents']/100:,.2f}")
        print("\nBy vendor:")
        for v in s["by_vendor"][:15]:
            print(f"  {v['vendor']:<30s} {v['count']:>3d} receipts  ${v['total_cents']/100:>10,.2f}")
        print("\nBy category:")
        for c in s["by_category"]:
            print(f"  {c['category']:<20s} {c['count']:>3d} receipts  ${c['total_cents']/100:>10,.2f}")
        print("\nBy month:")
        for m in s["by_month"]:
            print(f"  {m['month']}  {m['count']:>3d} receipts  ${m['total_cents']/100:>10,.2f}")

    else:
        print(f"Unknown: {cmd}")
        sys.exit(1)


if __name__ == "__main__":
    main()
