#!/usr/bin/env python3
"""youtube_market.py — 企画の入口になる言葉で YouTube を検索し、似た題材の動画が実際に何回見られているかを調べる.

企画会議（kikaku.py）が「どれが伸びるか」を推測だけで決めないための材料。
YouTube Data API の検索（search.list）と、動画・チャンネルの数字（videos.list・channels.list）を使う。

置き場所（動画/_調べ/{調べの名前}/）:
  キーワード.txt   1行に1つ、調べる言葉（# で始まる行は無視）
  かける.txt       印。議論係の見回り（debate_runner.py）がこれを見て動かす。終わると かけた.txt
  結果.md          言葉ごとの数字（本編とショートを分けて、再生数の中央値・上位の動画・登録者の少ないチャンネルで伸びた動画）
  結果.json        取ったままの数字

許可は youtube_schedule.py の読み取りの許可（~/.config/pixelstomotors/youtube_token.json）をそのまま使う。
API の1日の枠は10,000。検索1回で100使うので、1回の調べは言葉20個までにする。

手で動かすとき:  python3 youtube_market.py {調べのフォルダ}
"""
import json, os, statistics, sys, urllib.parse
from datetime import datetime, timedelta, timezone

HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
import youtube_schedule as ys

JST = timezone(timedelta(hours=9))
MAX_WORDS = 20
SHORT_SEC = 180          # これ以下はショートとして分ける


def api(path, tok, **q):
    return ys.call(f"{ys.API}/{path}?" + urllib.parse.urlencode(q), headers={"Authorization": "Bearer " + tok})


def search(tok, word, since):
    ids, page = [], ""
    for _ in range(1):                     # 1ページ50本（枠を守るため1ページだけ）
        q = dict(part="id", q=word, type="video", maxResults=50, regionCode="JP", relevanceLanguage="ja",
                 order="relevance", publishedAfter=since)
        if page:
            q["pageToken"] = page
        r = api("search", tok, **q)
        ids += [i["id"]["videoId"] for i in r.get("items", []) if i.get("id", {}).get("videoId")]
        page = r.get("nextPageToken")
        if not page:
            break
    return ids


def details(tok, ids):
    out = []
    for i in range(0, len(ids), 50):
        r = api("videos", tok, part="snippet,statistics,contentDetails", id=",".join(ids[i:i + 50]))
        out += r.get("items", [])
    return out


def subs(tok, chids):
    m = {}
    chids = list(dict.fromkeys(chids))
    for i in range(0, len(chids), 50):
        r = api("channels", tok, part="statistics", id=",".join(chids[i:i + 50]))
        for c in r.get("items", []):
            s = c.get("statistics", {})
            m[c["id"]] = None if s.get("hiddenSubscriberCount") else int(s.get("subscriberCount", 0))
    return m


def run(folder):
    words = [w.strip() for w in open(os.path.join(folder, "キーワード.txt"), encoding="utf-8")
             if w.strip() and not w.strip().startswith("#")][:MAX_WORDS]
    tok = ys.access_token()
    now = datetime.now(timezone.utc)
    since = (now - timedelta(days=365)).strftime("%Y-%m-%dT%H:%M:%SZ")
    allres = {}
    L = [f"# YouTube で調べた結果（{datetime.now(JST).strftime('%Y-%m-%d %H:%M')}・直近1年に公開された動画・日本向けの検索）", "",
         "- 1つの言葉につき、YouTube の検索の上位50本を見た。本編（3分より長い）とショートを分けた",
         "- 再生数は調べた時点の累計。公開からの日数がばらばらなので、1日あたりの再生数も出した",
         "- 「小さなチャンネルで伸びた」は、登録者1万人未満のチャンネルで再生数が登録者数を超えた本編。題材そのものの力が強い目安", ""]
    for w in words:
        ids = search(tok, w, since)
        vs = details(tok, ids)
        sm = subs(tok, [v["snippet"]["channelId"] for v in vs])
        rows = []
        for v in vs:
            sec = ys.seconds(v.get("contentDetails", {}).get("duration")) or 0
            pub = datetime.fromisoformat(v["snippet"]["publishedAt"].replace("Z", "+00:00"))
            days = max(1, (now - pub).days)
            views = int(v.get("statistics", {}).get("viewCount", 0))
            rows.append({"id": v["id"], "title": v["snippet"]["title"], "channel": v["snippet"]["channelTitle"],
                         "subs": sm.get(v["snippet"]["channelId"]), "views": views, "days": days,
                         "per_day": round(views / days, 1), "sec": sec, "short": sec <= SHORT_SEC,
                         "published": pub.date().isoformat()})
        allres[w] = rows
        longs = sorted([r for r in rows if not r["short"]], key=lambda r: -r["views"])
        shorts = [r for r in rows if r["short"]]
        L += [f"## {w}", ""]
        if longs:
            med = statistics.median(r["views"] for r in longs)
            n1k = sum(1 for r in longs if r["views"] >= 1000)
            n10k = sum(1 for r in longs if r["views"] >= 10000)
            L.append(f"- 本編 {len(longs)} 本：再生数の中央値 {med:,.0f} 回・1,000回以上 {n1k} 本・10,000回以上 {n10k} 本")
        else:
            L.append("- 本編は見つからなかった")
        if shorts:
            L.append(f"- ショート {len(shorts)} 本：中央値 {statistics.median(r['views'] for r in shorts):,.0f} 回")
        small = [r for r in longs if r["subs"] is not None and r["subs"] < 10000 and r["views"] > r["subs"]]
        L += ["", "上位の本編（再生数の順・5本）", "", "| 題名 | チャンネル（登録者） | 再生数 | 1日あたり | 公開日 |", "|---|---|---|---|---|"]
        for r in longs[:5]:
            L.append(f"| {r['title'][:50]} | {r['channel'][:20]}（{r['subs'] if r['subs'] is not None else '非公開'}） | {r['views']:,} | {r['per_day']:,} | {r['published']} |")
        if small:
            L += ["", "小さなチャンネルで伸びた本編", ""]
            for r in sorted(small, key=lambda r: -r["views"])[:5]:
                L.append(f"- {r['title'][:60]}（{r['channel'][:20]}・登録者 {r['subs']:,}・{r['views']:,} 回・{r['published']}）")
        L.append("")
    with open(os.path.join(folder, "結果.json"), "w", encoding="utf-8") as f:
        json.dump(allres, f, ensure_ascii=False, indent=1)
    with open(os.path.join(folder, "結果.md"), "w", encoding="utf-8") as f:
        f.write("\n".join(L))
    return len(words)


if __name__ == "__main__":
    print(run(sys.argv[1]), "語を調べました")
