"""Nora's daily report: exact steps in code, wrapped around one call to the brain.

Steps, in order:
  1. yesterdays_sales     code   sales by branch, yesterday and the same day last week
  2. titles_running_low   code   titles under 5 days of stock
  3. hub.call_agent       brain  the morning report, following the daily-report skill
  4. build_page           code   the report inside the shop's page template
  5. publish and email    hub    a private page for Nora, and a link in her inbox

Try it once by hand, without a mail server:
    HUBZOID_EMAIL_DELIVERY=preview hubzoid schedule run wren-page daily_report
Locally (no sign-in) a run acts as admin@localhost. Remove run_as below for a
local try, or sign Nora in once on a shared deployment first.
"""
import csv
import html
from datetime import date, timedelta
from pathlib import Path
from string import Template

from hubzoid import hub, step, workflow
from pydantic import BaseModel

HUB = Path(__file__).resolve().parents[2]
RAW = HUB / "raw_data"
TEMPLATE = Path(__file__).with_name("report_template.html")


class Report(BaseModel):
    """The finished report, returned as structured output so no chat display text is included."""
    html: str


def _sales() -> list[dict]:
    with open(RAW / "sales.csv", newline="") as f:
        return list(csv.DictReader(f))


@step
def yesterdays_sales() -> dict:
    """Copies sold per branch yesterday, and on the same day last week."""
    rows = _sales()
    yesterday = max(date.fromisoformat(r["date"]) for r in rows)
    week_ago = yesterday - timedelta(days=7)
    totals = {"yesterday": {}, "last_week": {}}
    for r in rows:
        day = date.fromisoformat(r["date"])
        key = "yesterday" if day == yesterday else "last_week" if day == week_ago else None
        if key:
            totals[key][r["branch"]] = totals[key].get(r["branch"], 0) + int(r["copies"])
    return {"date": yesterday.isoformat(), **totals}


@step
def titles_running_low(limit_days: float = 5) -> list:
    """Titles under the reorder line, with their days of stock."""
    rows = _sales()
    last = max(date.fromisoformat(r["date"]) for r in rows)
    week = {}
    for r in rows:
        if r["title"] != "All other titles" and date.fromisoformat(r["date"]) > last - timedelta(days=7):
            key = (r["title"], r["branch"])
            week[key] = week.get(key, 0) + int(r["copies"])
    low = []
    with open(RAW / "stock.csv", newline="") as f:
        for r in csv.DictReader(f):
            per_day = week.get((r["title"], r["branch"]), 0) / 7
            if per_day and int(r["copies"]) / per_day < limit_days:
                low.append({"title": r["title"], "branch": r["branch"],
                            "on_hand": int(r["copies"]), "days_of_stock": round(int(r["copies"]) / per_day, 1)})
    return low


@step
def build_page(path: str, report: str, day: str) -> str:
    """Put the agent's report into the shop's page template, and return the page's path."""
    page = Template(TEMPLATE.read_text()).substitute(day=html.escape(day), report=report)
    Path(path).write_text(page)
    return path


@workflow("daily 08:00", timezone="Europe/London", run_as="nora@wrenandpage.example")
def daily_report():
    sales = yesterdays_sales()
    low = titles_running_low()
    report = hub.call_agent(
        "Write the morning report for Nora as an HTML fragment (no <html> or <body>). "
        f"Sales by branch, yesterday and the same day last week: {sales}. "
        f"Titles running low: {low}.",
        response_model=Report,
    ).html
    page = build_page(str(hub.run_dir / "report.html"), report, sales["date"])
    link = hub.publish_artifact(Path(page), title="Daily report")
    hub.send_email("Your daily report", "It's ready.", artifacts=[link])
    return {"report": link["url"]}
