#!/usr/bin/env python3
"""
digest_now.py — on-demand: regenerate the auto-generated precursor files, then send the
two entity-split vault digests IMMEDIATELY (no 9am window). For when the owner wants a fresh
digest early instead of waiting for the scheduled morning send.

This is a thin ORCHESTRATOR over the exact code the hourly sweep already uses. It does
NOT duplicate any logic and it does NOT touch the sweep's schedule or its launchd job:
  - precursor regen  -> the sweep's own deterministic write tasks (Action Board, archive
                        completed todos, frontmatter backfill, broken-link repair), run
                        once now via sweep.py's task functions.
  - digest send      -> digest.py.run_digests(mode="send") — the single shared sender
                        (same path as digest.sh and the sweep), so there is no second
                        send path to drift from.

Independence from the hourly sweep (important — read before editing):
  - It reuses sweep.py / digest.py by import; it changes neither.
  - "today" is pinned to the OWNER'S resolved local date (sweep.resolve_location), exactly
    like the sweep — so an early send while they're abroad is dated/bucketed correctly, not
    off the host's home timezone clock.
  - After a successful send it marks the sweep's OWN per-digest once/day flags
    (digest:<key>:sent = today) in sweep-state.json, so today's scheduled 9am sweep sees
    the digest as already-sent and does NOT mail a duplicate. Pass --keep-scheduled to
    send now AND still let the 9am sweep fire (two copies that day).

WHERE TO RUN: on the the always-on host, where the secrets env (digest.env) and the
`anthropic` package live — NOT on the owner's laptop. The wrapper digest-now.sh sources
digest.env and forces live writes for this process only. From the laptop, drive it over
SSH (see digest-now.sh header).

Flags:
  --plan            show location + what would happen; no writes, no send, no state change
  --keep-scheduled  do not suppress today's 9am scheduled send (allow a second copy)
"""
import os
import sys
import datetime as dt
from pathlib import Path

from alexandria_vault.engine import consume_vault_arg

# `--vault` must land in VAULT_DIR before sweep/digest freeze their vault at import.
consume_vault_arg()

# Reuse the hourly agent's code verbatim: sweep.py owns location/state resolution and the
# precursor write tasks, and (through its _load_digest) digest.py is the shared sender.
# Nothing here re-implements that logic.
from alexandria_vault.engine import sweep  # noqa: E402

# The precursor files digest-now regenerates: the Action Board plus the sweep's auto-safe
# vault cleanups. This is an explicit ALLOWLIST, NOT "every task except the sends" — a
# denylist silently sweeps in any task added later (e.g. the team_inbox email-ingest and
# team_digests SEND tasks the sweep grew), which an on-demand digest must never trigger.
# digest-now sends the two main digests itself (below); everything else is the sweep's job.
_PRECURSOR_TASKS = ("action_board", "archive_completed", "frontmatter_backfill", "link_repair")


def main():
    plan = "--plan" in sys.argv
    keep_scheduled = "--keep-scheduled" in sys.argv
    if sweep.killed():
        sweep.log("digest-now: kill switch active (SWEEP_DISABLE / SWEEP_OFF) — aborting")
        return

    # First load is ONLY for the vault path. digest.py freezes its module-level TODAY at
    # import time from DIGEST_TODAY (unset here), so this module's TODAY is the machine
    # clock — fine for VAULT_DIR, but it must NOT be the one that sends (see below).
    vault = sweep._load_digest().VAULT_DIR
    state = sweep.load_state()
    loc = sweep.resolve_location(state, vault)
    now = loc["now"]
    fake = os.environ.get("SWEEP_FAKE_LOCAL")
    if fake:
        now = dt.datetime.fromisoformat(fake)

    # Pin "today" to the owner's LOCAL date (same as the sweep), so precursor bucketing and the
    # digest's date header + today/tomorrow split are correct even when he's abroad and the
    # host's clock (the home timezone) is on a different calendar day. digest.py reads this once,
    # at import, into its module-level TODAY — so it must be set BEFORE the load that sends
    # (and before each precursor task's fresh _load_digest()), which it now is.
    os.environ["DIGEST_TODAY"] = now.date().isoformat()
    sweep.log(
        f"digest-now: location={loc['location']} tz={loc['tz']} "
        f"local={now.isoformat(timespec='minutes')} today={now.date().isoformat()} "
        f"plan={plan} shadow={sweep.SHADOW} keep_scheduled={keep_scheduled}")
    ctx = {"vault": Path(vault), "loc": loc, "now": now, "state": state,
           "plan": plan, "digest": sweep._load_digest}

    # 1) Regenerate the precursor files — only the allowlisted deterministic write tasks,
    # looked up from sweep.TASKS by name (skip gracefully if one is ever renamed/removed).
    taskmap = dict(sweep.TASKS)
    for name in _PRECURSOR_TASKS:
        fn = taskmap.get(name)
        if fn is None:
            sweep.log(f"  precursor {name}: (not in sweep.TASKS — skipped)")
            continue
        try:
            res = fn(ctx)
            sweep.log(f"  precursor {name}: {res or 'no change'}")
        except Exception as e:
            sweep.log(f"  precursor {name}: ERROR {type(e).__name__}: {e}")

    # 2) Send the two entity-split digests now via the single shared sender (no 9am gate).
    def _record(key):
        # Mark the sweep's own once/day flag so today's scheduled 9am run skips this digest
        # (no duplicate). --keep-scheduled (and --plan) opt out.
        if keep_scheduled or plan:
            return
        sweep.mark_today(state, f"digest:{key}:sent", now)
        sweep.save_state(state)
        sweep.log(f"  state: digest:{key}:sent={now.date().isoformat()} "
                  f"(today's 9am sweep send suppressed)")

    # Re-load digest.py NOW that DIGEST_TODAY is set, so its frozen module-level TODAY
    # (and the date header + today/tomorrow/overdue bucketing) is the owner's local date — not
    # the import-time machine clock. The sweep gets this for free by calling _load_digest()
    # fresh inside its task; do the same here rather than reuse the path-only load above.
    sender = sweep._load_digest()
    results = sender.run_digests(
        mode=("plan" if plan else "send"),
        should_send=None,          # send regardless of any prior send today — this IS the early send
        after_send=_record,
        sink=None,
    )
    for r in results:
        sweep.log(f"  digest: {r}")


if __name__ == "__main__":
    main()
