# -*- coding: utf-8 -*-
"""
JV-Link レース情報 x 確定オッズ 整合性チェック（JV-Link 単体）

- RACE option=4 で FROM_TO の期間を取得
- RA(成績確定 k7) と O1〜O6 のレースキーを突き合わせ
- O1 は「レコードがある」だけでなく生データに単勝オッズ値があるかも数える
- 結果は diag_result.txt (UTF-8)

編集: FROM_TO のみ
実行:
  cd /d C:\\Users\\oshim\\jrvltsql
  venv32\\Scripts\\python.exe diag_jvlink_integrity.py
"""
from __future__ import annotations

import platform
import sys
import time
from collections import Counter, defaultdict

LOG_PATH = "diag_result.txt"
FROM_TO = "20260101000000-20260611000000"
BUFSIZE = 300000
# oshim PC は JVDTLab.JVLink。別環境は JVDTLabLib.JVLink のこともある。
PROGIDS = ("JVDTLab.JVLink", "JVDTLabLib.JVLink")

KUBUN_RA = {
    "1": "出走馬名表",
    "2": "出馬表",
    "3": "速報成績(3着)",
    "4": "速報成績(5着)",
    "5": "速報成績(全馬)",
    "6": "速報成績(入線順)",
    "7": "成績確定",
    "9": "中止",
    "0": "削除",
}


def _race_key(raw: str) -> str | None:
    if len(raw) < 27:
        return None
    return raw[11:27]


def _o1_has_tanodds(raw: str) -> bool:
  hdr, bpc = 43, 8
  if len(raw) < hdr + bpc:
      return False
  data = raw[hdr:]
  for i in range(0, len(data) - bpc + 1, bpc):
      odds_s = data[i + 2 : i + 6].strip()
      if odds_s and odds_s.strip("0"):
          try:
              if int(odds_s) > 0:
                  return True
          except ValueError:
              pass
  return False


def _fmt_key(k: str) -> str:
    if len(k) < 16:
        return k
    return f"{k[0:4]}/{k[4:6]}/{k[6:8]} 場{k[8:10]} {k[10:12]}回{k[12:14]}日 {k[14:16]}R"


def main(f) -> None:
    def log(msg: str) -> None:
        print(msg)
        f.write(str(msg) + "\n")
        f.flush()

    log("=== JV-Link レース/確定オッズ 整合性チェック ===")
    log(f"FROM_TO={FROM_TO}")
    log(f"Python {sys.version.split()[0]} ({platform.architecture()[0]})")
    if platform.architecture()[0] != "32bit":
        log("ERROR: 32bit Python が必要です (venv32\\Scripts\\python.exe)")
        sys.exit(1)

    try:
        import win32com.client as win32
    except ImportError:
        log("ERROR: pywin32 がありません (pip install pywin32)")
        sys.exit(1)

    jv = None
    for progid in PROGIDS:
        try:
            jv = win32.Dispatch(progid)
            log(f"COM ProgID: {progid}")
            break
        except Exception as e:
            log(f"ProgID {progid} NG: {e}")
    if jv is None:
        log("ERROR: JV-Link COM を開けません。JV-Link インストールを確認")
        return

    rc = jv.JVInit("UNKNOWN")
    log(f"JVInit rc={rc} (0=正常)")
    if rc != 0:
        log("→ JV-Link 設定(サービスキー)を確認")
        return

    r = jv.JVOpen("RACE", FROM_TO, 4, 0, 0, "")
    rc, readcount, dlcount, lastts = r[0], r[1], r[2], r[3]
    log(f"JVOpen rc={rc} readcount={readcount} dlcount={dlcount} last={lastts}")
    if rc < 0:
        log("→ JVOpen エラー (-1:該当なし -111:dataspec -112:fromtime -301/-302:認証)")
        return
    if readcount == 0:
        log("→ 対象ファイル 0 件")
        jv.JVClose()
        return

    t0 = time.time()
    last_log = -1
    while dlcount > 0:
        st = jv.JVStatus()
        if st < 0:
            log(f"JVStatus エラー: {st}")
            return
        if st >= dlcount:
            break
        elapsed = int(time.time() - t0)
        if elapsed // 30 > last_log:
            last_log = elapsed // 30
            log(f"  ダウンロード中 {st}/{dlcount} ({elapsed}秒)")
        time.sleep(2)
    log(f"ダウンロード完了 ({int(time.time()-t0)}秒)。読み込み開始...")

    counts: Counter[str] = Counter()
    ra_kubun: Counter[str] = Counter()
    keys: defaultdict[str, set[str]] = defaultdict(set)
    o1_odds_keys: set[str] = set()
    ra7_keys: set[str] = set()
    broken: list[str] = []

    while True:
        r = jv.JVRead(" " * BUFSIZE, BUFSIZE, "")
        rc, data = r[0], r[1]
        fname = r[2] if len(r) > 2 else ""
        if rc == 0:
            break
        if rc == -1:
            continue
        if rc == -3:
            time.sleep(1)
            continue
        if rc < 0:
            log(f"JVRead エラー rc={rc} file={fname}")
            if rc in (-402, -403, -502, -503):
                jv.JVFileDelete(fname)
                broken.append(fname)
                log("→ 破損ファイル削除。このスクリプトを再実行")
            break
        rec = data[:2]
        counts[rec] += 1
        if rec in ("RA", "SE", "HR", "O1", "O2", "O3", "O4", "O5", "O6"):
            key = _race_key(data)
            if not key:
                continue
            keys[rec].add(key)
            if rec == "RA":
                ra_kubun[data[2]] += 1
                if data[2] == "7":
                    ra7_keys.add(key)
            elif rec == "O1" and _o1_has_tanodds(data):
                o1_odds_keys.add(key)

    jv.JVClose()

    log("")
    log("=== 1) レコード種別ごとの取得件数 ===")
    for k in sorted(counts):
        log(f"  {k}: {counts[k]}")
    log("")
    log("=== 2) RA データ区分内訳 ===")
    for k in sorted(ra_kubun):
        log(f"  区分{k} ({KUBUN_RA.get(k, '?')}): {ra_kubun[k]}")
    log("")
    log("=== 3) レース数(ユニーク)の突き合わせ ===")
    base = ra7_keys if ra7_keys else keys.get("RA", set())
    base_name = "RA(成績確定 k7)" if ra7_keys else "RA(全区分)"
    log(f"  {base_name}: {len(base)} レース")
    log(f"  O1+odds(単勝値あり): {len(o1_odds_keys)} レース  <- 欲しい情報")
    ok = True
    for rec in ("HR", "O1", "O2", "O3", "O4", "O5", "O6"):
        n = len(keys.get(rec, set()))
        miss = base - keys.get(rec, set())
        status = "OK 一致" if not miss else f"NG {len(miss)} レース欠損"
        if miss:
            ok = False
        log(f"  {rec}: {n} レース → {status}")
    log("")
    miss_odds = base - o1_odds_keys
    log("=== 4) O1 レコードはあるが単勝オッズ値が無い ===")
    o1_only = keys.get("O1", set()) - o1_odds_keys
    log(f"  O1 行はあるが odds 値なし: {len(o1_only)} レース")
    log(f"  成績確定 k7 だが odds 値なし: {len(miss_odds)} レース")
    log("")
    if ok and not miss_odds:
        log("=== 判定: 成績確定レースすべてに O1 行と単勝オッズ値あり ===")
        log("→ JV-Link に欲しい情報が揃っている。")
    elif ok and miss_odds:
        log(f"=== 判定: O1 行は揃うが単勝オッズ値が {len(miss_odds)} レース欠損 ===")
        log("→ JV-Link 時点でオッズが空。jrvltsql を直しても埋まらない可能性大。")
        for k in sorted(miss_odds)[:30]:
            log(f"  {_fmt_key(k)}")
    else:
        miss = sorted(base - keys.get("O1", set()))
        log(f"=== 判定: O1 レコード欠損 {len(miss)} レース (先頭30) ===")
        for k in miss[:30]:
            log(f"  {_fmt_key(k)}")
    if broken:
        log(f"破損ファイル削除済み: {broken} → 再実行が必要")
    log("")
    log("=== 完了 ===")


if __name__ == "__main__":
    with open(LOG_PATH, "w", encoding="utf-8") as fp:
        try:
            main(fp)
        except SystemExit:
            raise
        except Exception:
            import traceback

            fp.write("EXCEPTION:\n" + traceback.format_exc())
            traceback.print_exc()
