#!/usr/bin/env python3
"""
Morning Briefing v2 — No More Chores
=====================================
Generates a Telegram-ready morning dashboard for Mike.

Usage:
  python3 scripts/morning-briefing-v2.py            # Full briefing
  python3 scripts/morning-briefing-v2.py --test      # Debug mode (verbose)
  python3 scripts/morning-briefing-v2.py --section email
  python3 scripts/morning-briefing-v2.py --section calendar
  python3 scripts/morning-briefing-v2.py --section bookings
  python3 scripts/morning-briefing-v2.py --section revenue
  python3 scripts/morning-briefing-v2.py --section slack
  python3 scripts/morning-briefing-v2.py --section complaints
"""

import base64
import email
import imaplib
import json
import os
import re
import subprocess
import sys
import tempfile
import time
import urllib.error
import urllib.parse
import urllib.request
from datetime import datetime, timedelta, timezone
from email.header import decode_header

# ---------------------------------------------------------------------------
# Config
# ---------------------------------------------------------------------------

SECRETS_DIR = os.path.expanduser("~/.openclaw/secrets")
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
WORKSPACE_DIR = os.path.dirname(SCRIPT_DIR)

GMAIL_ACCOUNT = "mikeziarko@gmail.com"
GMAIL_PWD_FILE = os.path.join(SECRETS_DIR, "gmail-app-password.txt")

CALENDAR_SA_FILE = os.path.join(SECRETS_DIR, "google-calendar-sa.json")
CALENDARS = ["mike@nomorechores.com", "mikeziarko@gmail.com"]

LAUNCH27_API_BASE = "https://nomorechores.launch27.com/latest"
LAUNCH27_KEY_FILE = os.path.join(SECRETS_DIR, "launch27-api-key.txt")

SLACK_TOKEN_FILE = os.path.join(SECRETS_DIR, "slack-token.txt")
SLACK_CHANNELS = {
    "calls":             "CDP3Y6YB0",
    "emilys-team":       "C02NFPKNZ1U",
    "customer-feedback": "C0AJQTVST1B",
}
ESCALATION_KEYWORDS = [
    "escalat", "complaint", "urgent", "angry", "upset",
    "refund", "cancel", "terrible", "awful", "horrible",
]

IMPORTANT_EMAIL_SENDERS = [
    "yelp", "google", "chase", "td bank", "bmo", "rbc", "cibc",
    "nicola", "ziarko", "revenue canada", "cra", "quickbooks",
    "intuit", "complaint", "review",
]

MOTIVATIONAL_QUOTES = {
    0: "Monday hustle: the week is yours to own.",         # Mon
    1: "Tuesday momentum: keep building.",                  # Tue
    2: "Wednesday — halfway there, push through.",          # Wed
    3: "Thursday: almost Friday, keep it tight.",           # Thu
    4: "Friday: finish strong, the weekend is earned.",     # Fri
    5: "Saturday: even weekends deserve a win.",            # Sat
    6: "Sunday: rest and plan — tomorrow starts fresh.",    # Sun
}

TEST_MODE = "--test" in sys.argv
SECTION_FILTER = None
if "--section" in sys.argv:
    idx = sys.argv.index("--section")
    if idx + 1 < len(sys.argv):
        SECTION_FILTER = sys.argv[idx + 1]

HTTP_TIMEOUT = 8 if TEST_MODE else 10


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

def dbg(msg):
    if TEST_MODE:
        print(f"  [DEBUG] {msg}", file=sys.stderr)


def read_secret(path):
    with open(path) as f:
        return f.read().strip()


def http_get(url, headers=None, timeout=None):
    req = urllib.request.Request(url, headers=headers or {})
    req.add_header("User-Agent", "MorningBriefing/2.0")
    with urllib.request.urlopen(req, timeout=timeout or HTTP_TIMEOUT) as resp:
        return json.loads(resp.read().decode())


def truncate(s, n=80):
    s = s.replace("\n", " ").strip()
    return s[:n] + "…" if len(s) > n else s


def fmt_currency(amount):
    """Format a number as $X,XXX"""
    try:
        v = float(amount)
        return f"${v:,.0f}"
    except (TypeError, ValueError):
        return str(amount)


# ---------------------------------------------------------------------------
# Section 1: Header
# ---------------------------------------------------------------------------

def section_header():
    now = datetime.now()
    day_name = now.strftime("%A")
    date_str = now.strftime("%B %-d, %Y")
    weekday = now.weekday()
    quote = MOTIVATIONAL_QUOTES.get(weekday, "Let's get it.")

    lines = [
        f"🌤️ Good morning, Mike!",
        f"📆 {day_name}, {date_str}",
        f"💬 {quote}",
    ]
    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Section 2: Email
# ---------------------------------------------------------------------------

def section_email():
    pwd = read_secret(GMAIL_PWD_FILE)
    mail = imaplib.IMAP4_SSL("imap.gmail.com")
    mail.login(GMAIL_ACCOUNT, pwd)
    mail.select("INBOX", readonly=True)

    since = (datetime.now() - timedelta(hours=24)).strftime("%d-%b-%Y")
    status, data = mail.search(None, f"(UNSEEN SINCE {since})")
    ids = data[0].split()
    total = len(ids)
    dbg(f"Email: {total} unread in last 24h")

    important = []
    regular = []

    for mid in ids[:30]:
        _, msg_data = mail.fetch(mid, "(BODY.PEEK[HEADER.FIELDS (FROM SUBJECT)])")
        raw = msg_data[0][1].decode("utf-8", errors="replace")
        msg = email.message_from_string(raw)
        from_raw = msg.get("From", "Unknown")
        subject_raw = msg.get("Subject", "")

        # Decode subject
        decoded = decode_header(subject_raw)
        subject_parts = []
        for part, charset in decoded:
            if isinstance(part, bytes):
                subject_parts.append(part.decode(charset or "utf-8", errors="replace"))
            else:
                subject_parts.append(part)
        subject = "".join(subject_parts) or "(no subject)"

        from_lower = from_raw.lower()
        is_important = any(kw in from_lower for kw in IMPORTANT_EMAIL_SENDERS)

        entry = (from_raw, subject)
        if is_important:
            important.append(entry)
        else:
            regular.append(entry)

    mail.logout()

    lines = [f"📧 Email — {total} unread in last 24h"]

    if important:
        lines.append(f"⚠️ Important ({len(important)}):")
        for frm, subj in important[:5]:
            frm_short = frm.split("<")[0].strip() or frm
            lines.append(f"  • {truncate(frm_short, 25)}: {truncate(subj, 55)}")

    show = regular[:max(0, 5 - len(important))]
    if show:
        lines.append(f"📬 Other highlights:")
        for frm, subj in show:
            frm_short = frm.split("<")[0].strip() or frm
            lines.append(f"  • {truncate(frm_short, 25)}: {truncate(subj, 55)}")

    if total == 0:
        lines.append("  Inbox clear! 🎉")

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Section 3: Calendar
# ---------------------------------------------------------------------------

def _get_calendar_token():
    sa = json.load(open(CALENDAR_SA_FILE))
    header = base64.urlsafe_b64encode(
        json.dumps({"alg": "RS256", "typ": "JWT"}).encode()
    ).rstrip(b"=").decode()

    now = int(time.time())
    claims = {
        "iss":   sa["client_email"],
        "scope": "https://www.googleapis.com/auth/calendar.readonly",
        "aud":   "https://oauth2.googleapis.com/token",
        "iat":   now,
        "exp":   now + 3600,
    }
    payload = base64.urlsafe_b64encode(
        json.dumps(claims).encode()
    ).rstrip(b"=").decode()
    msg = f"{header}.{payload}".encode()

    with tempfile.NamedTemporaryFile(mode="w", suffix=".pem", delete=False) as f:
        f.write(sa["private_key"])
        kf = f.name
    try:
        sig = base64.urlsafe_b64encode(
            subprocess.run(
                ["openssl", "dgst", "-sha256", "-sign", kf],
                input=msg, capture_output=True
            ).stdout
        ).rstrip(b"=").decode()
    finally:
        os.unlink(kf)

    data = urllib.parse.urlencode({
        "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
        "assertion":  f"{header}.{payload}.{sig}",
    }).encode()
    resp = json.loads(
        urllib.request.urlopen(
            urllib.request.Request("https://oauth2.googleapis.com/token", data=data),
            timeout=HTTP_TIMEOUT,
        ).read()
    )
    return resp["access_token"]


def _fetch_calendar_events(token, cal_id, time_min, time_max):
    params = urllib.parse.urlencode({
        "timeMin":      time_min,
        "timeMax":      time_max,
        "singleEvents": "true",
        "orderBy":      "startTime",
        "maxResults":   20,
    })
    url = (
        f"https://www.googleapis.com/calendar/v3/calendars/"
        f"{urllib.parse.quote(cal_id)}/events?{params}"
    )
    req = urllib.request.Request(url, headers={"Authorization": f"Bearer {token}"})
    data = json.loads(urllib.request.urlopen(req, timeout=HTTP_TIMEOUT).read())
    return data.get("items", [])


def _format_event_time(start_dict):
    dt_str = start_dict.get("dateTime")
    if dt_str:
        try:
            # Parse ISO datetime, strip tz offset for display
            dt = datetime.fromisoformat(dt_str.replace("Z", "+00:00"))
            local = dt.astimezone()
            return local.strftime("%-I:%M %p")
        except Exception:
            return dt_str[:16]
    # All-day
    return "All day"


def section_calendar():
    token = _get_calendar_token()
    now = datetime.now(timezone.utc)

    today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
    today_end   = today_start + timedelta(days=1)
    tomorrow_start = today_end
    tomorrow_end   = today_start + timedelta(days=2)

    fmt = "%Y-%m-%dT%H:%M:%SZ"

    today_events = []
    tomorrow_events = []

    for cal_id in CALENDARS:
        dbg(f"Fetching calendar: {cal_id}")
        t_events = _fetch_calendar_events(
            token, cal_id,
            today_start.strftime(fmt), today_end.strftime(fmt)
        )
        tm_events = _fetch_calendar_events(
            token, cal_id,
            tomorrow_start.strftime(fmt), tomorrow_end.strftime(fmt)
        )
        today_events.extend(t_events)
        tomorrow_events.extend(tm_events)

    # Deduplicate by event id
    seen = set()
    unique_today = []
    for e in today_events:
        eid = e.get("id", "")
        if eid not in seen:
            seen.add(eid)
            unique_today.append(e)

    seen = set()
    unique_tomorrow = []
    for e in tomorrow_events:
        eid = e.get("id", "")
        if eid not in seen:
            seen.add(eid)
            unique_tomorrow.append(e)

    # Sort by start time
    def sort_key(e):
        s = e.get("start", {})
        return s.get("dateTime", s.get("date", ""))

    unique_today.sort(key=sort_key)
    unique_tomorrow.sort(key=sort_key)

    lines = ["📅 Calendar"]

    lines.append(f"Today ({datetime.now().strftime('%a %-d %b')}):")
    if unique_today:
        for e in unique_today:
            t = _format_event_time(e.get("start", {}))
            title = e.get("summary", "(no title)")
            lines.append(f"  • {t} — {title}")
    else:
        lines.append("  Clear day! 🎉")

    lines.append(f"Tomorrow ({(datetime.now() + timedelta(days=1)).strftime('%a %-d %b')}):")
    if unique_tomorrow:
        for e in unique_tomorrow:
            t = _format_event_time(e.get("start", {}))
            title = e.get("summary", "(no title)")
            lines.append(f"  • {t} — {title}")
    else:
        lines.append("  Nothing scheduled")

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Section 4: Bookings
# ---------------------------------------------------------------------------

def section_bookings():
    key = read_secret(LAUNCH27_KEY_FILE)
    today = datetime.now().strftime("%Y-%m-%d")

    url = f"{LAUNCH27_API_BASE}/bookings?date_from={today}&date_to={today}"
    dbg(f"Launch27 URL: {url}")

    req = urllib.request.Request(url, headers={"Authorization": f"Bearer {key}"})
    req.add_header("User-Agent", "MorningBriefing/2.0")
    req.add_header("Accept", "application/json")
    try:
        with urllib.request.urlopen(req, timeout=HTTP_TIMEOUT) as resp:
            data = json.loads(resp.read().decode())
    except urllib.error.HTTPError as e:
        if e.code == 405:
            # Launch27 REST API is decommissioned — graceful fallback
            return (
                "📋 Today's Bookings\n"
                "  ⚠️ Launch27 API unavailable (405) — check dashboard manually\n"
                f"  https://nomorechores.launch27.com"
            )
        raise

    bookings = data if isinstance(data, list) else data.get("bookings", [])
    count = len(bookings)
    dbg(f"Bookings: {count} today")

    total_revenue = 0.0
    for b in bookings:
        price = b.get("total") or b.get("price") or b.get("amount") or 0
        try:
            total_revenue += float(price)
        except (TypeError, ValueError):
            pass

    lines = [f"📋 Today's Bookings — {datetime.now().strftime('%b %-d')}"]
    if count == 0:
        lines.append("  No bookings scheduled today")
    else:
        lines.append(f"  {count} booking{'s' if count != 1 else ''} | {fmt_currency(total_revenue)} revenue")
        for b in bookings[:5]:
            t = b.get("start_time") or b.get("time") or b.get("date") or ""
            name = b.get("client_name") or b.get("customer_name") or b.get("name") or "Client"
            address = b.get("address") or b.get("location") or ""
            if t and len(t) > 16:
                t = t[11:16]  # Extract HH:MM from ISO datetime
            line = f"  • {t} {name}"
            if address:
                line += f" — {truncate(address, 35)}"
            lines.append(line)
        if count > 5:
            lines.append(f"  ... and {count - 5} more")

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Section 5: Revenue MTD
# ---------------------------------------------------------------------------

def section_revenue():
    dbg("Running QuickBooks revenue tool...")
    result = subprocess.run(
        ["python3", os.path.join(WORKSPACE_DIR, "tools/quickbooks.py"), "revenue", "1"],
        capture_output=True,
        text=True,
        timeout=45,
        cwd=WORKSPACE_DIR,
    )

    output = result.stdout.strip()
    dbg(f"QB output (first 300): {output[:300]}")

    if result.returncode != 0 and not output:
        raise RuntimeError(f"QB exited {result.returncode}: {result.stderr[:200]}")

    lines = ["💰 Revenue MTD"]

    # Try to find MTD/current month revenue line
    mtd_match = None
    last_month_match = None

    for line in output.splitlines():
        ll = line.lower()
        if "mtd" in ll or "month to date" in ll or "this month" in ll:
            mtd_match = line.strip()
        elif "last month" in ll or "prev" in ll:
            last_month_match = line.strip()

    # Fallback: grab any dollar amounts from the output
    amounts = re.findall(r'\$[\d,]+(?:\.\d+)?', output)

    if mtd_match:
        lines.append(f"  {mtd_match}")
    elif amounts:
        lines.append(f"  MTD: {amounts[0]}")
        if len(amounts) > 1:
            lines.append(f"  Last month: {amounts[1]}")
    else:
        # Just include first few non-empty output lines
        relevant = [l for l in output.splitlines() if l.strip() and not l.startswith("[")]
        for l in relevant[:4]:
            lines.append(f"  {l}")

    if last_month_match and mtd_match:
        lines.append(f"  vs last month: {last_month_match}")

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Section 6: Slack Highlights
# ---------------------------------------------------------------------------

def section_slack():
    token = read_secret(SLACK_TOKEN_FILE)
    since_ts = str((datetime.now(timezone.utc) - timedelta(hours=12)).timestamp())

    lines = ["💬 Overnight Slack (last 12h)"]
    total_msgs = 0
    notable = []

    for channel_name, channel_id in SLACK_CHANNELS.items():
        dbg(f"Fetching Slack #{channel_name}")
        qs = urllib.parse.urlencode({
            "channel": channel_id,
            "oldest":  since_ts,
            "limit":   100,
        })
        url = f"https://slack.com/api/conversations.history?{qs}"
        req = urllib.request.Request(url, headers={"Authorization": f"Bearer {token}"})
        req.add_header("User-Agent", "MorningBriefing/2.0")

        with urllib.request.urlopen(req, timeout=HTTP_TIMEOUT) as resp:
            data = json.loads(resp.read().decode())

        if not data.get("ok"):
            lines.append(f"  #{channel_name}: unavailable")
            continue

        messages = [
            m for m in data.get("messages", [])
            if not m.get("bot_id") and m.get("subtype") != "bot_message"
        ]
        count = len(messages)
        total_msgs += count

        # Check for escalations
        flagged = []
        for m in messages:
            text = m.get("text", "").lower()
            for kw in ESCALATION_KEYWORDS:
                if kw in text:
                    flagged.append(m)
                    break

        flag_str = f" ⚠️ {len(flagged)} flagged" if flagged else ""
        lines.append(f"  #{channel_name}: {count} message{'s' if count != 1 else ''}{flag_str}")

        # Collect notable messages for the bottom
        for m in flagged[:2]:
            text = m.get("text", "")
            notable.append(f"  [{channel_name}] {truncate(text, 70)}")

    if notable:
        lines.append("Notable messages:")
        lines.extend(notable[:3])

    if total_msgs == 0:
        lines.append("  Quiet overnight 😴")

    return "\n".join(lines), total_msgs


# ---------------------------------------------------------------------------
# Section 7: Open Complaints
# ---------------------------------------------------------------------------

def section_complaints():
    result = subprocess.run(
        ["python3", os.path.join(SCRIPT_DIR, "complaint-tracker.py"), "--status"],
        capture_output=True,
        text=True,
        timeout=15,
        cwd=WORKSPACE_DIR,
    )

    output = result.stdout.strip()
    dbg(f"Complaints output: {output[:300]}")

    lines = ["🚨 Open Complaints"]

    if not output or "no open complaints" in output.lower():
        lines.append("  No open complaints 🎉")
    else:
        # Parse the --status output
        complaints = []
        current = {}
        for line in output.splitlines():
            line = line.strip()
            if line.startswith("ID:"):
                if current:
                    complaints.append(current)
                current = {"id": line.split(":", 1)[1].strip()}
            elif line.startswith("Customer:"):
                current["customer"] = line.split(":", 1)[1].strip()
            elif line.startswith("Channel:"):
                current["channel"] = line.split(":", 1)[1].strip()
            elif line.startswith("Date:"):
                current["date"] = line.split(":", 1)[1].strip()
            elif line.startswith("Keyword:"):
                current["keyword"] = line.split(":", 1)[1].strip()
            elif line.startswith("Snippet:"):
                current["snippet"] = line.split(":", 1)[1].strip()
        if current and "id" in current:
            complaints.append(current)

        if complaints:
            lines.append(f"  {len(complaints)} open:")
            for c in complaints:
                cname = c.get("customer", "Unknown")
                cdate = c.get("date", "?")
                cchan = c.get("channel", "?")
                csnip = truncate(c.get("snippet", ""), 60)
                lines.append(f"  • {cname} | {cdate} | {cchan}")
                if csnip:
                    lines.append(f"    {csnip}")
        else:
            # Output exists but we couldn't parse it — show raw
            for l in output.splitlines()[:6]:
                lines.append(f"  {l}")

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------

def run_section(name, fn, *args):
    """Run a section function, return (text, extra) catching all exceptions."""
    try:
        result = fn(*args)
        if isinstance(result, tuple):
            return result[0], result[1] if len(result) > 1 else None
        return result, None
    except Exception as e:
        dbg(f"Section '{name}' failed: {e}")
        if TEST_MODE:
            import traceback
            traceback.print_exc(file=sys.stderr)
        return f"⚠️ {name} unavailable", None


def main():
    # Pulse accumulators
    booking_count = 0
    mtd_revenue = "?"
    email_count = 0
    slack_count = 0

    sections = []

    # --- Header (always) ---
    if not SECTION_FILTER or SECTION_FILTER == "header":
        sections.append(section_header())

    # --- Email ---
    if not SECTION_FILTER or SECTION_FILTER == "email":
        text, _ = run_section("Email", section_email)
        sections.append(text)
        m = re.search(r"(\d+) unread", text)
        if m:
            email_count = int(m.group(1))

    # --- Calendar ---
    if not SECTION_FILTER or SECTION_FILTER == "calendar":
        text, _ = run_section("Calendar", section_calendar)
        sections.append(text)

    # --- Bookings ---
    if not SECTION_FILTER or SECTION_FILTER == "bookings":
        text, _ = run_section("Bookings", section_bookings)
        sections.append(text)
        m = re.search(r"(\d+) booking", text)
        if m:
            booking_count = int(m.group(1))

    # --- Revenue ---
    if not SECTION_FILTER or SECTION_FILTER == "revenue":
        text, _ = run_section("Revenue", section_revenue)
        sections.append(text)
        amounts = re.findall(r'\$[\d,]+', text)
        if amounts:
            mtd_revenue = amounts[0]

    # --- Slack ---
    if not SECTION_FILTER or SECTION_FILTER == "slack":
        text, count = run_section("Slack", section_slack)
        sections.append(text)
        if count is not None:
            slack_count = count

    # --- Complaints ---
    if not SECTION_FILTER or SECTION_FILTER == "complaints":
        text, _ = run_section("Complaints", section_complaints)
        sections.append(text)

    # --- Pulse summary ---
    if not SECTION_FILTER:
        pulse = (
            f"📊 Quick Pulse — "
            f"{booking_count} bookings today | "
            f"{mtd_revenue} MTD revenue | "
            f"{email_count} unread emails | "
            f"{slack_count} Slack messages overnight"
        )
        sections.append(pulse)

    print("\n\n".join(sections))


if __name__ == "__main__":
    main()
