# -*- coding: utf-8 -*-
"""
全 O1 生レコード x NL_O1 を1レースずつ突き合わせ（約1,600件）

JV-Link から O1 生データを読み、バイト位置で単勝オッズをパース。
同じレースキーで keiba.db NL_O1 の TanOdds と比較。

出力: o1_full_check.txt （UTF-8）

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

import platform
import sys
import time
from collections import defaultdict

FROM_TO = "20260101000000-20260611000000"
OPTION = 4
BUFSIZE = 300000
DB = r"data\keiba.db"
OUT = "o1_full_check.txt"
PROGIDS = ("JVDTLab.JVLink", "JVDTLabLib.JVLink")
O1_HDR = 43
O1_BPC = 8  # umaban2 + odds4 + ninki2


def _db_key(raw: str) -> str | None:
    if len(raw) < 27:
        return None
    return f"{raw[11:15]}|{raw[19:21]}|{raw[15:19]}|{raw[25:27]}"


def _fmt_key(k: str) -> str:
    y, j, md, rn = k.split("|")
    return f"{y}/{md[0:2]}/{md[2:4]} 場{j} {rn}R"


def _parse_jv_tanodds(raw: str) -> dict[int, float]:
    """生 O1 から馬番→単勝オッズ(倍率)。"""
    out: dict[int, float] = {}
    if len(raw) < O1_HDR + O1_BPC:
        return out
    data = raw[O1_HDR:]
    for i in range(0, len(data) - O1_BPC + 1, O1_BPC):
        uma_s = data[i : i + 2].strip()
        odds_s = data[i + 2 : i + 6].strip()
        if not uma_s or not odds_s or odds_s.strip("0") == "":
            continue
        try:
            uma = int(uma_s)
            v = int(odds_s) / 10.0
            if uma > 0 and v > 0:
                out[uma] = v
        except ValueError:
            continue
    return out


def _load_db_o1(path: str) -> dict[str, dict[int, str]]:
    import sqlite3

    conn = sqlite3.connect(path)
    by_race: dict[str, dict[int, str]] = defaultdict(dict)
    for y, j, md, rn, uma, to, kubun in conn.execute(
        "SELECT Year,JyoCD,MonthDay,RaceNum,Umaban,TanOdds,DataKubun "
        "FROM NL_O1 WHERE Year='2026' AND DataKubun IN ('4','5')"
    ):
        k = f"{y}|{str(j).zfill(2)}|{md}|{str(rn).zfill(2)}"
        if uma is not None:
            by_race[k][int(uma)] = to
    conn.close()
    return by_race


def _dispatch(win32):
    for p in PROGIDS:
        try:
            return win32.Dispatch(p), p
        except Exception:
            pass
    return None, ""


def main() -> None:
    if platform.architecture()[0] != "32bit":
        print("ERROR: venv32\\Scripts\\python.exe")
        sys.exit(1)
    import win32com.client as win32

    print("Loading NL_O1 from", DB, "...")
    db = _load_db_o1(DB)
    print("DB races:", len(db))

    jv = None
    for p in PROGIDS:
        try:
            jv = win32.Dispatch(p)
            print("ProgID:", p)
            break
        except Exception:
            pass
    if jv is None:
        sys.exit("JV-Link COM failed")

    if jv.JVInit("UNKNOWN") != 0:
        sys.exit("JVInit failed")

    r = jv.JVOpen("RACE", FROM_TO, OPTION, 0, 0, "")
    if r[0] < 0 or r[1] == 0:
        jv.JVClose()
        sys.exit(f"JVOpen failed rc={r[0]} read={r[1]}")

    dl = r[2]
    while dl > 0:
        st = jv.JVStatus()
        if st < 0 or st >= dl:
            break
        time.sleep(1)

    records: list[tuple[str, str, dict[int, float]]] = []
    print("JVRead...")
    while True:
        r = jv.JVRead(" " * BUFSIZE, BUFSIZE, "")
        rc, data = r[0], r[1]
        if rc == 0:
            break
        if rc in (-1, -3):
            if rc == -3:
                time.sleep(1)
            continue
        if rc < 0:
            break
        if len(data) < 2 or data[:2] != "O1":
            continue
        k = _db_key(data)
        if k:
            records.append((k, data, _parse_jv_tanodds(data)))

    jv.JVClose()
    print("JV O1 records:", len(records))

    ok = 0
    jv_ok_db_empty = []
    jv_ok_db_partial = []
    no_db_rows = []
    jv_empty = []

    with open(OUT, "w", encoding="utf-8") as f:
        f.write("=== O1 全件チェック JV生 vs NL_O1 ===\n")
        f.write(f"FROM_TO={FROM_TO}\n")
        f.write(f"JV O1 records: {len(records)}\n")
        f.write(f"DB races (NL_O1 kubun 4,5): {len(db)}\n\n")

        for k, raw, jv_odds in sorted(records, key=lambda x: x[0]):
            db_horses = db.get(k, {})
            db_with = {
                u: v
                for u, v in db_horses.items()
                if v is not None and str(v).strip() and float(str(v).strip() or 0) > 0
            }
            jv_n = len(jv_odds)
            db_n = len(db_with)

            if jv_n == 0:
                jv_empty.append(k)
                status = "JV_EMPTY"
            elif not db_horses:
                no_db_rows.append(k)
                status = "NO_DB_ROW"
            elif db_n == 0 and jv_n > 0:
                jv_ok_db_empty.append((k, raw, jv_odds, db_horses))
                status = "JV_OK_DB_EMPTY"
            elif db_n < jv_n:
                jv_ok_db_partial.append((k, jv_odds, db_with))
                status = f"PARTIAL jv={jv_n} db={db_n}"
            else:
                ok += 1
                status = "OK"

            f.write(f"{_fmt_key(k)}\t{status}\tjv_horses={jv_n}\tdb_tanodds={db_n}\n")

        f.write("\n=== サマリ ===\n")
        f.write(f"OK (JV+DB両方オッズあり):     {ok}\n")
        f.write(f"JV_OK_DB_EMPTY (展開欠陥):    {len(jv_ok_db_empty)}\n")
        f.write(f"PARTIAL (一部のみDBに入った): {len(jv_ok_db_partial)}\n")
        f.write(f"NO_DB_ROW:                    {len(no_db_rows)}\n")
        f.write(f"JV_EMPTY (生データにオッズ無): {len(jv_empty)}\n")

        f.write("\n=== JV_OK_DB_EMPTY 全件 (生オッズ vs DB) ===\n")
        for k, raw, jv_odds, db_horses in jv_ok_db_empty:
            f.write(f"\n--- {_fmt_key(k)} DataKubun={raw[2:3]!r} ---\n")
            f.write("  JV生パース(馬番:オッズ): " + ", ".join(f"{u}:{v}" for u, v in sorted(jv_odds.items())[:8]))
            if len(jv_odds) > 8:
                f.write(f" ...+{len(jv_odds)-8}")
            f.write("\n")
            f.write("  DB TanOdds: " + ", ".join(f"{u}:{db_horses.get(u)!r}" for u in sorted(db_horses.keys())[:8]))
            f.write("\n")
            f.write("  raw[43:120] repr: " + repr(raw[43:120]) + "\n")

        if jv_ok_db_partial:
            f.write("\n=== PARTIAL 先頭20件 ===\n")
            for k, jv_odds, db_with in jv_ok_db_partial[:20]:
                f.write(f"{_fmt_key(k)} jv={len(jv_odds)} db={len(db_with)}\n")

    print("")
    print(f"OK:              {ok}")
    print(f"JV_OK_DB_EMPTY:  {len(jv_ok_db_empty)}  <- 生はあるDBが空")
    print(f"PARTIAL:         {len(jv_ok_db_partial)}")
    print(f"NO_DB_ROW:       {len(no_db_rows)}")
    print(f"JV_EMPTY:        {len(jv_empty)}")
    print("")
    print("Wrote:", OUT)
    if len(jv_ok_db_empty) > 0 and len(jv_empty) == 0:
        print("=> 生データは揃い、jrvltsql 展開で TanOdds が落ちているレースが", len(jv_ok_db_empty), "件")


if __name__ == "__main__":
    main()
