"""
DEWA PR Tracker — Streamlit Dashboard
Run:  streamlit run dashboard.py
"""
import math
import re as _re
from datetime import datetime, timedelta

import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import streamlit as st

import database as db
from config import (
    COST_PER_PODCAST_OLD_AED,
    NOTEBOOKLM_MONTHLY_USD,
    NOTEBOOKLM_START_DATE,
    USD_TO_AED,
    DASHBOARD_TITLE,
    PAGE_SIZE,
)

# ── Page setup ────────────────────────────────────────────────────────────────
st.set_page_config(
    page_title=DASHBOARD_TITLE,
    page_icon="📰",
    layout="wide",
    initial_sidebar_state="expanded",
)

# ── SVG helpers ───────────────────────────────────────────────────────────────
def _svg(paths, size=20, color="#00b4d8"):
    return (
        f'<svg xmlns="http://www.w3.org/2000/svg" width="{size}" height="{size}" '
        f'viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2" '
        f'stroke-linecap="round" stroke-linejoin="round" '
        f'style="vertical-align:middle;margin-right:8px">{paths}</svg>'
    )

ICO_NEWS    = _svg('<path d="M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 0-2 2Zm0 0a2 2 0 0 1-2-2v-9c0-1.1.9-2 2-2h2"/><path d="M18 14h-8"/><path d="M15 18h-5"/><path d="M10 6h8v4h-8V6Z"/>')
ICO_MONEY   = _svg('<line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/>', color="#51cf66")
ICO_CHART   = _svg('<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/>', color="#4cc9f0")
ICO_LIST    = _svg('<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>')
ICO_MIC     = _svg('<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/>', color="#f59e0b")
ICO_MIC_SM  = _svg('<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/>', size=15, color="#f59e0b")
ICO_CLOCK   = _svg('<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>', color="#94a3b8")
ICO_LOG     = _svg('<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/>', color="#94a3b8")
ICO_LINK    = _svg('<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>', size=14, color="#00b4d8")

def section_title(icon, text):
    st.markdown(
        f'
{icon}{text}
',
        unsafe_allow_html=True,
    )

# ── CSS — hide Streamlit chrome (Deploy button, footer, menu) ─────────────────
st.markdown("""

""", unsafe_allow_html=True)

# ── Data loading (cached 5 min) ───────────────────────────────────────────────
@st.cache_data(ttl=300)
def load_data():
    db.init_db()
    prs      = [dict(r) for r in db.get_all_press_releases()]
    pods     = [dict(r) for r in db.get_all_podcasts()]
    roi_rows = [dict(r) for r in db.get_podcasts_monthly_since(NOTEBOOKLM_START_DATE)]
    stats    = db.get_stats()
    log      = [dict(r) for r in db.get_scrape_log()]
    return prs, pods, roi_rows, stats, log

prs, pods, roi_rows, stats, scrape_log = load_data()
df_pr  = pd.DataFrame(prs)
df_pod = pd.DataFrame(pods)

if not df_pod.empty and "title" in df_pod.columns:
    df_pod["language"] = df_pod["title"].apply(
        lambda t: "ar" if _re.search(r"[\u0600-\u06FF]", str(t)) else "en"
    )

if not df_pr.empty:
    df_pr["date"]  = pd.to_datetime(df_pr["date"], errors="coerce")
    df_pr["week"]  = df_pr["date"].dt.to_period("W").astype(str)
    df_pr["month"] = df_pr["date"].dt.to_period("M").astype(str)

if not df_pod.empty:
    df_pod["date"] = pd.to_datetime(df_pod["date"], errors="coerce")

# ── ROI pre-calculations ──────────────────────────────────────────────────────
NOTEBOOKLM_AED   = NOTEBOOKLM_MONTHLY_USD * USD_TO_AED   # ~73.4 AED/month
NLM_START        = pd.Timestamp(NOTEBOOKLM_START_DATE)

df_roi = pd.DataFrame(roi_rows) if roi_rows else pd.DataFrame(
    columns=["month", "total", "en_count", "ar_count"]
)

if not df_roi.empty:
    df_roi["old_cost"]   = df_roi["total"] * COST_PER_PODCAST_OLD_AED
    df_roi["new_cost"]   = NOTEBOOKLM_AED   # subscription per month (fixed)
    df_roi["savings"]    = df_roi["old_cost"] - df_roi["new_cost"]

    total_pods_nlm  = int(df_roi["total"].sum())
    total_old_cost  = float(df_roi["old_cost"].sum())
    total_new_cost  = float(df_roi["new_cost"].sum())
    total_savings   = float(df_roi["savings"].sum())
    roi_pct         = (total_savings / total_old_cost * 100) if total_old_cost > 0 else 0
else:
    total_pods_nlm = total_old_cost = total_new_cost = total_savings = roi_pct = 0

# ── Sidebar ───────────────────────────────────────────────────────────────────
with st.sidebar:
    # DEWA logo — styled text (image blocked externally)
    st.markdown(
        '
DEWA
'
        '
Dubai Electricity & Water Authority
',
        unsafe_allow_html=True,
    )
    st.markdown("---")
    st.markdown("**Filters**")

    if not df_pr.empty and df_pr["date"].notna().any():
        min_date = df_pr["date"].min().date()
        max_date = df_pr["date"].max().date()
    else:
        min_date = datetime(2019, 1, 1).date()
        max_date = datetime.today().date()

    date_from, date_to = st.date_input(
        "Date range",
        value=(NLM_START.date(), max_date),
        min_value=min_date,
        max_value=max_date,
    )

    categories = ["All"]
    if not df_pr.empty and "category" in df_pr.columns:
        cats = df_pr["category"].dropna().unique().tolist()
        categories += sorted(cats)
    selected_cat = st.selectbox("Category", categories)

    has_podcast_filter = st.checkbox("Show only PRs with podcast", value=False)

    st.divider()
    if st.button("Refresh data", use_container_width=True):
        st.cache_data.clear()
        st.rerun()

    if st.button("Run scraper now", use_container_width=True):
        with st.spinner("Scraping DEWA website…"):
            try:
                from scraper import run_scrape
                prs_n, pods_n = run_scrape()
                st.success(f"Done — {prs_n} PRs, {pods_n} podcasts saved.")
                st.cache_data.clear()
                st.rerun()
            except Exception as e:
                st.error(f"Scraper error: {e}")

    st.divider()
    st.caption(f"Last scraped: {stats.get('last_scraped', 'Never') or 'Never'}")

# ── Apply PR filters ──────────────────────────────────────────────────────────
filtered = df_pr.copy()
if not filtered.empty:
    if filtered["date"].notna().any():
        filtered = filtered[
            (filtered["date"].dt.date >= date_from) &
            (filtered["date"].dt.date <= date_to)
        ]
    if selected_cat != "All":
        filtered = filtered[filtered["category"] == selected_cat]
    if has_podcast_filter:
        filtered = filtered[filtered["podcast_count"] > 0]

# ═══════════════════════════════════════════════════════════════════════════════
# HEADER
# ═══════════════════════════════════════════════════════════════════════════════
st.markdown(
    f'
'
    f'{_svg("""<path d="M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 0-2 2Zm0 0a2 2 0 0 1-2-2v-9c0-1.1.9-2 2-2h2"/><path d="M18 14h-8"/><path d="M15 18h-5"/><path d="M10 6h8v4h-8V6Z"/>""", size=34, color="#00b4d8")}'
    f'{DASHBOARD_TITLE}
',
    unsafe_allow_html=True,
)
st.markdown(
    f'
'
    f'Tracking all DEWA press releases and NotebookLM podcast ROI since {NOTEBOOKLM_START_DATE} — '
    f'{datetime.today().strftime("%d %B %Y")}
',
    unsafe_allow_html=True,
)
st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 1 — Key Metrics
# ═══════════════════════════════════════════════════════════════════════════════
today       = datetime.today().date()
week_start  = today - timedelta(days=today.weekday())
month_start = today.replace(day=1)

if not filtered.empty and filtered["date"].notna().any():
    prs_today = len(filtered[filtered["date"].dt.date == today])
    prs_week  = len(filtered[filtered["date"].dt.date >= week_start])
    prs_month = len(filtered[filtered["date"].dt.date >= month_start])
else:
    prs_today = prs_week = prs_month = 0

c1, c2, c3, c4, c5 = st.columns(5)
with c1:
    st.metric("Total Press Releases", f"{len(filtered):,}")
with c2:
    st.metric("Podcasts (since May 2025)", f"{total_pods_nlm:,}")
with c3:
    st.metric("Published Today", prs_today)
with c4:
    st.metric("This Week", prs_week)
with c5:
    st.metric("This Month", prs_month)

st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 2 — NotebookLM ROI  (auto-calculated from May 23, 2025)
# ═══════════════════════════════════════════════════════════════════════════════
section_title(ICO_MONEY, "NotebookLM ROI — Since 23 May 2025")

# Summary cards
sc1, sc2, sc3, sc4 = st.columns(4)
with sc1:
    st.markdown(
        f'
'
        f'
Old Cost (Traditional)
'
        f'
AED {total_old_cost:,.0f}
'
        f'
{total_pods_nlm} podcasts × AED 20,000
'
        f'
',
        unsafe_allow_html=True,
    )
with sc2:
    months_active = len(df_roi)
    st.markdown(
        f'
'
        f'
New Cost (NotebookLM)
'
        f'
AED {total_new_cost:,.0f}
'
        f'
{months_active} months × AED {NOTEBOOKLM_AED:.0f}/mo (${NOTEBOOKLM_MONTHLY_USD})
'
        f'
',
        unsafe_allow_html=True,
    )
with sc3:
    st.markdown(
        f'
'
        f'
Total Savings
'
        f'
AED {total_savings:,.0f}
'
        f'
Since {NOTEBOOKLM_START_DATE}
'
        f'
',
        unsafe_allow_html=True,
    )
with sc4:
    st.markdown(
        f'
'
        f'
Return on Investment
'
        f'
{roi_pct:.1f}%
'
        f'
Savings ÷ Old Cost
'
        f'
',
        unsafe_allow_html=True,
    )

st.markdown("
", unsafe_allow_html=True)

# ── Monthly detail ────────────────────────────────────────────────────────────
if not df_roi.empty:
    chart_col, table_col = st.columns([3, 2])

    with chart_col:
        # Grouped bar: old vs new cost per month
        df_chart = df_roi[["month", "old_cost", "new_cost"]].copy()
        df_chart_long = df_chart.melt(
            id_vars="month",
            value_vars=["old_cost", "new_cost"],
            var_name="Type",
            value_name="AED",
        )
        df_chart_long["Type"] = df_chart_long["Type"].map(
            {"old_cost": "Old Cost (Traditional)", "new_cost": "New Cost (NotebookLM)"}
        )
        fig_bar = px.bar(
            df_chart_long, x="month", y="AED", color="Type", barmode="group",
            color_discrete_map={
                "Old Cost (Traditional)": "#ff6b6b",
                "New Cost (NotebookLM)":  "#51cf66",
            },
            title="Monthly Cost Comparison: Traditional vs NotebookLM",
        )
        fig_bar.update_layout(
            paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
            font_color="white", legend_title_text="",
            xaxis=dict(showgrid=False, title=""),
            yaxis=dict(gridcolor="#1e293b", title="AED"),
            margin=dict(t=40, b=10), height=320,
        )
        st.plotly_chart(fig_bar, use_container_width=True)

        # Podcast count per month
        fig_count = px.bar(
            df_roi, x="month", y="total",
            color_discrete_sequence=["#f59e0b"],
            title="Podcasts Produced per Month (since NotebookLM)",
        )
        fig_count.update_layout(
            paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
            font_color="white",
            xaxis=dict(showgrid=False, title=""),
            yaxis=dict(gridcolor="#1e293b", title="Podcasts"),
            margin=dict(t=40, b=10), height=280,
        )
        st.plotly_chart(fig_count, use_container_width=True)

    with table_col:
        st.markdown(
            '
'
            'Monthly breakdown — each row = 1 calendar month
',
            unsafe_allow_html=True,
        )
        rows_html = ""
        for _, r in df_roi.iterrows():
            rows_html += (
                f'
'
                f'{r["month"]}	'
                f'{int(r["total"])}	'
                f'{int(r["en_count"])}	'
                f'{int(r["ar_count"])}	'
                f'AED {r["old_cost"]:,.0f}	'
                f'AED {r["new_cost"]:,.0f}	'
                f'AED {r["savings"]:,.0f}	'
                f'
'
            )
        # Total row
        rows_html += (
            f'
'
            f'TOTAL	'
            f'{total_pods_nlm}	'
            f'{int(df_roi["en_count"].sum())}	'
            f'{int(df_roi["ar_count"].sum())}	'
            f'AED {total_old_cost:,.0f}	'
            f'AED {total_new_cost:,.0f}	'
            f'AED {total_savings:,.0f}	'
            f'
'
        )
        st.markdown(
            f'
'
            f'

'
            f'Month	Podcasts	EN	AR	'
            f'Old Cost	New Cost	Savings	'
            f'

'
            f'
{rows_html}
'
            f'
',
            unsafe_allow_html=True,
        )

st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 3 — Publication Trends
# ═══════════════════════════════════════════════════════════════════════════════
section_title(ICO_CHART, "Publication Trends")
tab_daily, tab_weekly, tab_monthly = st.tabs(["Daily", "Weekly", "Monthly"])

def make_bar(df_g, x, y, title, color="#00b4d8"):
    fig = px.bar(df_g, x=x, y=y, title=title, color_discrete_sequence=[color])
    fig.update_layout(
        paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
        font_color="white",
        xaxis=dict(showgrid=False, title=""),
        yaxis=dict(gridcolor="#1e293b"),
        margin=dict(t=40, b=10),
    )
    return fig

with tab_daily:
    if not filtered.empty and filtered["date"].notna().any():
        daily = filtered.groupby(filtered["date"].dt.date).size().reset_index(name="count")
        daily.columns = ["date", "count"]
        st.plotly_chart(make_bar(daily, "date", "count", "Press Releases per Day"), use_container_width=True)
    else:
        st.info("No data yet. Run the scraper first.")

with tab_weekly:
    if not filtered.empty and "week" in filtered.columns and filtered["date"].notna().any():
        weekly = filtered.groupby("week").size().reset_index(name="count")
        st.plotly_chart(make_bar(weekly, "week", "count", "Press Releases per Week", "#4cc9f0"), use_container_width=True)
    else:
        st.info("No data yet.")

with tab_monthly:
    if not filtered.empty and "month" in filtered.columns and filtered["date"].notna().any():
        monthly = filtered.groupby("month").size().reset_index(name="count")
        st.plotly_chart(make_bar(monthly, "month", "count", "Press Releases per Month", "#7209b7"), use_container_width=True)
    else:
        st.info("No data yet.")

st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 4 — Press Releases Table
# ═══════════════════════════════════════════════════════════════════════════════
section_title(ICO_LIST, "Press Releases")

search = st.text_input("Search press releases", placeholder="Filter by title…")
display = (
    filtered[filtered["title"].str.contains(search, case=False, na=False)]
    if search else filtered.copy()
)

total_pages = max(1, math.ceil(len(display) / PAGE_SIZE))
page_num    = st.number_input("Page", min_value=1, max_value=total_pages, value=1, step=1)
start       = (page_num - 1) * PAGE_SIZE
end         = start + PAGE_SIZE
page_data   = display.iloc[start:end]

st.caption(
    f"Showing {start+1}–{min(end, len(display))} of {len(display)} press releases "
    f"(page {page_num}/{total_pages})"
)

for _, row in page_data.iterrows():
    with st.container():
        tc, dc, pc = st.columns([5, 2, 1])
        with tc:
            url   = row.get("url", "")
            title = row.get("title", "—")
            st.markdown(f"**[{title}]({url})**" if url else f"**{title}**")
            desc = str(row.get("description") or "")
            if desc:
                st.caption(desc[:160] + "…" if len(desc) > 160 else desc)
        with dc:
            d = row.get("date")
            st.markdown(f"`{d.strftime('%d %b %Y') if pd.notna(d) else '—'}`")
        with pc:
            pc_count = row.get("podcast_count", 0)
            st.markdown(
                ICO_MIC_SM + f" {pc_count}" if pc_count else "—",
                unsafe_allow_html=True,
            )
    st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 5 — Podcasts
# ═══════════════════════════════════════════════════════════════════════════════
if not df_pod.empty:
    section_title(ICO_MIC, "Podcasts")

    lang_filter = st.radio("Language", ["All", "English", "Arabic"], horizontal=True)
    df_pod_show = df_pod.copy()
    if lang_filter == "English":
        df_pod_show = df_pod_show[df_pod_show["language"] == "en"]
    elif lang_filter == "Arabic":
        df_pod_show = df_pod_show[df_pod_show["language"] == "ar"]

    pod_search = st.text_input("Search podcasts", placeholder="Filter by title…")
    if pod_search:
        df_pod_show = df_pod_show[
            df_pod_show["title"].str.contains(pod_search, case=False, na=False)
        ]

    st.caption(f"Showing {len(df_pod_show)} of {len(df_pod)} episodes")

    for _, pod in df_pod_show.head(100).iterrows():
        with st.expander(f"{pod.get('title', '—')}  |  {pod.get('date', '')}"):
            col_a, col_b = st.columns([3, 1])
            with col_a:
                if pod.get("pr_title"):
                    st.markdown(ICO_LINK + f" **Linked PR:** {pod['pr_title']}", unsafe_allow_html=True)
                if pod.get("url"):
                    st.markdown(f"[Open on RSS.com]({pod['url']})")
                if pod.get("audio_url"):
                    st.audio(pod["audio_url"])
            with col_b:
                if pod.get("duration"):
                    st.metric("Duration", pod["duration"])
                st.metric("Saved vs old cost", f"AED {COST_PER_PODCAST_OLD_AED:,}")

    st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 6 — Scrape Log
# ═══════════════════════════════════════════════════════════════════════════════
with st.expander("Scrape history"):
    if scrape_log:
        df_log = pd.DataFrame(scrape_log)
        st.dataframe(
            df_log[["ran_at", "prs_found", "pods_found", "status", "notes"]],
            use_container_width=True,
        )
    else:
        st.info("No scrape runs recorded yet.")

st.markdown(
    '
'
    'DEWA PR Tracker · Playwright + SQLite + Streamlit
',
    unsafe_allow_html=True,
)

-    roi_rows = [dict(r) for r in db.get_podcasts_monthly_since(NOTEBOOKLM_START_DATE)]

+    roi_rows = [dict(r) for r in db.get_roi_monthly_since(NOTEBOOKLM_START_DATE)]


Edit

if not df_roi.empty:
    df_roi["old_cost"]   = df_roi["total"] * COST_PER_PODCAST_OLD_AED
    df_roi["new_cost"]   = NOTEBOOKLM_AED   # subscription per month (fixed)
    df_roi["savings"]    = df_roi["old_cost"] - df_roi["new_cost"]

    total_pods_nlm  = int(df_roi["total"].sum())
    total_old_cost  = float(df_roi["old_cost"].sum())
    total_new_cost  = float(df_roi["new_cost"].sum())
    total_savings   = float(df_roi["savings"].sum())
    roi_pct         = (total_savings / total_old_cost * 100) if total_old_cost > 0 else 0
else:
    total_pods_nlm = total_old_cost = total_new_cost = total_savings = roi_pct = 0

if not df_roi.empty:
    # Cost basis: 1 podcast per press release @ AED 20,000
    # (PR cost = 0 AED; only the podcast production was charged)
    df_roi["old_cost"] = df_roi["pr_count"] * COST_PER_PODCAST_OLD_AED
    df_roi["new_cost"] = NOTEBOOKLM_AED   # flat monthly subscription
    df_roi["savings"]  = df_roi["old_cost"] - df_roi["new_cost"]

    total_prs_nlm   = int(df_roi["pr_count"].sum())
    total_eps_nlm   = int(df_roi["total_episodes"].sum())
    total_old_cost  = float(df_roi["old_cost"].sum())
    total_new_cost  = float(df_roi["new_cost"].sum())
    total_savings   = float(df_roi["savings"].sum())
    roi_pct         = (total_savings / total_old_cost * 100) if total_old_cost > 0 else 0
else:
    total_prs_nlm = total_eps_nlm = total_old_cost = total_new_cost = total_savings = roi_pct = 0


The file /Users/khalidbinkalban/Projects/dewa-pr-tracker/dashboard.py has been updated successfully.

if not df_roi.empty:
    # Cost basis: 1 podcast per press release @ AED 20,000
    # (PR cost = 0 AED; only the podcast production was charged)
    df_roi["old_cost"] = df_roi["pr_count"] * COST_PER_PODCAST_OLD_AED
    df_roi["new_cost"] = NOTEBOOKLM_AED   # flat monthly subscription
    df_roi["savings"]  = df_roi["old_cost"] - df_roi["new_cost"]

    total_prs_nlm   = int(df_roi["pr_count"].sum())
    total_eps_nlm   = int(df_roi["total_episodes"].sum())
    total_old_cost  = float(df_roi["old_cost"].sum())
    total_new_cost  = float(df_roi["new_cost"].sum())
    total_savings   = float(df_roi["savings"].sum())
    roi_pct         = (total_savings / total_old_cost * 100) if total_old_cost > 0 else 0
else:
    total_prs_nlm = total_eps_nlm = total_old_cost = total_new_cost = total_savings = roi_pct = 0

"""
DEWA PR Tracker — Streamlit Dashboard
Run:  streamlit run dashboard.py
"""
import math
import re as _re
from datetime import datetime, timedelta

import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import streamlit as st

import database as db
from config import (
    COST_PER_PODCAST_OLD_AED,
    NOTEBOOKLM_MONTHLY_USD,
    NOTEBOOKLM_START_DATE,
    USD_TO_AED,
    DASHBOARD_TITLE,
    PAGE_SIZE,
)

# ── Page setup ────────────────────────────────────────────────────────────────
st.set_page_config(
    page_title=DASHBOARD_TITLE,
    page_icon="📰",
    layout="wide",
    initial_sidebar_state="expanded",
)

# ── SVG helpers ───────────────────────────────────────────────────────────────
def _svg(paths, size=20, color="#00b4d8"):
    return (
        f'<svg xmlns="http://www.w3.org/2000/svg" width="{size}" height="{size}" '
        f'viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2" '
        f'stroke-linecap="round" stroke-linejoin="round" '
        f'style="vertical-align:middle;margin-right:8px">{paths}</svg>'
    )

ICO_NEWS    = _svg('<path d="M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 0-2 2Zm0 0a2 2 0 0 1-2-2v-9c0-1.1.9-2 2-2h2"/><path d="M18 14h-8"/><path d="M15 18h-5"/><path d="M10 6h8v4h-8V6Z"/>')
ICO_MONEY   = _svg('<line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/>', color="#51cf66")
ICO_CHART   = _svg('<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/>', color="#4cc9f0")
ICO_LIST    = _svg('<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>')
ICO_MIC     = _svg('<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/>', color="#f59e0b")
ICO_MIC_SM  = _svg('<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/>', size=15, color="#f59e0b")
ICO_CLOCK   = _svg('<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>', color="#94a3b8")
ICO_LOG     = _svg('<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/>', color="#94a3b8")
ICO_LINK    = _svg('<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>', size=14, color="#00b4d8")

def section_title(icon, text):
    st.markdown(
        f'
{icon}{text}
',
        unsafe_allow_html=True,
    )

# ── CSS — hide Streamlit chrome (Deploy button, footer, menu) ─────────────────
st.markdown("""

""", unsafe_allow_html=True)

# ── Data loading (cached 5 min) ───────────────────────────────────────────────
@st.cache_data(ttl=300)
def load_data():
    db.init_db()
    prs      = [dict(r) for r in db.get_all_press_releases()]
    pods     = [dict(r) for r in db.get_all_podcasts()]
    roi_rows = [dict(r) for r in db.get_roi_monthly_since(NOTEBOOKLM_START_DATE)]
    stats    = db.get_stats()
    log      = [dict(r) for r in db.get_scrape_log()]
    return prs, pods, roi_rows, stats, log

prs, pods, roi_rows, stats, scrape_log = load_data()
df_pr  = pd.DataFrame(prs)
df_pod = pd.DataFrame(pods)

if not df_pod.empty and "title" in df_pod.columns:
    df_pod["language"] = df_pod["title"].apply(
        lambda t: "ar" if _re.search(r"[\u0600-\u06FF]", str(t)) else "en"
    )

if not df_pr.empty:
    df_pr["date"]  = pd.to_datetime(df_pr["date"], errors="coerce")
    df_pr["week"]  = df_pr["date"].dt.to_period("W").astype(str)
    df_pr["month"] = df_pr["date"].dt.to_period("M").astype(str)

if not df_pod.empty:
    df_pod["date"] = pd.to_datetime(df_pod["date"], errors="coerce")

# ── ROI pre-calculations ──────────────────────────────────────────────────────
NOTEBOOKLM_AED   = NOTEBOOKLM_MONTHLY_USD * USD_TO_AED   # ~73.4 AED/month
NLM_START        = pd.Timestamp(NOTEBOOKLM_START_DATE)

df_roi = pd.DataFrame(roi_rows) if roi_rows else pd.DataFrame(
    columns=["month", "total", "en_count", "ar_count"]
)

if not df_roi.empty:
    df_roi["old_cost"]   = df_roi["total"] * COST_PER_PODCAST_OLD_AED
    df_roi["new_cost"]   = NOTEBOOKLM_AED   # subscription per month (fixed)
    df_roi["savings"]    = df_roi["old_cost"] - df_roi["new_cost"]

    total_pods_nlm  = int(df_roi["total"].sum())
    total_old_cost  = float(df_roi["old_cost"].sum())
    total_new_cost  = float(df_roi["new_cost"].sum())
    total_savings   = float(df_roi["savings"].sum())
    roi_pct         = (total_savings / total_old_cost * 100) if total_old_cost > 0 else 0
else:
    total_pods_nlm = total_old_cost = total_new_cost = total_savings = roi_pct = 0

# ── Sidebar ───────────────────────────────────────────────────────────────────
with st.sidebar:
    # DEWA logo — styled text (image blocked externally)
    st.markdown(
        '
DEWA
'
        '
Dubai Electricity & Water Authority
',
        unsafe_allow_html=True,
    )
    st.markdown("---")
    st.markdown("**Filters**")

    if not df_pr.empty and df_pr["date"].notna().any():
        min_date = df_pr["date"].min().date()
        max_date = df_pr["date"].max().date()
    else:
        min_date = datetime(2019, 1, 1).date()
        max_date = datetime.today().date()

    date_from, date_to = st.date_input(
        "Date range",
        value=(NLM_START.date(), max_date),
        min_value=min_date,
        max_value=max_date,
    )

    categories = ["All"]
    if not df_pr.empty and "category" in df_pr.columns:
        cats = df_pr["category"].dropna().unique().tolist()
        categories += sorted(cats)
    selected_cat = st.selectbox("Category", categories)

    has_podcast_filter = st.checkbox("Show only PRs with podcast", value=False)

    st.divider()
    if st.button("Refresh data", use_container_width=True):
        st.cache_data.clear()
        st.rerun()

    if st.button("Run scraper now", use_container_width=True):
        with st.spinner("Scraping DEWA website…"):
            try:
                from scraper import run_scrape
                prs_n, pods_n = run_scrape()
                st.success(f"Done — {prs_n} PRs, {pods_n} podcasts saved.")
                st.cache_data.clear()
                st.rerun()
            except Exception as e:
                st.error(f"Scraper error: {e}")

    st.divider()
    st.caption(f"Last scraped: {stats.get('last_scraped', 'Never') or 'Never'}")

# ── Apply PR filters ──────────────────────────────────────────────────────────
filtered = df_pr.copy()
if not filtered.empty:
    if filtered["date"].notna().any():
        filtered = filtered[
            (filtered["date"].dt.date >= date_from) &
            (filtered["date"].dt.date <= date_to)
        ]
    if selected_cat != "All":
        filtered = filtered[filtered["category"] == selected_cat]
    if has_podcast_filter:
        filtered = filtered[filtered["podcast_count"] > 0]

# ═══════════════════════════════════════════════════════════════════════════════
# HEADER
# ═══════════════════════════════════════════════════════════════════════════════
st.markdown(
    f'
'
    f'{_svg("""<path d="M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 0-2 2Zm0 0a2 2 0 0 1-2-2v-9c0-1.1.9-2 2-2h2"/><path d="M18 14h-8"/><path d="M15 18h-5"/><path d="M10 6h8v4h-8V6Z"/>""", size=34, color="#00b4d8")}'
    f'{DASHBOARD_TITLE}
',
    unsafe_allow_html=True,
)
st.markdown(
    f'
'
    f'Tracking all DEWA press releases and NotebookLM podcast ROI since {NOTEBOOKLM_START_DATE} — '
    f'{datetime.today().strftime("%d %B %Y")}
',
    unsafe_allow_html=True,
)
st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 1 — Key Metrics
# ═══════════════════════════════════════════════════════════════════════════════
today       = datetime.today().date()
week_start  = today - timedelta(days=today.weekday())
month_start = today.replace(day=1)

if not filtered.empty and filtered["date"].notna().any():
    prs_today = len(filtered[filtered["date"].dt.date == today])
    prs_week  = len(filtered[filtered["date"].dt.date >= week_start])
    prs_month = len(filtered[filtered["date"].dt.date >= month_start])
else:
    prs_today = prs_week = prs_month = 0

c1, c2, c3, c4, c5 = st.columns(5)
with c1:
    st.metric("Total Press Releases", f"{len(filtered):,}")
with c2:
    st.metric("Podcasts (since May 2025)", f"{total_pods_nlm:,}")
with c3:
    st.metric("Published Today", prs_today)
with c4:
    st.metric("This Week", prs_week)
with c5:
    st.metric("This Month", prs_month)

st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 2 — NotebookLM ROI  (auto-calculated from May 23, 2025)
# ═══════════════════════════════════════════════════════════════════════════════
section_title(ICO_MONEY, "NotebookLM ROI — Since 23 May 2025")

# Summary cards
sc1, sc2, sc3, sc4 = st.columns(4)
with sc1:
    st.markdown(
        f'
'
        f'
Old Cost (Traditional)
'
        f'
AED {total_old_cost:,.0f}
'
        f'
{total_pods_nlm} podcasts × AED 20,000
'
        f'
',
        unsafe_allow_html=True,
    )
with sc2:
    months_active = len(df_roi)
    st.markdown(
        f'
'
        f'
New Cost (NotebookLM)
'
        f'
AED {total_new_cost:,.0f}
'
        f'
{months_active} months × AED {NOTEBOOKLM_AED:.0f}/mo (${NOTEBOOKLM_MONTHLY_USD})
'
        f'
',
        unsafe_allow_html=True,
    )
with sc3:
    st.markdown(
        f'
'
        f'
Total Savings
'
        f'
AED {total_savings:,.0f}
'
        f'
Since {NOTEBOOKLM_START_DATE}
'
        f'
',
        unsafe_allow_html=True,
    )
with sc4:
    st.markdown(
        f'
'
        f'
Return on Investment
'
        f'
{roi_pct:.1f}%
'
        f'
Savings ÷ Old Cost
'
        f'
',
        unsafe_allow_html=True,
    )

st.markdown("
", unsafe_allow_html=True)

# ── Monthly detail ────────────────────────────────────────────────────────────
if not df_roi.empty:
    chart_col, table_col = st.columns([3, 2])

    with chart_col:
        # Grouped bar: old vs new cost per month
        df_chart = df_roi[["month", "old_cost", "new_cost"]].copy()
        df_chart_long = df_chart.melt(
            id_vars="month",
            value_vars=["old_cost", "new_cost"],
            var_name="Type",
            value_name="AED",
        )
        df_chart_long["Type"] = df_chart_long["Type"].map(
            {"old_cost": "Old Cost (Traditional)", "new_cost": "New Cost (NotebookLM)"}
        )
        fig_bar = px.bar(
            df_chart_long, x="month", y="AED", color="Type", barmode="group",
            color_discrete_map={
                "Old Cost (Traditional)": "#ff6b6b",
                "New Cost (NotebookLM)":  "#51cf66",
            },
            title="Monthly Cost Comparison: Traditional vs NotebookLM",
        )
        fig_bar.update_layout(
            paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
            font_color="white", legend_title_text="",
            xaxis=dict(showgrid=False, title=""),
            yaxis=dict(gridcolor="#1e293b", title="AED"),
            margin=dict(t=40, b=10), height=320,
        )
        st.plotly_chart(fig_bar, use_container_width=True)

        # Podcast count per month
        fig_count = px.bar(
            df_roi, x="month", y="total",
            color_discrete_sequence=["#f59e0b"],
            title="Podcasts Produced per Month (since NotebookLM)",
        )
        fig_count.update_layout(
            paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
            font_color="white",
            xaxis=dict(showgrid=False, title=""),
            yaxis=dict(gridcolor="#1e293b", title="Podcasts"),
            margin=dict(t=40, b=10), height=280,
        )
        st.plotly_chart(fig_count, use_container_width=True)

    with table_col:
        st.markdown(
            '
'
            'Monthly breakdown — each row = 1 calendar month
',
            unsafe_allow_html=True,
        )
        rows_html = ""
        for _, r in df_roi.iterrows():
            rows_html += (
                f'
'
                f'{r["month"]}	'
                f'{int(r["total"])}	'
                f'{int(r["en_count"])}	'
                f'{int(r["ar_count"])}	'
                f'AED {r["old_cost"]:,.0f}	'
                f'AED {r["new_cost"]:,.0f}	'
                f'AED {r["savings"]:,.0f}	'
                f'
'
            )
        # Total row
        rows_html += (
            f'
'
            f'TOTAL	'
            f'{total_pods_nlm}	'
            f'{int(df_roi["en_count"].sum())}	'
            f'{int(df_roi["ar_count"].sum())}	'
            f'AED {total_old_cost:,.0f}	'
            f'AED {total_new_cost:,.0f}	'
            f'AED {total_savings:,.0f}	'
            f'
'
        )
        st.markdown(
            f'
'
            f'

'
            f'Month	Podcasts	EN	AR	'
            f'Old Cost	New Cost	Savings	'
            f'

'
            f'
{rows_html}
'
            f'
',
            unsafe_allow_html=True,
        )

st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 3 — Publication Trends
# ═══════════════════════════════════════════════════════════════════════════════
section_title(ICO_CHART, "Publication Trends")
tab_daily, tab_weekly, tab_monthly = st.tabs(["Daily", "Weekly", "Monthly"])

def make_bar(df_g, x, y, title, color="#00b4d8"):
    fig = px.bar(df_g, x=x, y=y, title=title, color_discrete_sequence=[color])
    fig.update_layout(
        paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
        font_color="white",
        xaxis=dict(showgrid=False, title=""),
        yaxis=dict(gridcolor="#1e293b"),
        margin=dict(t=40, b=10),
    )
    return fig

with tab_daily:
    if not filtered.empty and filtered["date"].notna().any():
        daily = filtered.groupby(filtered["date"].dt.date).size().reset_index(name="count")
        daily.columns = ["date", "count"]
        st.plotly_chart(make_bar(daily, "date", "count", "Press Releases per Day"), use_container_width=True)
    else:
        st.info("No data yet. Run the scraper first.")

with tab_weekly:
    if not filtered.empty and "week" in filtered.columns and filtered["date"].notna().any():
        weekly = filtered.groupby("week").size().reset_index(name="count")
        st.plotly_chart(make_bar(weekly, "week", "count", "Press Releases per Week", "#4cc9f0"), use_container_width=True)
    else:
        st.info("No data yet.")

with tab_monthly:
    if not filtered.empty and "month" in filtered.columns and filtered["date"].notna().any():
        monthly = filtered.groupby("month").size().reset_index(name="count")
        st.plotly_chart(make_bar(monthly, "month", "count", "Press Releases per Month", "#7209b7"), use_container_width=True)
    else:
        st.info("No data yet.")

st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 4 — Press Releases Table
# ═══════════════════════════════════════════════════════════════════════════════
section_title(ICO_LIST, "Press Releases")

search = st.text_input("Search press releases", placeholder="Filter by title…")
display = (
    filtered[filtered["title"].str.contains(search, case=False, na=False)]
    if search else filtered.copy()
)

total_pages = max(1, math.ceil(len(display) / PAGE_SIZE))
page_num    = st.number_input("Page", min_value=1, max_value=total_pages, value=1, step=1)
start       = (page_num - 1) * PAGE_SIZE
end         = start + PAGE_SIZE
page_data   = display.iloc[start:end]

st.caption(
    f"Showing {start+1}–{min(end, len(display))} of {len(display)} press releases "
    f"(page {page_num}/{total_pages})"
)

for _, row in page_data.iterrows():
    with st.container():
        tc, dc, pc = st.columns([5, 2, 1])
        with tc:
            url   = row.get("url", "")
            title = row.get("title", "—")
            st.markdown(f"**[{title}]({url})**" if url else f"**{title}**")
            desc = str(row.get("description") or "")
            if desc:
                st.caption(desc[:160] + "…" if len(desc) > 160 else desc)
        with dc:
            d = row.get("date")
            st.markdown(f"`{d.strftime('%d %b %Y') if pd.notna(d) else '—'}`")
        with pc:
            pc_count = row.get("podcast_count", 0)
            st.markdown(
                ICO_MIC_SM + f" {pc_count}" if pc_count else "—",
                unsafe_allow_html=True,
            )
    st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 5 — Podcasts
# ═══════════════════════════════════════════════════════════════════════════════
if not df_pod.empty:
    section_title(ICO_MIC, "Podcasts")

    lang_filter = st.radio("Language", ["All", "English", "Arabic"], horizontal=True)
    df_pod_show = df_pod.copy()
    if lang_filter == "English":
        df_pod_show = df_pod_show[df_pod_show["language"] == "en"]
    elif lang_filter == "Arabic":
        df_pod_show = df_pod_show[df_pod_show["language"] == "ar"]

    pod_search = st.text_input("Search podcasts", placeholder="Filter by title…")
    if pod_search:
        df_pod_show = df_pod_show[
            df_pod_show["title"].str.contains(pod_search, case=False, na=False)
        ]

    st.caption(f"Showing {len(df_pod_show)} of {len(df_pod)} episodes")

    for _, pod in df_pod_show.head(100).iterrows():
        with st.expander(f"{pod.get('title', '—')}  |  {pod.get('date', '')}"):
            col_a, col_b = st.columns([3, 1])
            with col_a:
                if pod.get("pr_title"):
                    st.markdown(ICO_LINK + f" **Linked PR:** {pod['pr_title']}", unsafe_allow_html=True)
                if pod.get("url"):
                    st.markdown(f"[Open on RSS.com]({pod['url']})")
                if pod.get("audio_url"):
                    st.audio(pod["audio_url"])
            with col_b:
                if pod.get("duration"):
                    st.metric("Duration", pod["duration"])
                st.metric("Saved vs old cost", f"AED {COST_PER_PODCAST_OLD_AED:,}")

    st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 6 — Scrape Log
# ═══════════════════════════════════════════════════════════════════════════════
with st.expander("Scrape history"):
    if scrape_log:
        df_log = pd.DataFrame(scrape_log)
        st.dataframe(
            df_log[["ran_at", "prs_found", "pods_found", "status", "notes"]],
            use_container_width=True,
        )
    else:
        st.info("No scrape runs recorded yet.")

st.markdown(
    '
'
    'DEWA PR Tracker · Playwright + SQLite + Streamlit
',
    unsafe_allow_html=True,
)

-    df_roi["old_cost"]   = df_roi["total"] * COST_PER_PODCAST_OLD_AED

-    df_roi["new_cost"]   = NOTEBOOKLM_AED   # subscription per month (fixed)

-    df_roi["savings"]    = df_roi["old_cost"] - df_roi["new_cost"]

+    # Cost basis: 1 podcast per press release @ AED 20,000

+    # (PR cost = 0 AED; only the podcast production was charged)

+    df_roi["old_cost"] = df_roi["pr_count"] * COST_PER_PODCAST_OLD_AED

+    df_roi["new_cost"] = NOTEBOOKLM_AED   # flat monthly subscription

+    df_roi["savings"]  = df_roi["old_cost"] - df_roi["new_cost"]

-    total_pods_nlm  = int(df_roi["total"].sum())

+    total_prs_nlm   = int(df_roi["pr_count"].sum())

+    total_eps_nlm   = int(df_roi["total_episodes"].sum())

-    total_pods_nlm = total_old_cost = total_new_cost = total_savings = roi_pct = 0

+    total_prs_nlm = total_eps_nlm = total_old_cost = total_new_cost = total_savings = roi_pct = 0


Now fix the summary cards and table to use `pr_count` instead of `total`:


with c1:
    st.metric("Total Press Releases", f"{len(filtered):,}")
with c2:
    st.metric("Podcasts (since May 2025)", f"{total_pods_nlm:,}")

with c1:
    st.metric("Total Press Releases", f"{len(filtered):,}")
with c2:
    st.metric("Podcasts (since May 2025)", f"{total_prs_nlm:,}")


with c1:
    st.metric("Total Press Releases", f"{len(filtered):,}")
with c2:
    st.metric("Podcasts (since May 2025)", f"{total_prs_nlm:,}")

"""
DEWA PR Tracker — Streamlit Dashboard
Run:  streamlit run dashboard.py
"""
import math
import re as _re
from datetime import datetime, timedelta

import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import streamlit as st

import database as db
from config import (
    COST_PER_PODCAST_OLD_AED,
    NOTEBOOKLM_MONTHLY_USD,
    NOTEBOOKLM_START_DATE,
    USD_TO_AED,
    DASHBOARD_TITLE,
    PAGE_SIZE,
)

# ── Page setup ────────────────────────────────────────────────────────────────
st.set_page_config(
    page_title=DASHBOARD_TITLE,
    page_icon="📰",
    layout="wide",
    initial_sidebar_state="expanded",
)

# ── SVG helpers ───────────────────────────────────────────────────────────────
def _svg(paths, size=20, color="#00b4d8"):
    return (
        f'<svg xmlns="http://www.w3.org/2000/svg" width="{size}" height="{size}" '
        f'viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2" '
        f'stroke-linecap="round" stroke-linejoin="round" '
        f'style="vertical-align:middle;margin-right:8px">{paths}</svg>'
    )

ICO_NEWS    = _svg('<path d="M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 0-2 2Zm0 0a2 2 0 0 1-2-2v-9c0-1.1.9-2 2-2h2"/><path d="M18 14h-8"/><path d="M15 18h-5"/><path d="M10 6h8v4h-8V6Z"/>')
ICO_MONEY   = _svg('<line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/>', color="#51cf66")
ICO_CHART   = _svg('<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/>', color="#4cc9f0")
ICO_LIST    = _svg('<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>')
ICO_MIC     = _svg('<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/>', color="#f59e0b")
ICO_MIC_SM  = _svg('<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/>', size=15, color="#f59e0b")
ICO_CLOCK   = _svg('<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>', color="#94a3b8")
ICO_LOG     = _svg('<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/>', color="#94a3b8")
ICO_LINK    = _svg('<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>', size=14, color="#00b4d8")

def section_title(icon, text):
    st.markdown(
        f'
{icon}{text}
',
        unsafe_allow_html=True,
    )

# ── CSS — hide Streamlit chrome (Deploy button, footer, menu) ─────────────────
st.markdown("""

""", unsafe_allow_html=True)

# ── Data loading (cached 5 min) ───────────────────────────────────────────────
@st.cache_data(ttl=300)
def load_data():
    db.init_db()
    prs      = [dict(r) for r in db.get_all_press_releases()]
    pods     = [dict(r) for r in db.get_all_podcasts()]
    roi_rows = [dict(r) for r in db.get_roi_monthly_since(NOTEBOOKLM_START_DATE)]
    stats    = db.get_stats()
    log      = [dict(r) for r in db.get_scrape_log()]
    return prs, pods, roi_rows, stats, log

prs, pods, roi_rows, stats, scrape_log = load_data()
df_pr  = pd.DataFrame(prs)
df_pod = pd.DataFrame(pods)

if not df_pod.empty and "title" in df_pod.columns:
    df_pod["language"] = df_pod["title"].apply(
        lambda t: "ar" if _re.search(r"[\u0600-\u06FF]", str(t)) else "en"
    )

if not df_pr.empty:
    df_pr["date"]  = pd.to_datetime(df_pr["date"], errors="coerce")
    df_pr["week"]  = df_pr["date"].dt.to_period("W").astype(str)
    df_pr["month"] = df_pr["date"].dt.to_period("M").astype(str)

if not df_pod.empty:
    df_pod["date"] = pd.to_datetime(df_pod["date"], errors="coerce")

# ── ROI pre-calculations ──────────────────────────────────────────────────────
NOTEBOOKLM_AED   = NOTEBOOKLM_MONTHLY_USD * USD_TO_AED   # ~73.4 AED/month
NLM_START        = pd.Timestamp(NOTEBOOKLM_START_DATE)

df_roi = pd.DataFrame(roi_rows) if roi_rows else pd.DataFrame(
    columns=["month", "total", "en_count", "ar_count"]
)

if not df_roi.empty:
    # Cost basis: 1 podcast per press release @ AED 20,000
    # (PR cost = 0 AED; only the podcast production was charged)
    df_roi["old_cost"] = df_roi["pr_count"] * COST_PER_PODCAST_OLD_AED
    df_roi["new_cost"] = NOTEBOOKLM_AED   # flat monthly subscription
    df_roi["savings"]  = df_roi["old_cost"] - df_roi["new_cost"]

    total_prs_nlm   = int(df_roi["pr_count"].sum())
    total_eps_nlm   = int(df_roi["total_episodes"].sum())
    total_old_cost  = float(df_roi["old_cost"].sum())
    total_new_cost  = float(df_roi["new_cost"].sum())
    total_savings   = float(df_roi["savings"].sum())
    roi_pct         = (total_savings / total_old_cost * 100) if total_old_cost > 0 else 0
else:
    total_prs_nlm = total_eps_nlm = total_old_cost = total_new_cost = total_savings = roi_pct = 0

# ── Sidebar ───────────────────────────────────────────────────────────────────
with st.sidebar:
    # DEWA logo — styled text (image blocked externally)
    st.markdown(
        '
DEWA
'
        '
Dubai Electricity & Water Authority
',
        unsafe_allow_html=True,
    )
    st.markdown("---")
    st.markdown("**Filters**")

    if not df_pr.empty and df_pr["date"].notna().any():
        min_date = df_pr["date"].min().date()
        max_date = df_pr["date"].max().date()
    else:
        min_date = datetime(2019, 1, 1).date()
        max_date = datetime.today().date()

    date_from, date_to = st.date_input(
        "Date range",
        value=(NLM_START.date(), max_date),
        min_value=min_date,
        max_value=max_date,
    )

    categories = ["All"]
    if not df_pr.empty and "category" in df_pr.columns:
        cats = df_pr["category"].dropna().unique().tolist()
        categories += sorted(cats)
    selected_cat = st.selectbox("Category", categories)

    has_podcast_filter = st.checkbox("Show only PRs with podcast", value=False)

    st.divider()
    if st.button("Refresh data", use_container_width=True):
        st.cache_data.clear()
        st.rerun()

    if st.button("Run scraper now", use_container_width=True):
        with st.spinner("Scraping DEWA website…"):
            try:
                from scraper import run_scrape
                prs_n, pods_n = run_scrape()
                st.success(f"Done — {prs_n} PRs, {pods_n} podcasts saved.")
                st.cache_data.clear()
                st.rerun()
            except Exception as e:
                st.error(f"Scraper error: {e}")

    st.divider()
    st.caption(f"Last scraped: {stats.get('last_scraped', 'Never') or 'Never'}")

# ── Apply PR filters ──────────────────────────────────────────────────────────
filtered = df_pr.copy()
if not filtered.empty:
    if filtered["date"].notna().any():
        filtered = filtered[
            (filtered["date"].dt.date >= date_from) &
            (filtered["date"].dt.date <= date_to)
        ]
    if selected_cat != "All":
        filtered = filtered[filtered["category"] == selected_cat]
    if has_podcast_filter:
        filtered = filtered[filtered["podcast_count"] > 0]

# ═══════════════════════════════════════════════════════════════════════════════
# HEADER
# ═══════════════════════════════════════════════════════════════════════════════
st.markdown(
    f'
'
    f'{_svg("""<path d="M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 0-2 2Zm0 0a2 2 0 0 1-2-2v-9c0-1.1.9-2 2-2h2"/><path d="M18 14h-8"/><path d="M15 18h-5"/><path d="M10 6h8v4h-8V6Z"/>""", size=34, color="#00b4d8")}'
    f'{DASHBOARD_TITLE}
',
    unsafe_allow_html=True,
)
st.markdown(
    f'
'
    f'Tracking all DEWA press releases and NotebookLM podcast ROI since {NOTEBOOKLM_START_DATE} — '
    f'{datetime.today().strftime("%d %B %Y")}
',
    unsafe_allow_html=True,
)
st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 1 — Key Metrics
# ═══════════════════════════════════════════════════════════════════════════════
today       = datetime.today().date()
week_start  = today - timedelta(days=today.weekday())
month_start = today.replace(day=1)

if not filtered.empty and filtered["date"].notna().any():
    prs_today = len(filtered[filtered["date"].dt.date == today])
    prs_week  = len(filtered[filtered["date"].dt.date >= week_start])
    prs_month = len(filtered[filtered["date"].dt.date >= month_start])
else:
    prs_today = prs_week = prs_month = 0

c1, c2, c3, c4, c5 = st.columns(5)
with c1:
    st.metric("Total Press Releases", f"{len(filtered):,}")
with c2:
    st.metric("Podcasts (since May 2025)", f"{total_pods_nlm:,}")
with c3:
    st.metric("Published Today", prs_today)
with c4:
    st.metric("This Week", prs_week)
with c5:
    st.metric("This Month", prs_month)

st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 2 — NotebookLM ROI  (auto-calculated from May 23, 2025)
# ═══════════════════════════════════════════════════════════════════════════════
section_title(ICO_MONEY, "NotebookLM ROI — Since 23 May 2025")

# Summary cards
sc1, sc2, sc3, sc4 = st.columns(4)
with sc1:
    st.markdown(
        f'
'
        f'
Old Cost (Traditional)
'
        f'
AED {total_old_cost:,.0f}
'
        f'
{total_pods_nlm} podcasts × AED 20,000
'
        f'
',
        unsafe_allow_html=True,
    )
with sc2:
    months_active = len(df_roi)
    st.markdown(
        f'
'
        f'
New Cost (NotebookLM)
'
        f'
AED {total_new_cost:,.0f}
'
        f'
{months_active} months × AED {NOTEBOOKLM_AED:.0f}/mo (${NOTEBOOKLM_MONTHLY_USD})
'
        f'
',
        unsafe_allow_html=True,
    )
with sc3:
    st.markdown(
        f'
'
        f'
Total Savings
'
        f'
AED {total_savings:,.0f}
'
        f'
Since {NOTEBOOKLM_START_DATE}
'
        f'
',
        unsafe_allow_html=True,
    )
with sc4:
    st.markdown(
        f'
'
        f'
Return on Investment
'
        f'
{roi_pct:.1f}%
'
        f'
Savings ÷ Old Cost
'
        f'
',
        unsafe_allow_html=True,
    )

st.markdown("
", unsafe_allow_html=True)

# ── Monthly detail ────────────────────────────────────────────────────────────
if not df_roi.empty:
    chart_col, table_col = st.columns([3, 2])

    with chart_col:
        # Grouped bar: old vs new cost per month
        df_chart = df_roi[["month", "old_cost", "new_cost"]].copy()
        df_chart_long = df_chart.melt(
            id_vars="month",
            value_vars=["old_cost", "new_cost"],
            var_name="Type",
            value_name="AED",
        )
        df_chart_long["Type"] = df_chart_long["Type"].map(
            {"old_cost": "Old Cost (Traditional)", "new_cost": "New Cost (NotebookLM)"}
        )
        fig_bar = px.bar(
            df_chart_long, x="month", y="AED", color="Type", barmode="group",
            color_discrete_map={
                "Old Cost (Traditional)": "#ff6b6b",
                "New Cost (NotebookLM)":  "#51cf66",
            },
            title="Monthly Cost Comparison: Traditional vs NotebookLM",
        )
        fig_bar.update_layout(
            paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
            font_color="white", legend_title_text="",
            xaxis=dict(showgrid=False, title=""),
            yaxis=dict(gridcolor="#1e293b", title="AED"),
            margin=dict(t=40, b=10), height=320,
        )
        st.plotly_chart(fig_bar, use_container_width=True)

        # Podcast count per month
        fig_count = px.bar(
            df_roi, x="month", y="total",
            color_discrete_sequence=["#f59e0b"],
            title="Podcasts Produced per Month (since NotebookLM)",
        )
        fig_count.update_layout(
            paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
            font_color="white",
            xaxis=dict(showgrid=False, title=""),
            yaxis=dict(gridcolor="#1e293b", title="Podcasts"),
            margin=dict(t=40, b=10), height=280,
        )
        st.plotly_chart(fig_count, use_container_width=True)

    with table_col:
        st.markdown(
            '
'
            'Monthly breakdown — each row = 1 calendar month
',
            unsafe_allow_html=True,
        )
        rows_html = ""
        for _, r in df_roi.iterrows():
            rows_html += (
                f'
'
                f'{r["month"]}	'
                f'{int(r["total"])}	'
                f'{int(r["en_count"])}	'
                f'{int(r["ar_count"])}	'
                f'AED {r["old_cost"]:,.0f}	'
                f'AED {r["new_cost"]:,.0f}	'
                f'AED {r["savings"]:,.0f}	'
                f'
'
            )
        # Total row
        rows_html += (
            f'
'
            f'TOTAL	'
            f'{total_pods_nlm}	'
            f'{int(df_roi["en_count"].sum())}	'
            f'{int(df_roi["ar_count"].sum())}	'
            f'AED {total_old_cost:,.0f}	'
            f'AED {total_new_cost:,.0f}	'
            f'AED {total_savings:,.0f}	'
            f'
'
        )
        st.markdown(
            f'
'
            f'

'
            f'Month	Podcasts	EN	AR	'
            f'Old Cost	New Cost	Savings	'
            f'

'
            f'
{rows_html}
'
            f'
',
            unsafe_allow_html=True,
        )

st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 3 — Publication Trends
# ═══════════════════════════════════════════════════════════════════════════════
section_title(ICO_CHART, "Publication Trends")
tab_daily, tab_weekly, tab_monthly = st.tabs(["Daily", "Weekly", "Monthly"])

def make_bar(df_g, x, y, title, color="#00b4d8"):
    fig = px.bar(df_g, x=x, y=y, title=title, color_discrete_sequence=[color])
    fig.update_layout(
        paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
        font_color="white",
        xaxis=dict(showgrid=False, title=""),
        yaxis=dict(gridcolor="#1e293b"),
        margin=dict(t=40, b=10),
    )
    return fig

with tab_daily:
    if not filtered.empty and filtered["date"].notna().any():
        daily = filtered.groupby(filtered["date"].dt.date).size().reset_index(name="count")
        daily.columns = ["date", "count"]
        st.plotly_chart(make_bar(daily, "date", "count", "Press Releases per Day"), use_container_width=True)
    else:
        st.info("No data yet. Run the scraper first.")

with tab_weekly:
    if not filtered.empty and "week" in filtered.columns and filtered["date"].notna().any():
        weekly = filtered.groupby("week").size().reset_index(name="count")
        st.plotly_chart(make_bar(weekly, "week", "count", "Press Releases per Week", "#4cc9f0"), use_container_width=True)
    else:
        st.info("No data yet.")

with tab_monthly:
    if not filtered.empty and "month" in filtered.columns and filtered["date"].notna().any():
        monthly = filtered.groupby("month").size().reset_index(name="count")
        st.plotly_chart(make_bar(monthly, "month", "count", "Press Releases per Month", "#7209b7"), use_container_width=True)
    else:
        st.info("No data yet.")

st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 4 — Press Releases Table
# ═══════════════════════════════════════════════════════════════════════════════
section_title(ICO_LIST, "Press Releases")

search = st.text_input("Search press releases", placeholder="Filter by title…")
display = (
    filtered[filtered["title"].str.contains(search, case=False, na=False)]
    if search else filtered.copy()
)

total_pages = max(1, math.ceil(len(display) / PAGE_SIZE))
page_num    = st.number_input("Page", min_value=1, max_value=total_pages, value=1, step=1)
start       = (page_num - 1) * PAGE_SIZE
end         = start + PAGE_SIZE
page_data   = display.iloc[start:end]

st.caption(
    f"Showing {start+1}–{min(end, len(display))} of {len(display)} press releases "
    f"(page {page_num}/{total_pages})"
)

for _, row in page_data.iterrows():
    with st.container():
        tc, dc, pc = st.columns([5, 2, 1])
        with tc:
            url   = row.get("url", "")
            title = row.get("title", "—")
            st.markdown(f"**[{title}]({url})**" if url else f"**{title}**")
            desc = str(row.get("description") or "")
            if desc:
                st.caption(desc[:160] + "…" if len(desc) > 160 else desc)
        with dc:
            d = row.get("date")
            st.markdown(f"`{d.strftime('%d %b %Y') if pd.notna(d) else '—'}`")
        with pc:
            pc_count = row.get("podcast_count", 0)
            st.markdown(
                ICO_MIC_SM + f" {pc_count}" if pc_count else "—",
                unsafe_allow_html=True,
            )
    st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 5 — Podcasts
# ═══════════════════════════════════════════════════════════════════════════════
if not df_pod.empty:
    section_title(ICO_MIC, "Podcasts")

    lang_filter = st.radio("Language", ["All", "English", "Arabic"], horizontal=True)
    df_pod_show = df_pod.copy()
    if lang_filter == "English":
        df_pod_show = df_pod_show[df_pod_show["language"] == "en"]
    elif lang_filter == "Arabic":
        df_pod_show = df_pod_show[df_pod_show["language"] == "ar"]

    pod_search = st.text_input("Search podcasts", placeholder="Filter by title…")
    if pod_search:
        df_pod_show = df_pod_show[
            df_pod_show["title"].str.contains(pod_search, case=False, na=False)
        ]

    st.caption(f"Showing {len(df_pod_show)} of {len(df_pod)} episodes")

    for _, pod in df_pod_show.head(100).iterrows():
        with st.expander(f"{pod.get('title', '—')}  |  {pod.get('date', '')}"):
            col_a, col_b = st.columns([3, 1])
            with col_a:
                if pod.get("pr_title"):
                    st.markdown(ICO_LINK + f" **Linked PR:** {pod['pr_title']}", unsafe_allow_html=True)
                if pod.get("url"):
                    st.markdown(f"[Open on RSS.com]({pod['url']})")
                if pod.get("audio_url"):
                    st.audio(pod["audio_url"])
            with col_b:
                if pod.get("duration"):
                    st.metric("Duration", pod["duration"])
                st.metric("Saved vs old cost", f"AED {COST_PER_PODCAST_OLD_AED:,}")

    st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 6 — Scrape Log
# ═══════════════════════════════════════════════════════════════════════════════
with st.expander("Scrape history"):
    if scrape_log:
        df_log = pd.DataFrame(scrape_log)
        st.dataframe(
            df_log[["ran_at", "prs_found", "pods_found", "status", "notes"]],
            use_container_width=True,
        )
    else:
        st.info("No scrape runs recorded yet.")

st.markdown(
    '
'
    'DEWA PR Tracker · Playwright + SQLite + Streamlit
',
    unsafe_allow_html=True,
)

-    st.metric("Podcasts (since May 2025)", f"{total_pods_nlm:,}")

+    st.metric("Podcasts (since May 2025)", f"{total_prs_nlm:,}")


f'
{total_pods_nlm} podcasts × AED 20,000
'

f'
{total_prs_nlm} PRs × AED 20,000/podcast
'


"""
DEWA PR Tracker — Streamlit Dashboard
Run:  streamlit run dashboard.py
"""
import math
import re as _re
from datetime import datetime, timedelta

import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import streamlit as st

import database as db
from config import (
    COST_PER_PODCAST_OLD_AED,
    NOTEBOOKLM_MONTHLY_USD,
    NOTEBOOKLM_START_DATE,
    USD_TO_AED,
    DASHBOARD_TITLE,
    PAGE_SIZE,
)

# ── Page setup ────────────────────────────────────────────────────────────────
st.set_page_config(
    page_title=DASHBOARD_TITLE,
    page_icon="📰",
    layout="wide",
    initial_sidebar_state="expanded",
)

# ── SVG helpers ───────────────────────────────────────────────────────────────
def _svg(paths, size=20, color="#00b4d8"):
    return (
        f'<svg xmlns="http://www.w3.org/2000/svg" width="{size}" height="{size}" '
        f'viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2" '
        f'stroke-linecap="round" stroke-linejoin="round" '
        f'style="vertical-align:middle;margin-right:8px">{paths}</svg>'
    )

ICO_NEWS    = _svg('<path d="M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 0-2 2Zm0 0a2 2 0 0 1-2-2v-9c0-1.1.9-2 2-2h2"/><path d="M18 14h-8"/><path d="M15 18h-5"/><path d="M10 6h8v4h-8V6Z"/>')
ICO_MONEY   = _svg('<line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/>', color="#51cf66")
ICO_CHART   = _svg('<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/>', color="#4cc9f0")
ICO_LIST    = _svg('<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>')
ICO_MIC     = _svg('<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/>', color="#f59e0b")
ICO_MIC_SM  = _svg('<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/>', size=15, color="#f59e0b")
ICO_CLOCK   = _svg('<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>', color="#94a3b8")
ICO_LOG     = _svg('<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/>', color="#94a3b8")
ICO_LINK    = _svg('<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>', size=14, color="#00b4d8")

def section_title(icon, text):
    st.markdown(
        f'
{icon}{text}
',
        unsafe_allow_html=True,
    )

# ── CSS — hide Streamlit chrome (Deploy button, footer, menu) ─────────────────
st.markdown("""

""", unsafe_allow_html=True)

# ── Data loading (cached 5 min) ───────────────────────────────────────────────
@st.cache_data(ttl=300)
def load_data():
    db.init_db()
    prs      = [dict(r) for r in db.get_all_press_releases()]
    pods     = [dict(r) for r in db.get_all_podcasts()]
    roi_rows = [dict(r) for r in db.get_roi_monthly_since(NOTEBOOKLM_START_DATE)]
    stats    = db.get_stats()
    log      = [dict(r) for r in db.get_scrape_log()]
    return prs, pods, roi_rows, stats, log

prs, pods, roi_rows, stats, scrape_log = load_data()
df_pr  = pd.DataFrame(prs)
df_pod = pd.DataFrame(pods)

if not df_pod.empty and "title" in df_pod.columns:
    df_pod["language"] = df_pod["title"].apply(
        lambda t: "ar" if _re.search(r"[\u0600-\u06FF]", str(t)) else "en"
    )

if not df_pr.empty:
    df_pr["date"]  = pd.to_datetime(df_pr["date"], errors="coerce")
    df_pr["week"]  = df_pr["date"].dt.to_period("W").astype(str)
    df_pr["month"] = df_pr["date"].dt.to_period("M").astype(str)

if not df_pod.empty:
    df_pod["date"] = pd.to_datetime(df_pod["date"], errors="coerce")

# ── ROI pre-calculations ──────────────────────────────────────────────────────
NOTEBOOKLM_AED   = NOTEBOOKLM_MONTHLY_USD * USD_TO_AED   # ~73.4 AED/month
NLM_START        = pd.Timestamp(NOTEBOOKLM_START_DATE)

df_roi = pd.DataFrame(roi_rows) if roi_rows else pd.DataFrame(
    columns=["month", "total", "en_count", "ar_count"]
)

if not df_roi.empty:
    # Cost basis: 1 podcast per press release @ AED 20,000
    # (PR cost = 0 AED; only the podcast production was charged)
    df_roi["old_cost"] = df_roi["pr_count"] * COST_PER_PODCAST_OLD_AED
    df_roi["new_cost"] = NOTEBOOKLM_AED   # flat monthly subscription
    df_roi["savings"]  = df_roi["old_cost"] - df_roi["new_cost"]

    total_prs_nlm   = int(df_roi["pr_count"].sum())
    total_eps_nlm   = int(df_roi["total_episodes"].sum())
    total_old_cost  = float(df_roi["old_cost"].sum())
    total_new_cost  = float(df_roi["new_cost"].sum())
    total_savings   = float(df_roi["savings"].sum())
    roi_pct         = (total_savings / total_old_cost * 100) if total_old_cost > 0 else 0
else:
    total_prs_nlm = total_eps_nlm = total_old_cost = total_new_cost = total_savings = roi_pct = 0

# ── Sidebar ───────────────────────────────────────────────────────────────────
with st.sidebar:
    # DEWA logo — styled text (image blocked externally)
    st.markdown(
        '
DEWA
'
        '
Dubai Electricity & Water Authority
',
        unsafe_allow_html=True,
    )
    st.markdown("---")
    st.markdown("**Filters**")

    if not df_pr.empty and df_pr["date"].notna().any():
        min_date = df_pr["date"].min().date()
        max_date = df_pr["date"].max().date()
    else:
        min_date = datetime(2019, 1, 1).date()
        max_date = datetime.today().date()

    date_from, date_to = st.date_input(
        "Date range",
        value=(NLM_START.date(), max_date),
        min_value=min_date,
        max_value=max_date,
    )

    categories = ["All"]
    if not df_pr.empty and "category" in df_pr.columns:
        cats = df_pr["category"].dropna().unique().tolist()
        categories += sorted(cats)
    selected_cat = st.selectbox("Category", categories)

    has_podcast_filter = st.checkbox("Show only PRs with podcast", value=False)

    st.divider()
    if st.button("Refresh data", use_container_width=True):
        st.cache_data.clear()
        st.rerun()

    if st.button("Run scraper now", use_container_width=True):
        with st.spinner("Scraping DEWA website…"):
            try:
                from scraper import run_scrape
                prs_n, pods_n = run_scrape()
                st.success(f"Done — {prs_n} PRs, {pods_n} podcasts saved.")
                st.cache_data.clear()
                st.rerun()
            except Exception as e:
                st.error(f"Scraper error: {e}")

    st.divider()
    st.caption(f"Last scraped: {stats.get('last_scraped', 'Never') or 'Never'}")

# ── Apply PR filters ──────────────────────────────────────────────────────────
filtered = df_pr.copy()
if not filtered.empty:
    if filtered["date"].notna().any():
        filtered = filtered[
            (filtered["date"].dt.date >= date_from) &
            (filtered["date"].dt.date <= date_to)
        ]
    if selected_cat != "All":
        filtered = filtered[filtered["category"] == selected_cat]
    if has_podcast_filter:
        filtered = filtered[filtered["podcast_count"] > 0]

# ═══════════════════════════════════════════════════════════════════════════════
# HEADER
# ═══════════════════════════════════════════════════════════════════════════════
st.markdown(
    f'
'
    f'{_svg("""<path d="M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 0-2 2Zm0 0a2 2 0 0 1-2-2v-9c0-1.1.9-2 2-2h2"/><path d="M18 14h-8"/><path d="M15 18h-5"/><path d="M10 6h8v4h-8V6Z"/>""", size=34, color="#00b4d8")}'
    f'{DASHBOARD_TITLE}
',
    unsafe_allow_html=True,
)
st.markdown(
    f'
'
    f'Tracking all DEWA press releases and NotebookLM podcast ROI since {NOTEBOOKLM_START_DATE} — '
    f'{datetime.today().strftime("%d %B %Y")}
',
    unsafe_allow_html=True,
)
st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 1 — Key Metrics
# ═══════════════════════════════════════════════════════════════════════════════
today       = datetime.today().date()
week_start  = today - timedelta(days=today.weekday())
month_start = today.replace(day=1)

if not filtered.empty and filtered["date"].notna().any():
    prs_today = len(filtered[filtered["date"].dt.date == today])
    prs_week  = len(filtered[filtered["date"].dt.date >= week_start])
    prs_month = len(filtered[filtered["date"].dt.date >= month_start])
else:
    prs_today = prs_week = prs_month = 0

c1, c2, c3, c4, c5 = st.columns(5)
with c1:
    st.metric("Total Press Releases", f"{len(filtered):,}")
with c2:
    st.metric("Podcasts (since May 2025)", f"{total_prs_nlm:,}")
with c3:
    st.metric("Published Today", prs_today)
with c4:
    st.metric("This Week", prs_week)
with c5:
    st.metric("This Month", prs_month)

st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 2 — NotebookLM ROI  (auto-calculated from May 23, 2025)
# ═══════════════════════════════════════════════════════════════════════════════
section_title(ICO_MONEY, "NotebookLM ROI — Since 23 May 2025")

# Summary cards
sc1, sc2, sc3, sc4 = st.columns(4)
with sc1:
    st.markdown(
        f'
'
        f'
Old Cost (Traditional)
'
        f'
AED {total_old_cost:,.0f}
'
        f'
{total_pods_nlm} podcasts × AED 20,000
'
        f'
',
        unsafe_allow_html=True,
    )
with sc2:
    months_active = len(df_roi)
    st.markdown(
        f'
'
        f'
New Cost (NotebookLM)
'
        f'
AED {total_new_cost:,.0f}
'
        f'
{months_active} months × AED {NOTEBOOKLM_AED:.0f}/mo (${NOTEBOOKLM_MONTHLY_USD})
'
        f'
',
        unsafe_allow_html=True,
    )
with sc3:
    st.markdown(
        f'
'
        f'
Total Savings
'
        f'
AED {total_savings:,.0f}
'
        f'
Since {NOTEBOOKLM_START_DATE}
'
        f'
',
        unsafe_allow_html=True,
    )
with sc4:
    st.markdown(
        f'
'
        f'
Return on Investment
'
        f'
{roi_pct:.1f}%
'
        f'
Savings ÷ Old Cost
'
        f'
',
        unsafe_allow_html=True,
    )

st.markdown("
", unsafe_allow_html=True)

# ── Monthly detail ────────────────────────────────────────────────────────────
if not df_roi.empty:
    chart_col, table_col = st.columns([3, 2])

    with chart_col:
        # Grouped bar: old vs new cost per month
        df_chart = df_roi[["month", "old_cost", "new_cost"]].copy()
        df_chart_long = df_chart.melt(
            id_vars="month",
            value_vars=["old_cost", "new_cost"],
            var_name="Type",
            value_name="AED",
        )
        df_chart_long["Type"] = df_chart_long["Type"].map(
            {"old_cost": "Old Cost (Traditional)", "new_cost": "New Cost (NotebookLM)"}
        )
        fig_bar = px.bar(
            df_chart_long, x="month", y="AED", color="Type", barmode="group",
            color_discrete_map={
                "Old Cost (Traditional)": "#ff6b6b",
                "New Cost (NotebookLM)":  "#51cf66",
            },
            title="Monthly Cost Comparison: Traditional vs NotebookLM",
        )
        fig_bar.update_layout(
            paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
            font_color="white", legend_title_text="",
            xaxis=dict(showgrid=False, title=""),
            yaxis=dict(gridcolor="#1e293b", title="AED"),
            margin=dict(t=40, b=10), height=320,
        )
        st.plotly_chart(fig_bar, use_container_width=True)

        # Podcast count per month
        fig_count = px.bar(
            df_roi, x="month", y="total",
            color_discrete_sequence=["#f59e0b"],
            title="Podcasts Produced per Month (since NotebookLM)",
        )
        fig_count.update_layout(
            paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
            font_color="white",
            xaxis=dict(showgrid=False, title=""),
            yaxis=dict(gridcolor="#1e293b", title="Podcasts"),
            margin=dict(t=40, b=10), height=280,
        )
        st.plotly_chart(fig_count, use_container_width=True)

    with table_col:
        st.markdown(
            '
'
            'Monthly breakdown — each row = 1 calendar month
',
            unsafe_allow_html=True,
        )
        rows_html = ""
        for _, r in df_roi.iterrows():
            rows_html += (
                f'
'
                f'{r["month"]}	'
                f'{int(r["total"])}	'
                f'{int(r["en_count"])}	'
                f'{int(r["ar_count"])}	'
                f'AED {r["old_cost"]:,.0f}	'
                f'AED {r["new_cost"]:,.0f}	'
                f'AED {r["savings"]:,.0f}	'
                f'
'
            )
        # Total row
        rows_html += (
            f'
'
            f'TOTAL	'
            f'{total_pods_nlm}	'
            f'{int(df_roi["en_count"].sum())}	'
            f'{int(df_roi["ar_count"].sum())}	'
            f'AED {total_old_cost:,.0f}	'
            f'AED {total_new_cost:,.0f}	'
            f'AED {total_savings:,.0f}	'
            f'
'
        )
        st.markdown(
            f'
'
            f'

'
            f'Month	Podcasts	EN	AR	'
            f'Old Cost	New Cost	Savings	'
            f'

'
            f'
{rows_html}
'
            f'
',
            unsafe_allow_html=True,
        )

st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 3 — Publication Trends
# ═══════════════════════════════════════════════════════════════════════════════
section_title(ICO_CHART, "Publication Trends")
tab_daily, tab_weekly, tab_monthly = st.tabs(["Daily", "Weekly", "Monthly"])

def make_bar(df_g, x, y, title, color="#00b4d8"):
    fig = px.bar(df_g, x=x, y=y, title=title, color_discrete_sequence=[color])
    fig.update_layout(
        paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
        font_color="white",
        xaxis=dict(showgrid=False, title=""),
        yaxis=dict(gridcolor="#1e293b"),
        margin=dict(t=40, b=10),
    )
    return fig

with tab_daily:
    if not filtered.empty and filtered["date"].notna().any():
        daily = filtered.groupby(filtered["date"].dt.date).size().reset_index(name="count")
        daily.columns = ["date", "count"]
        st.plotly_chart(make_bar(daily, "date", "count", "Press Releases per Day"), use_container_width=True)
    else:
        st.info("No data yet. Run the scraper first.")

with tab_weekly:
    if not filtered.empty and "week" in filtered.columns and filtered["date"].notna().any():
        weekly = filtered.groupby("week").size().reset_index(name="count")
        st.plotly_chart(make_bar(weekly, "week", "count", "Press Releases per Week", "#4cc9f0"), use_container_width=True)
    else:
        st.info("No data yet.")

with tab_monthly:
    if not filtered.empty and "month" in filtered.columns and filtered["date"].notna().any():
        monthly = filtered.groupby("month").size().reset_index(name="count")
        st.plotly_chart(make_bar(monthly, "month", "count", "Press Releases per Month", "#7209b7"), use_container_width=True)
    else:
        st.info("No data yet.")

st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 4 — Press Releases Table
# ═══════════════════════════════════════════════════════════════════════════════
section_title(ICO_LIST, "Press Releases")

search = st.text_input("Search press releases", placeholder="Filter by title…")
display = (
    filtered[filtered["title"].str.contains(search, case=False, na=False)]
    if search else filtered.copy()
)

total_pages = max(1, math.ceil(len(display) / PAGE_SIZE))
page_num    = st.number_input("Page", min_value=1, max_value=total_pages, value=1, step=1)
start       = (page_num - 1) * PAGE_SIZE
end         = start + PAGE_SIZE
page_data   = display.iloc[start:end]

st.caption(
    f"Showing {start+1}–{min(end, len(display))} of {len(display)} press releases "
    f"(page {page_num}/{total_pages})"
)

for _, row in page_data.iterrows():
    with st.container():
        tc, dc, pc = st.columns([5, 2, 1])
        with tc:
            url   = row.get("url", "")
            title = row.get("title", "—")
            st.markdown(f"**[{title}]({url})**" if url else f"**{title}**")
            desc = str(row.get("description") or "")
            if desc:
                st.caption(desc[:160] + "…" if len(desc) > 160 else desc)
        with dc:
            d = row.get("date")
            st.markdown(f"`{d.strftime('%d %b %Y') if pd.notna(d) else '—'}`")
        with pc:
            pc_count = row.get("podcast_count", 0)
            st.markdown(
                ICO_MIC_SM + f" {pc_count}" if pc_count else "—",
                unsafe_allow_html=True,
            )
    st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 5 — Podcasts
# ═══════════════════════════════════════════════════════════════════════════════
if not df_pod.empty:
    section_title(ICO_MIC, "Podcasts")

    lang_filter = st.radio("Language", ["All", "English", "Arabic"], horizontal=True)
    df_pod_show = df_pod.copy()
    if lang_filter == "English":
        df_pod_show = df_pod_show[df_pod_show["language"] == "en"]
    elif lang_filter == "Arabic":
        df_pod_show = df_pod_show[df_pod_show["language"] == "ar"]

    pod_search = st.text_input("Search podcasts", placeholder="Filter by title…")
    if pod_search:
        df_pod_show = df_pod_show[
            df_pod_show["title"].str.contains(pod_search, case=False, na=False)
        ]

    st.caption(f"Showing {len(df_pod_show)} of {len(df_pod)} episodes")

    for _, pod in df_pod_show.head(100).iterrows():
        with st.expander(f"{pod.get('title', '—')}  |  {pod.get('date', '')}"):
            col_a, col_b = st.columns([3, 1])
            with col_a:
                if pod.get("pr_title"):
                    st.markdown(ICO_LINK + f" **Linked PR:** {pod['pr_title']}", unsafe_allow_html=True)
                if pod.get("url"):
                    st.markdown(f"[Open on RSS.com]({pod['url']})")
                if pod.get("audio_url"):
                    st.audio(pod["audio_url"])
            with col_b:
                if pod.get("duration"):
                    st.metric("Duration", pod["duration"])
                st.metric("Saved vs old cost", f"AED {COST_PER_PODCAST_OLD_AED:,}")

    st.divider()

# ═══════════════════════════════════════════════════════════════════════════════
# ROW 6 — Scrape Log
# ═══════════════════════════════════════════════════════════════════════════════
with st.expander("Scrape history"):
    if scrape_log:
        df_log = pd.DataFrame(scrape_log)
        st.dataframe(
            df_log[["ran_at", "prs_found", "pods_found", "status", "notes"]],
            use_container_width=True,
        )
    else:
        st.info("No scrape runs recorded yet.")

st.markdown(
    '
'
    'DEWA PR Tracker · Playwright + SQLite + Streamlit
',
    unsafe_allow_html=True,
)

f'<div style="background:#1e293b;border-radius:10px;padding:18px 20px;border-left:4px solid #ff6b6b">'

-        f'
{total_pods_nlm} podcasts × AED 20,000
'

+        f'
{total_prs_nlm} PRs × AED 20,000/podcast
'


with chart_col:
        # Grouped bar: old vs new cost per month
        df_chart = df_roi[["month", "old_cost", "new_cost"]].copy()
        df_chart_long = df_chart.melt(
            id_vars="month",
            value_vars=["old_cost", "new_cost"],
            var_name="Type",
            value_name="AED",
        )
        df_chart_long["Type"] = df_chart_long["Type"].map(
            {"old_cost": "Old Cost (Traditional)", "new_cost": "New Cost (NotebookLM)"}
        )
        fig_bar = px.bar(
            df_chart_long, x="month", y="AED", color="Type", barmode="group",
            color_discrete_map={
                "Old Cost (Traditional)": "#ff6b6b",
                "New Cost (NotebookLM)":  "#51cf66",
            },
            title="Monthly Cost Comparison: Traditional vs NotebookLM",
        )
        fig_bar.update_layout(
            paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
            font_color="white", legend_title_text="",
            xaxis=dict(showgrid=False, title=""),
            yaxis=dict(gridcolor="#1e293b", title="AED"),
            margin=dict(t=40, b=10), height=320,
        )
        st.plotly_chart(fig_bar, use_container_width=True)

        # Podcast count per month
        fig_count = px.bar(
            df_roi, x="month", y="total",
            color_discrete_sequence=["#f59e0b"],
            title="Podcasts Produced per Month (since NotebookLM)",
        )
        fig_count.update_layout(
            paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
            font_color="white",
            xaxis=dict(showgrid=False, title=""),
            yaxis=dict(gridcolor="#1e293b", title="Podcasts"),
            margin=dict(t=40, b=10), height=280,
        )
        st.plotly_chart(fig_count, use_container_width=True)

    with table_col:
        st.markdown(
            '
'
            'Monthly breakdown — each row = 1 calendar month
',
            unsafe_allow_html=True,
        )
        rows_html = ""
        for _, r in df_roi.iterrows():
            rows_html += (
                f'
'
                f'{r["month"]}	'
                f'{int(r["total"])}	'
                f'{int(r["en_count"])}	'
                f'{int(r["ar_count"])}	'
                f'AED {r["old_cost"]:,.0f}	'
                f'AED {r["new_cost"]:,.0f}	'
                f'AED {r["savings"]:,.0f}	'
                f'
'
            )
        # Total row
        rows_html += (
            f'
'
            f'TOTAL	'
            f'{total_pods_nlm}	'
            f'{int(df_roi["en_count"].sum())}	'
            f'{int(df_roi["ar_count"].sum())}	'
            f'AED {total_old_cost:,.0f}	'
            f'AED {total_new_cost:,.0f}	'
            f'AED {total_savings:,.0f}	'
            f'
'
        )
        st.markdown(
            f'
'
            f'

'
            f'Month	Podcasts	EN	AR	'
            f'Old Cost	New Cost	Savings	'
            f'

'
            f'
{rows_html}
'
            f'
',
            unsafe_allow_html=True,
        )

with chart_col:
        # Grouped bar: old vs new cost per month
        df_chart = df_roi[["month", "old_cost", "new_cost"]].copy()
        df_chart_long = df_chart.melt(
            id_vars="month",
            value_vars=["old_cost", "new_cost"],
            var_name="Type", value_name="AED",
        )
        df_chart_long["Type"] = df_chart_long["Type"].map(
            {"old_cost": "Traditional (AED 20k/podcast)", "new_cost": "NotebookLM ($20/month)"}
        )
        fig_bar = px.bar(
            df_chart_long, x="month", y="AED", color="Type", barmode="group",
            color_discrete_map={
                "Traditional (AED 20k/podcast)": "#ff6b6b",
                "NotebookLM ($20/month)":         "#51cf66",
            },
            title="Monthly Cost: Traditional Podcast Production vs NotebookLM",
        )
        fig_bar.update_layout(
            paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
            font_color="white", legend_title_text="",
            xaxis=dict(showgrid=False, title=""),
            yaxis=dict(gridcolor="#1e293b", title="AED"),
            margin=dict(t=40, b=10), height=320,
        )
        st.plotly_chart(fig_bar, use_container_width=True)

        # PRs with podcasts per month
        fig_count = px.bar(
            df_roi, x="month", y="pr_count",
            color_discrete_sequence=["#f59e0b"],
            title="Press Releases with Podcasts per Month (since NotebookLM)",
        )
        fig_count.update_layout(
            paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
            font_color="white",
            xaxis=dict(showgrid=False, title=""),
            yaxis=dict(gridcolor="#1e293b", title="Press Releases"),
            margin=dict(t=40, b=10), height=280,
        )
        st.plotly_chart(fig_count, use_container_width=True)

    with table_col:
        st.markdown(
            '
'
            'Cost basis: AED 20,000 per PR podcast (EN+AR counted as one production).
'
            'NotebookLM: flat $20/month regardless of volume.
',
            unsafe_allow_html=True,
        )
        rows_html = ""
        for _, r in df_roi.iterrows():
            rows_html += (
                f'
'
                f'{r["month"]}	'
                f'{int(r["pr_count"])}	'
                f'{int(r["en_episodes"])}	'
                f'{int(r["ar_episodes"])}	'
                f'AED {r["old_cost"]:,.0f}	'
                f'AED {r["new_cost"]:,.0f}	'
                f'AED {r["savings"]:,.0f}	'
                f'
'
            )
        rows_html += (
            f'
'
            f'TOTAL	'
            f'{total_prs_nlm}	'
            f'{int(df_roi["en_episodes"].sum())}	'
            f'{int(df_roi["ar_episodes"].sum())}	'
            f'AED {total_old_cost:,.0f}	'
            f'AED {total_new_cost:,.0f}	'
            f'AED {total_savings:,.0f}	'
            f'
'
        )
        st.markdown(
            f'
'
            f'

'
            f'Month	PRs	EN eps	AR eps	'
            f'Old Cost	New Cost	Savings	'
            f'

'
            f'
{rows_html}
'
            f'
',
            unsafe_allow_html=True,
        )