#!/usr/bin/env python3
"""
count_claude_tokens.py  -  recount your Claude Code token usage the documented way.

Claude Code writes one line per content block of a streamed response into
~/.claude/projects/**/*.jsonl. Every line of one response carries the same
requestId, the same message.id and the same usage object, so summing lines
counts one API call two to four times (sometimes forty). Anthropic's own
documentation says to count each message.id once. This script does both and
prints the ratio for your machine.

No dependencies. Python 3.8+. Read-only.

Usage:
    python3 count_claude_tokens.py            # all time
    python3 count_claude_tokens.py 7          # last 7 days (UTC)
    python3 count_claude_tokens.py 30
"""
import glob, json, os, sys, datetime, collections

F = ("input_tokens", "output_tokens", "cache_creation_input_tokens", "cache_read_input_tokens")
root = os.path.expanduser("~/.claude/projects")
days = int(sys.argv[1]) if len(sys.argv) > 1 else None
since = (datetime.date.today() - datetime.timedelta(days=days - 1)).isoformat() if days else "0000-00-00"

per_line = collections.Counter()
per_call = {}          # key -> usage dict with the largest total (the complete record)
rows_per_call = collections.Counter()
files = 0

for path in glob.glob(os.path.join(root, "**", "*.jsonl"), recursive=True):
    files += 1
    with open(path, errors="replace") as fh:
        for n, line in enumerate(fh):
            if '"usage"' not in line:
                continue
            try:
                e = json.loads(line)
            except Exception:
                continue
            msg = e.get("message") or {}
            u = msg.get("usage")
            ts = e.get("timestamp") or ""
            if not u or ts[:10] < since:
                continue
            for k in F:
                per_line[k] += u.get(k, 0)
            key = e.get("requestId") or msg.get("id") or (path, n)
            rows_per_call[key] += 1
            tot = sum(u.get(k, 0) for k in F)
            if key not in per_call or tot > sum(per_call[key].get(k, 0) for k in F):
                per_call[key] = u

per_request = collections.Counter()
for u in per_call.values():
    for k in F:
        per_request[k] += u.get(k, 0)

L = sum(per_line.values()); R = sum(per_request.values())
label = f"last {days} days" if days else "all time"
print(f"Claude Code token recount, {label}: {files} transcript files, {len(per_call):,} API calls\n")
print(f"{'field':28s} {'per line (Stats tab)':>22s} {'per call (documented)':>22s} {'ratio':>7s}")
for k in F:
    print(f"{k:28s} {per_line[k]:22,d} {per_request[k]:22,d} {per_line[k] / max(1, per_request[k]):7.2f}")
print(f"{'TOTAL':28s} {L:22,d} {R:22,d} {L / max(1, R):7.2f}")
dist = collections.Counter(min(n, 5) for n in rows_per_call.values())
print("\nlines per call: " + ", ".join(f"{'5+' if n == 5 else n}: {100 * dist[n] / max(1, len(per_call)):.0f}%" for n in sorted(dist)) + f"; max {max(rows_per_call.values(), default=0)}")
print("\nThe Stats tab in /usage shows the 'per line' column. The documented count is 'per call'.")
