#!/usr/bin/env python3
"""AI と書いたコミットが .md に足した行が、あとの時点まで残っているかを数える。
git blame で消えたと出た行は、文面がどこかに残っているかでさらに分ける。

使い方: python3 survival.py [数えるコミットの最後] [測る時点]
  どちらも省略すると HEAD。測る時点だけを先に進めると、同じコミットを後から数え直せる。
"""
import collections
import re
import subprocess
import sys

until = sys.argv[1] if len(sys.argv) > 1 else "HEAD"
at = sys.argv[2] if len(sys.argv) > 2 else until


def git(*args):
    return subprocess.run(["git", *args], capture_output=True, text=True, check=True).stdout


# Co-Authored-By に Claude が入ったコミット(共同作成者が何人いても 1 行にまとめる)
ai = set()
for line in git("log", until, "--format=%H %(trailers:key=Co-Authored-By,valueonly,separator=%x2C )").splitlines():
    sha, _, authors = line.partition(" ")
    if "claude" in authors.lower():
        ai.add(sha)

# AI のコミットが .md に足した行(前後の空白は除く)と、コミットの日付
added = collections.defaultdict(collections.Counter)
day = {}
for line in git("log", until, "--no-merges", "-p", "--format=C %H %ad", "--date=short", "--", "*.md").splitlines():
    if line.startswith("C "):
        _, sha, date = line.split()
        day[sha] = date
    elif sha in ai and line.startswith("+") and not line.startswith("+++"):
        added[sha][line[1:].strip()] += 1

# 測る時点の .md の全行と、git blame が各行を帰属させたコミット
now = set()
alive = collections.defaultdict(collections.Counter)
for path in git("ls-tree", "-r", "-z", "--name-only", at).split("\0"):
    if not path.endswith(".md"):
        continue
    for line in git("blame", "--line-porcelain", at, "--", path).splitlines():
        if re.match(r"[0-9a-f]{40} ", line):
            sha = line[:40]
        elif line.startswith("\t"):
            now.add(line[1:].strip())
            alive[sha][line[1:].strip()] += 1

total, kept, gone = collections.Counter(), collections.Counter(), collections.Counter()
for sha, lines in added.items():
    total[day[sha]] += sum(lines.values())
    kept[day[sha]] += sum((lines & alive[sha]).values())
    for text, n in (lines - alive[sha]).items():
        if text not in now:
            gone["文面も残っていない"] += n
        elif not text:
            gone["空行"] += n
        elif not re.search(r"\w", text):
            gone["記号だけの行"] += n
        else:
            gone["文字のある行"] += n

for d in sorted(total):
    print(f"{d}  追加 {total[d]:6}  残存 {kept[d]:6}  {kept[d] / total[d]:6.1%}")
t, k = sum(total.values()), sum(kept.values())
if not t:
    sys.exit("Claude と書いたコミットが .md に足した行が見つからない")
print(f"合計  追加 {t}  残存 {k}  {k / t:.1%}")
print(f"消えた {t - k} 行" + ("のうち " + "、".join(f"{kind} {n}" for kind, n in gone.most_common()) if gone else ""))
