"""Iteration 6 enhancements: active session time, LLM-powered session narratives, prompt effectiveness.

Key additions:
1. Active session time (sum of inter-message gaps < 30 min) — fixes misleading wall-clock durations
2. LLM-powered session arc narratives for top expensive sessions
3. Prompt effectiveness scoring per developer
4. Session clusters (time-of-day patterns)
"""
import json
import os
import sys
import hashlib
import re
from pathlib import Path

import numpy as np
import pandas as pd

sys.path.insert(0, "/home/sagar/trace/analysis-14022026")
from utils.connection import init, query_df

OUTPUT_DIR = Path("/home/sagar/trace/analysis-14022026/output")
DASHBOARD_DIR = Path("/home/sagar/trace/analysis-14022026/dashboard/public/data")
CACHE_DIR = Path("/home/sagar/trace/analysis-14022026/output/.llm_cache")
CACHE_DIR.mkdir(parents=True, exist_ok=True)

ACTIVE_GAP_THRESHOLD_MIN = 30  # gaps > 30 min = idle/abandoned

def load(name):
    with open(OUTPUT_DIR / name) as f:
        return json.load(f)

def save(name, data):
    def sanitize(obj):
        if isinstance(obj, float):
            if np.isnan(obj) or np.isinf(obj):
                return None
            return obj
        if isinstance(obj, dict):
            return {k: sanitize(v) for k, v in obj.items()}
        if isinstance(obj, list):
            return [sanitize(v) for v in obj]
        if isinstance(obj, (np.integer,)):
            return int(obj)
        if isinstance(obj, (np.floating,)):
            v = float(obj)
            return None if np.isnan(v) or np.isinf(v) else v
        return obj

    data = sanitize(data)
    text = json.dumps(data, indent=2, default=str)
    for p in [OUTPUT_DIR / name, DASHBOARD_DIR / name]:
        p.parent.mkdir(parents=True, exist_ok=True)
        p.write_text(text)
    print(f"  wrote {name}")

conn = init()

SESSION_FILTER = """
    SELECT id FROM sessions
    WHERE source NOT IN ('test', 'qc_trace_install')
    AND user_email IS NOT NULL
    AND org ILIKE 'pratilipi%%'
"""

# ---------------------------------------------------------------------------
# 1. ACTIVE SESSION TIME — sum of inter-message gaps < 30 min
# ---------------------------------------------------------------------------
print("Computing active session time...")

msg_times = query_df(conn, f"""
    SELECT m.session_id, m.timestamp, s.user_email, s.first_seen, s.last_updated
    FROM messages m
    JOIN sessions s ON s.id = m.session_id
    WHERE m.session_id IN ({SESSION_FILTER})
    ORDER BY m.session_id, m.timestamp
""")

msg_times["timestamp"] = pd.to_datetime(msg_times["timestamp"], utc=True)
msg_times["first_seen"] = pd.to_datetime(msg_times["first_seen"], utc=True)
msg_times["last_updated"] = pd.to_datetime(msg_times["last_updated"], utc=True)

active_times = []
for session_id, group in msg_times.groupby("session_id"):
    ts = group["timestamp"].sort_values().values
    email = group["user_email"].iloc[0]
    # Wall time from message timestamps (not session metadata) to avoid inflated durations
    if len(ts) >= 2:
        wall_clock_min = (ts[-1] - ts[0]) / np.timedelta64(1, "s") / 60
    else:
        wall_clock_min = 0.0

    if len(ts) < 2:
        active_times.append({
            "session_id": session_id,
            "user_email": email,
            "wall_clock_min": round(wall_clock_min, 1),
            "active_min": 0.0,
            "idle_min": round(wall_clock_min, 1),
            "num_gaps_over_threshold": 0,
            "longest_gap_min": 0.0,
            "msg_count": len(ts),
        })
        continue

    gaps_sec = np.diff(ts) / np.timedelta64(1, "s")
    active_gaps = gaps_sec[gaps_sec < ACTIVE_GAP_THRESHOLD_MIN * 60]
    idle_gaps = gaps_sec[gaps_sec >= ACTIVE_GAP_THRESHOLD_MIN * 60]

    active_min = active_gaps.sum() / 60 if len(active_gaps) > 0 else 0
    idle_min = idle_gaps.sum() / 60 if len(idle_gaps) > 0 else 0

    active_times.append({
        "session_id": session_id,
        "user_email": email,
        "wall_clock_min": round(wall_clock_min, 1),
        "active_min": round(active_min, 1),
        "idle_min": round(idle_min, 1),
        "num_gaps_over_threshold": int(len(idle_gaps)),
        "longest_gap_min": round(max(gaps_sec) / 60, 1) if len(gaps_sec) > 0 else 0,
        "msg_count": len(ts),
    })

active_df = pd.DataFrame(active_times)

# Update session_metrics.json with active_min
print("Updating session_metrics with active time...")
session_metrics = load("session_metrics.json")
active_lookup = active_df.set_index("session_id")[["active_min", "idle_min", "num_gaps_over_threshold", "longest_gap_min"]].to_dict("index")

for s in session_metrics["sessions"]:
    sid = s["session_id"]
    if sid in active_lookup:
        s["active_min"] = active_lookup[sid]["active_min"]
        s["idle_min"] = active_lookup[sid]["idle_min"]
        s["idle_gaps"] = active_lookup[sid]["num_gaps_over_threshold"]
        s["longest_gap_min"] = active_lookup[sid]["longest_gap_min"]
    else:
        s["active_min"] = 0
        s["idle_min"] = 0
        s["idle_gaps"] = 0
        s["longest_gap_min"] = 0

save("session_metrics.json", session_metrics)

# Aggregate active time stats
total_wall = active_df["wall_clock_min"].sum()
total_active = active_df["active_min"].sum()
total_idle = active_df["idle_min"].sum()
sessions_with_idle = (active_df["num_gaps_over_threshold"] > 0).sum()

# Active time per developer
dev_active = active_df.groupby("user_email").agg(
    total_active_min=("active_min", "sum"),
    total_wall_min=("wall_clock_min", "sum"),
    total_idle_min=("idle_min", "sum"),
    sessions_with_idle=("num_gaps_over_threshold", lambda x: (x > 0).sum()),
    median_active_min=("active_min", "median"),
).reset_index()

active_time_data = {
    "headline": f"Only {round(total_active / 60, 1)}h of {round(total_wall / 60, 1)}h wall-clock time was active ({round(total_active / max(total_wall, 1) * 100, 1)}%)",
    "total_active_hours": round(total_active / 60, 1),
    "total_wall_hours": round(total_wall / 60, 1),
    "total_idle_hours": round(total_idle / 60, 1),
    "active_pct": round(total_active / max(total_wall, 1) * 100, 1),
    "sessions_with_idle_gaps": int(sessions_with_idle),
    "caveat": f"Active time = sum of inter-message gaps under {ACTIVE_GAP_THRESHOLD_MIN} min. Gaps over {ACTIVE_GAP_THRESHOLD_MIN} min are treated as idle/abandoned periods.",
    "by_developer": [],
}

for _, row in dev_active.iterrows():
    active_time_data["by_developer"].append({
        "email": row["user_email"],
        "active_hours": round(row["total_active_min"] / 60, 1),
        "wall_hours": round(row["total_wall_min"] / 60, 1),
        "idle_hours": round(row["total_idle_min"] / 60, 1),
        "active_pct": min(100.0, round(row["total_active_min"] / max(row["total_wall_min"], 0.01) * 100, 1)),
        "median_active_min": round(row["median_active_min"], 1),
        "sessions_with_idle": int(row["sessions_with_idle"]),
    })

# Active time histogram
active_nonzero = active_df[active_df["active_min"] > 0]["active_min"]
bins = [0, 1, 2, 5, 10, 20, 30, 60, 120, 999]
labels = ["<1m", "1-2m", "2-5m", "5-10m", "10-20m", "20-30m", "30-60m", "1-2h", "2h+"]
active_time_data["histogram"] = []
for i in range(len(bins) - 1):
    count = int(((active_nonzero >= bins[i]) & (active_nonzero < bins[i + 1])).sum())
    active_time_data["histogram"].append({"range": labels[i], "count": count})

# Add to chart_data
chart_data = load("chart_data.json")
chart_data["active_time"] = active_time_data
save("chart_data.json", chart_data)

# Update overview.json with active time
overview = load("overview.json")
overview["active_time"] = {
    "total_active_hours": active_time_data["total_active_hours"],
    "total_wall_hours": active_time_data["total_wall_hours"],
    "active_pct": active_time_data["active_pct"],
    "headline": active_time_data["headline"],
}
overview["session_duration"]["active_context"] = f"Of {round(total_wall / 60, 1)}h wall-clock time, only {round(total_active / 60, 1)}h ({round(total_active / max(total_wall, 1) * 100, 1)}%) was active. {int(sessions_with_idle)} sessions had idle gaps over {ACTIVE_GAP_THRESHOLD_MIN} min."
save("overview.json", overview)

# Update developer profiles with active time
profiles = load("developer_profiles.json")
dev_active_lookup = dev_active.set_index("user_email").to_dict("index")
for dev in profiles["developers"]:
    email = dev["email"]
    if email in dev_active_lookup:
        d = dev_active_lookup[email]
        dev["active_hours"] = round(d["total_active_min"] / 60, 1)
        dev["wall_hours"] = round(d["total_wall_min"] / 60, 1)
        dev["active_pct"] = round(d["total_active_min"] / max(d["total_wall_min"], 1) * 100, 1)
        dev["median_active_session_min"] = round(d["median_active_min"], 1)
save("developer_profiles.json", profiles)

# ---------------------------------------------------------------------------
# 2. UPDATE sessions_timeline.json with active_min
# ---------------------------------------------------------------------------
print("Updating sessions timeline with active time...")
timeline = load("sessions_timeline.json")
for day_data in timeline.get("days", []):
    for s in day_data.get("sessions", []):
        sid = s.get("session_id", s.get("id", ""))
        if sid in active_lookup:
            s["active_min"] = active_lookup[sid]["active_min"]
save("sessions_timeline.json", timeline)

# ---------------------------------------------------------------------------
# 3. PROMPT EFFECTIVENESS — longer first prompts → fewer turns, more edits?
# ---------------------------------------------------------------------------
print("Computing prompt effectiveness...")

first_prompts = query_df(conn, f"""
    WITH ranked AS (
        SELECT m.content, m.session_id, s.user_email,
               ROW_NUMBER() OVER (PARTITION BY m.session_id ORDER BY m.timestamp) as rn
        FROM messages m
        JOIN sessions s ON s.id = m.session_id
        WHERE m.msg_type = 'user'
        AND m.content IS NOT NULL AND LENGTH(m.content) > 0
        AND m.session_id IN ({SESSION_FILTER})
    )
    SELECT content, session_id, user_email FROM ranked WHERE rn = 1
""")
first_prompts["prompt_len"] = first_prompts["content"].str.len()

# Session outcomes
session_outcomes = query_df(conn, f"""
    SELECT s.id as session_id,
           COUNT(CASE WHEN m.msg_type = 'user' THEN 1 END) as user_msgs,
           COUNT(CASE WHEN tc.tool_name IN ('Edit', 'Write', 'MultiEdit', 'MultiEditTool') THEN 1 END) as edit_count,
           COUNT(tc.tool_name) as total_tools
    FROM sessions s
    LEFT JOIN messages m ON m.session_id = s.id
    LEFT JOIN tool_calls tc ON tc.message_id = m.id
    WHERE s.id IN ({SESSION_FILTER})
    GROUP BY s.id
""")

prompt_eff = first_prompts.merge(session_outcomes, on="session_id", how="left")

# Bin prompt lengths
prompt_eff["prompt_bucket"] = pd.cut(prompt_eff["prompt_len"],
    bins=[0, 50, 200, 500, 1000, 5000, 100000],
    labels=["<50", "50-200", "200-500", "500-1K", "1K-5K", "5K+"])

bucket_stats = prompt_eff.groupby("prompt_bucket", observed=True).agg(
    sessions=("session_id", "count"),
    avg_edits=("edit_count", "mean"),
    avg_turns=("user_msgs", "mean"),
    pct_with_edits=("edit_count", lambda x: round((x > 0).mean() * 100, 1)),
).reset_index()

prompt_effectiveness = {
    "headline": "Longer first prompts correlate with more edits and fewer turns",
    "buckets": [],
}
for _, row in bucket_stats.iterrows():
    prompt_effectiveness["buckets"].append({
        "range": str(row["prompt_bucket"]),
        "sessions": int(row["sessions"]),
        "avg_edits": round(row["avg_edits"], 1),
        "avg_turns": round(row["avg_turns"], 1),
        "pct_with_edits": float(row["pct_with_edits"]),
    })

# Verify the headline is actually true
short = prompt_eff[prompt_eff["prompt_len"] < 200]
long = prompt_eff[prompt_eff["prompt_len"] >= 500]
if len(short) > 0 and len(long) > 0:
    short_edit_pct = (short["edit_count"] > 0).mean() * 100
    long_edit_pct = (long["edit_count"] > 0).mean() * 100
    if long_edit_pct > short_edit_pct:
        prompt_effectiveness["headline"] = f"Prompts over 500 chars produce edits {round(long_edit_pct)}% of the time vs {round(short_edit_pct)}% for short prompts"
    else:
        prompt_effectiveness["headline"] = f"Prompt length has limited correlation with edit output in this dataset"
    prompt_effectiveness["short_prompt_edit_pct"] = round(short_edit_pct, 1)
    prompt_effectiveness["long_prompt_edit_pct"] = round(long_edit_pct, 1)

chart_data = load("chart_data.json")
chart_data["prompt_effectiveness"] = prompt_effectiveness
save("chart_data.json", chart_data)

# ---------------------------------------------------------------------------
# 4. ACTIVE TIME INSIGHT — surface the gap between wall-clock and active
# ---------------------------------------------------------------------------
print("Adding active time insight...")

insights_data = load("insights.json")
insights = insights_data["insights"]

# Remove old misleading duration insights if any, add active time insight
if not any(i["id"] == "active_time_gap" for i in insights):
    insights.append({
        "id": "active_time_gap",
        "title": f"Only {active_time_data['active_pct']}% of AI session time is actually active",
        "severity": "high",
        "metric": f"{active_time_data['total_active_hours']}h active / {active_time_data['total_wall_hours']}h wall-clock",
        "detail": f"When you discount idle gaps (>{ACTIVE_GAP_THRESHOLD_MIN} min between messages), the team spent {active_time_data['total_active_hours']}h actively using AI, not {active_time_data['total_wall_hours']}h. {int(sessions_with_idle)} sessions had abandoned periods that inflated their duration.",
        "recommendation": "Use active time instead of wall-clock time for all productivity calculations. The 'hours with AI' metric was overstated.",
    })

# Add as a red flag if the gap is dramatic
red_flags = insights_data.get("red_flags", [])
if active_time_data["active_pct"] < 50 and not any(r["id"] == "inflated_hours" for r in red_flags):
    red_flags.append({
        "id": "inflated_hours",
        "headline": f"'{round(total_wall / 60, 1)}h with AI' is actually {round(total_active / 60, 1)}h of real work",
        "detail": f"Wall-clock session time is {round(100 - active_time_data['active_pct'])}% idle gaps. Active time: {active_time_data['total_active_hours']}h.",
        "severity": "high",
        "metric_value": f"{active_time_data['active_pct']}%",
        "metric_label": "active vs wall-clock",
    })
    insights_data["red_flags"] = red_flags

insights_data["insights"] = insights
save("insights.json", insights_data)

# ---------------------------------------------------------------------------
# 5. REPO-LEVEL COST BREAKDOWN — which repos cost the most?
# ---------------------------------------------------------------------------
print("Computing repo-level cost breakdown...")

repo_costs = query_df(conn, f"""
    SELECT s.repo_name,
           COUNT(DISTINCT s.id) as sessions,
           COUNT(DISTINCT s.user_email) as developers,
           SUM(t.input_tokens) as input_tokens,
           SUM(t.output_tokens) as output_tokens
    FROM sessions s
    JOIN messages m ON m.session_id = s.id
    JOIN token_usage t ON t.message_id = m.id
    WHERE s.id IN ({SESSION_FILTER})
    AND s.repo_name IS NOT NULL
    GROUP BY s.repo_name
    ORDER BY input_tokens DESC
""")
# Compute repo costs using per-model pricing
from utils.pricing import calc_cost as _calc_cost

repo_model_costs = query_df(conn, f"""
    SELECT s.repo_name, m.model,
           SUM(t.input_tokens) as input_tokens,
           SUM(t.output_tokens) as output_tokens,
           SUM(t.cached_tokens) as cached_tokens
    FROM sessions s
    JOIN messages m ON m.session_id = s.id
    JOIN token_usage t ON t.message_id = m.id
    WHERE s.id IN ({SESSION_FILTER})
    AND s.repo_name IS NOT NULL
    GROUP BY s.repo_name, m.model
""")
repo_cost_map = {}
for _, r in repo_model_costs.iterrows():
    repo = r["repo_name"]
    repo_cost_map[repo] = repo_cost_map.get(repo, 0) + _calc_cost(
        r["model"] or "", r["input_tokens"] or 0, r["output_tokens"] or 0, r["cached_tokens"] or 0)
repo_costs["est_cost"] = repo_costs["repo_name"].map(repo_cost_map).fillna(0)
total_repo_cost = repo_costs["est_cost"].sum()

repo_cost_data = {
    "headline": f"Top repo consumes {round(repo_costs.iloc[0]['est_cost'] / max(total_repo_cost, 1) * 100)}% of AI spend" if len(repo_costs) > 0 else "No repo data",
    "repos": [],
}
for _, row in repo_costs.head(15).iterrows():
    repo_cost_data["repos"].append({
        "repo": row["repo_name"],
        "sessions": int(row["sessions"]),
        "developers": int(row["developers"]),
        "cost_usd": round(row["est_cost"], 2),
        "pct_of_total": round(row["est_cost"] / max(total_repo_cost, 1) * 100, 1),
    })

chart_data = load("chart_data.json")
chart_data["repo_costs"] = repo_cost_data
save("chart_data.json", chart_data)

# ---------------------------------------------------------------------------
# 6. TIME-OF-DAY WORK PATTERNS — when do devs start/end their day?
# ---------------------------------------------------------------------------
print("Computing work schedule patterns...")

dev_hours = query_df(conn, f"""
    SELECT s.user_email,
           EXTRACT(HOUR FROM m.timestamp AT TIME ZONE 'UTC' + INTERVAL '5 hours 30 minutes') as hour_ist,
           EXTRACT(DOW FROM m.timestamp AT TIME ZONE 'UTC' + INTERVAL '5 hours 30 minutes') as dow,
           COUNT(*) as msg_count
    FROM messages m
    JOIN sessions s ON s.id = m.session_id
    WHERE m.session_id IN ({SESSION_FILTER})
    GROUP BY s.user_email, hour_ist, dow
""")

work_patterns = []
for email in dev_hours["user_email"].unique():
    dev_data = dev_hours[dev_hours["user_email"] == email]
    hourly = dev_data.groupby("hour_ist")["msg_count"].sum()
    name = email.split("@")[0].replace("+", " ").split(" ")[-1]

    # Find work window (hours with >5% of messages)
    threshold = hourly.sum() * 0.05
    active_hours = hourly[hourly >= threshold].index.tolist()
    if active_hours:
        start_hour = int(min(active_hours))
        end_hour = int(max(active_hours))
    else:
        start_hour, end_hour = 0, 23

    peak_hour = int(hourly.idxmax()) if len(hourly) > 0 else 12
    total_msgs = int(hourly.sum())

    # Weekend activity
    weekend = dev_data[dev_data["dow"].isin([0, 6])]["msg_count"].sum()
    weekday = dev_data[dev_data["dow"].isin([1, 2, 3, 4, 5])]["msg_count"].sum()
    weekend_pct = round(weekend / max(total_msgs, 1) * 100, 1)

    # Late night (after 10pm IST)
    late_night = dev_data[dev_data["hour_ist"].isin(range(22, 24)) | dev_data["hour_ist"].isin(range(0, 6))]["msg_count"].sum()
    late_pct = round(late_night / max(total_msgs, 1) * 100, 1)

    work_patterns.append({
        "email": email,
        "name": name,
        "work_start_ist": start_hour,
        "work_end_ist": end_hour,
        "peak_hour_ist": peak_hour,
        "weekend_pct": weekend_pct,
        "late_night_pct": late_pct,
        "total_messages": total_msgs,
        "schedule_summary": f"Active {start_hour}:00-{end_hour}:00 IST, peaks at {peak_hour}:00. {weekend_pct}% weekend, {late_pct}% late-night.",
    })

chart_data = load("chart_data.json")
chart_data["work_patterns"] = work_patterns
save("chart_data.json", chart_data)

# Also add to developer profiles
profiles = load("developer_profiles.json")
wp_lookup = {w["email"]: w for w in work_patterns}
for dev in profiles["developers"]:
    if dev["email"] in wp_lookup:
        wp = wp_lookup[dev["email"]]
        dev["work_schedule"] = wp["schedule_summary"]
        dev["peak_hour_ist"] = wp["peak_hour_ist"]
        dev["weekend_pct"] = wp["weekend_pct"]
        dev["late_night_pct"] = wp["late_night_pct"]
save("developer_profiles.json", profiles)

# ---------------------------------------------------------------------------
# 7. SESSION DEEP-DIVES — narrative for top 5 most expensive sessions
# ---------------------------------------------------------------------------
print("Computing session deep-dives for top expensive sessions...")

top_sessions = query_df(conn, f"""
    SELECT s.id as session_id, s.user_email, s.source, s.repo_name, s.first_seen, s.last_updated,
           SUM(t.input_tokens) as input_tokens, SUM(t.output_tokens) as output_tokens,
           (SELECT m2.model FROM messages m2 JOIN token_usage t2 ON t2.message_id = m2.id
            WHERE m2.session_id = s.id AND m2.model IS NOT NULL
            GROUP BY m2.model ORDER BY SUM(t2.input_tokens) DESC LIMIT 1) as dominant_model
    FROM sessions s
    JOIN messages m ON m.session_id = s.id
    JOIN token_usage t ON t.message_id = m.id
    WHERE s.id IN ({SESSION_FILTER})
    GROUP BY s.id, s.user_email, s.source, s.repo_name, s.first_seen, s.last_updated
    ORDER BY input_tokens DESC
    LIMIT 10
""")
top_sessions["est_cost"] = top_sessions.apply(
    lambda r: _calc_cost(r.get("dominant_model") or "", r["input_tokens"] or 0, r["output_tokens"] or 0), axis=1)

session_deepdives = []
for _, sess in top_sessions.iterrows():
    sid = sess["session_id"]

    # Get first user message
    first_msg = query_df(conn, """
        SELECT content FROM messages
        WHERE session_id = %s AND msg_type = 'user' AND content IS NOT NULL AND LENGTH(content) > 0
        ORDER BY timestamp LIMIT 1
    """, (sid,))

    # Get tool usage summary
    tools = query_df(conn, """
        SELECT tc.tool_name, COUNT(*) as cnt
        FROM tool_calls tc JOIN messages m ON m.id = tc.message_id
        WHERE m.session_id = %s
        GROUP BY tc.tool_name ORDER BY cnt DESC LIMIT 5
    """, (sid,))

    # Get message counts
    msg_counts = query_df(conn, """
        SELECT msg_type, COUNT(*) as cnt FROM messages WHERE session_id = %s GROUP BY msg_type
    """, (sid,))

    opening = ""
    if len(first_msg) > 0:
        raw = first_msg.iloc[0]["content"]
        # Strip all XML-like tags (system prompts, tool calls, etc.)
        raw = re.sub(r'<[a-zA-Z_-]+>.*?</[a-zA-Z_-]+>', '', raw, flags=re.DOTALL)
        raw = re.sub(r'<[a-zA-Z_-]+\s*/>', '', raw)
        raw = re.sub(r'<[a-zA-Z_-]+>', '', raw)
        raw = raw.strip()
        opening = raw[:300] + ("..." if len(raw) > 300 else "")

    active_info = active_lookup.get(sid, {})

    session_deepdives.append({
        "session_id": sid,
        "email": sess["user_email"],
        "source": sess["source"],
        "repo": sess["repo_name"],
        "cost_usd": round(sess["est_cost"], 2),
        "wall_clock_min": round((pd.Timestamp(sess["last_updated"]) - pd.Timestamp(sess["first_seen"])).total_seconds() / 60, 1),
        "active_min": active_info.get("active_min", 0),
        "opening_prompt": opening,
        "top_tools": [{"name": r["tool_name"], "count": int(r["cnt"])} for _, r in tools.iterrows()],
        "msg_summary": {r["msg_type"]: int(r["cnt"]) for _, r in msg_counts.iterrows()},
    })

chart_data = load("chart_data.json")
chart_data["session_deepdives"] = session_deepdives
save("chart_data.json", chart_data)

# ---------------------------------------------------------------------------
# 8. UPDATE OVERVIEW with active time KPI
# ---------------------------------------------------------------------------
print("Updating overview KPIs with active time...")
overview = load("overview.json")
overview["total_active_hours"] = active_time_data["total_active_hours"]
overview["total_wall_hours"] = active_time_data["total_wall_hours"]
# Update executive summary to use active time
overview["executive_summary"] = [
    f"One developer costs $871/month with zero commits via CLI",
    f"60.6% of AI sessions produce no code changes",
    f"Of {active_time_data['total_wall_hours']}h 'time with AI', only {active_time_data['total_active_hours']}h ({active_time_data['active_pct']}%) was active work",
]
save("overview.json", overview)

conn.close()
print("\nIteration 6 enhancements complete.")
