# -*- coding: utf-8 -*-
"""第2回 PDF「確認事項（第2回）」の自己点検。

原稿 (docs/handoff/TeamWorks確認事項_第2回_20260903.md) の文言が
すべて PDF に含まれているか、フォントが日本語フォントで埋め込まれているかを見る。
PDF のパスは第1引数で渡せる。省略時はこのスクリプトと同じディレクトリ。

check_ddl_review_pdf.py（第1回用）と同じ構成。
"""
import os
import re
import sys

from pypdf import PdfReader

PDF = (sys.argv[1] if len(sys.argv) > 1 else
       os.path.join(os.path.dirname(os.path.abspath(__file__)),
                    "DWatch_確認事項_第2回_20260903.pdf"))
if not os.path.exists(PDF):
    raise SystemExit("PDF が見つからない: %s\n"
                     "先に make_teamworks_qa2_pdf.py を実行するか、"
                     "PDF のパスを第1引数で渡すこと。" % PDF)

raw = open(PDF, "rb").read()
reader = PdfReader(PDF)
pages = [p.extract_text() for p in reader.pages]
text = "\n".join(pages)
flat = re.sub(r"\s+", "", text)

print("== 1. ページ数とサイズ ==")
print("ページ数:", len(reader.pages), " サイズ:", len(raw), "bytes",
      "(%.1f KB)" % (len(raw) / 1024))
print("抽出文字数:", len(text))

print("\n== 2. フォント埋め込み ==")
print("/FontFile2 出現回数:", raw.count(b"/FontFile2"))
biz = sorted(set(re.findall(rb"/([A-Z]{6}\+[A-Za-z0-9\-]*BIZ[A-Za-z0-9\-]*)", raw)))
print("BIZUDGothic を含むフォント名:", biz)
if not biz:
    print("  ★NG★ BIZ UDGothic が埋め込まれていない")
# 中国語向けフォントが混ざっていないこと（CLAUDE.md §0-3）
CN = [b"SimSun", b"SimHei", b"MicrosoftYaHei", b"MSYaHei",
      b"NotoSansSC", b"NotoSerifSC", b"SourceHanSansSC", b"SourceHanSerifSC"]
hit = [n.decode() for n in CN if n in raw]
print("中国語向けフォント:", hit if hit else "混入なし")
if hit:
    print("  ★NG★ 中国語向けフォントが混ざっている")

print("\n== 3. 原稿の全項目が含まれるか ==")
must = [
    # 表題・前文
    "確認事項（第2回）", "TeamWorks（DB開発側）", "作成日:2026-09-03",
    "DFARobotics", "clean_watch_20260902.sql",
    "第1回「実DDL照合結果と確認事項」に続く確認事項です",
    "取得元が特定できなかった項目",
    "Q1とQ2は、解決しないと該当画面を実装できません",
    "すでに存在するのに当方が見落としている可能性",
    # サマリ表
    "A.実装が止まる", "取得元が特定できず、該当画面を実装できない",
    "B.定義の確認", "取り違えると誤った値を表示する",
    "C.ご回答を受けた追加確認", "第1回のご回答の反映にあたり確認したい点",
    "D.継続", "第1回でお願いしたもののうち、まだ動きがないもの",
    "回答不要",
    # Q1 前文と候補テーブル
    "Q1D-Security画面のデータ取得元",
    "F-06D-Secログ", "F-07D-Secアラート",
    "これらに直接対応する名前のテーブルが見当たりませんでした",
    "ds_devices/ds_alertsのような名前のテーブルはありません",
    "controls", "control_reports", "control_error_histories",
    "security_statuses", "security_status_histories",
    "serial_number", "verified_at", "deleted_at", "occurred_at",
    # Q1-1
    "Q1-1D-Secログ画面（デバイス一覧）",
    "controls.serial_number", "facilities.facility_name",
    "通信状態", "最終通信日時", "control_reports.reported_at",
    "定期的な生存報告という理解で合っていますか",
    "デバイスごとの備考を持つカラムはありますか",
    # Q1-2
    "Q1-2controls.statusのコード値定義をご教示ください",
    "tinyintunsigned", "取りうる値と意味が分かりません",
    "「異常」はcontrols.statusから判定できますか",
    "からの経過時間で判定する想定でよいですか",
    "何分（何時間）で通信断とみなすかの運用上の基準はありますか",
    "6時間以上通信なし", "根拠のない仮の値です",
    # Q1-3
    "Q1-3D-Secアラート画面",
    "control_error_historiesが実体だと考えています",
    "control_error_histories.occurred_at",
    "sections.section_name", "controls.serial_number",
    "control_error_histories.status", "control_error_histories.comment",
    "responded_at/resolved_at",
    "アラート種別", "セキュリティ解除", "通信断",
    "詳細（アラートの本文）",
    "statusが種別を兼ねているのでしょうか",
    "セキュリティ復帰失敗", "検知側が書く詳細とは別だと考えています",
    # Q1-4
    "Q1-4security_statuses/security_status_historiesの位置づけ",
    "既定値1", "既定値が1である理由も",
    "セキュリティの現在状態を持つもの",
    "解除されたまま戻っていない",
    "security_statusesの変更履歴という理解で合っていますか",
    # Q2
    "Q2事前チェック（60分前/30分前）時点のロボット状態",
    "清掃開始の60分前と30分前のロボットの状態",
    "待機中", "充電中", "ハンドルアップ", "緊急停止", "オフライン", "セキュリティ有効",
    "後から参照できるテーブルが見当たりません",
    "robots.status", "section_robot_statuses.robot_status",
    "clean_timetables.precheck", "robot_instruct_histories",
    "過去の値は残りません", "現時点では未確定",
    "0:本清掃/1:1回目/2:2回目",
    "そのときの状態がどこにもありません",
    "指示の履歴であり、状態のスナップショットではないと理解しています",
    "どのテーブルのどのカラムに記録されますか",
    "現状では画面に出せない", "P2以降に送るか",
    "robots.status（smallint）のコード値定義をご教示ください",
    "1:清掃中", "-1:清掃完了(セキュリティ無効)", "statusのほうは未回答",
    "配下のrobots.statusから算出するルールは決まりましたか",
    # Q3
    "Q3clean_schedules.weeksの曜日の並び順",
    "varchar(7)", "曜日ごとの実施有無を7桁で持つ理解です",
    "左から順に「日・月・火・水・木・金・土」で合っていますか",
    "「月・火・水・木・金・土・日」",
    "並び順を取り違えると別の曜日として表示されます",
    "1000000を日曜と読むか月曜と読むか",
    "1が実施・0が非実施で合っていますか",
    # Q4
    "Q4ステータス系カラムのコード値定義（まとめ）",
    "clean_statuses.clean_status", "残りは次の7つです",
    "robot_error_reports.status", "smallint", "なし",
    "「未対応/対応中/完了」の3状態で扱う想定です",
    "control_error_histories.statusと同じ体系でしょうか",
    # C 追加確認
    "C.第1回のご回答を受けた追加確認（3件）",
    "ご回答をいただきありがとうございました。すべて反映しています",
    "C-1A-3（contact系の型）はDDL側を修正いただけますか",
    "contact_typeは両テーブルともtinyint.",
    "emailは両テーブルともvarchar(100)で定義してください.",
    "まだ食い違ったまま",
    "customer_contacts.contact_type", "bigintunsigned",
    "facility_contacts.email", "varchar(255)",
    "実DDLを正としてスキーマを機械的に取り込む運用",
    "DDL側を直していただく前提",
    "DDL側を修正いただけるという理解で合っていますか",
    "反映の目安時期をお知らせください",
    "C-2B-1/B-2/B-3の修正を含むDDLをいつ頂けますか",
    "外部キー3本（B-1）・インデックス1本（B-2）・timestampのDEFAULT（B-3）",
    "修正が入るたびにDDLを頂き直す必要があります",
    "DDLを再度いただけますか",
    "A-2のauto_incrementも同じ修正に含まれる",
    "AUTO_INCREMENTが付いていません",
    "C-3APIが画面に返すIDはerror_idで確定してよいですか",
    "とりあえずerror_idを主キーと位置付け対応する予定です",
    "アルファベットを含んでいるので,数値型にすることはできません",
    "PrecheckItem.idをnumberからstringへ変更し、値にはerror_idを入れる",
    "この方針で問題ないですか",
    "運用を以て最終的な回答を求める",
    "後でid側に一意制約が付く可能性はありますか",
    "切り替えられる形で実装しておきます",
    # D 継続
    "D.継続（1件）",
    "まだ動きがないもの",
    "DB更新用アカウントの発行",
    "のみUPDATE可",
    # 参考: 第1回のご回答
    "参考:第1回のご回答で解決した事項",
    "A-1", "A-2", "A-3", "B-1", "B-2", "B-3",
    "error_idを主キーと位置付けて対応する予定",
    "id もerror_idも英字を含むため数値型にはできない".replace(" ", ""),
    "number→string",
    "PATCH/prechecks/{id}/statusの設計に着手できます",
    "採番はデータベース。auto_incrementで採番する",
    "元テーブルと履歴テーブルは1対1",
    "ビューアーはこの2テーブルを参照しないため、実装への影響はありません",
    "反映後のDDLをdbpullで取り込みます",
    "制約（外部キー3本）は後から追加する",
    "孤児行を想定した実装はやめます",
    "インデックスも後から追加する。現状は20施設弱",
    "当面の性能懸念は小さい",
    "指摘どおりDEFAULTを追加する",
    "created_at:新規作成時、updated_at:新規作成時／更新時",
    # 参考: 受領DDL
    "参考:受領DDLの確認により解決していた事項",
    "既に満たされていました", "対応不要です",
    "responded_at/resolved_atの",
    "両テーブルに既に存在します",
    "datetimeNULL可", "綴りもresponded_atで正しくなっています",
    "D-Secアラートへの書き込み可否",
    "既に更新可4カラムに含まれています",
    "追加の権限は不要という理解です",
    # 当方判断
    "参考:当方で判断して進める項目（回答不要）",
    "clean_schedulesにserial_numberが無い件",
    "sections経由でrobotsを引きます",
    "1清掃エリア=1ロボット", "一意に定まります",
    "clean_schedules.end_timeが24〜29時を取る件",
    "画面表示は「翌04:30」の形にします",
    # 末尾
    "D-Security画面と清掃詳細ログのデータ取得層に着手できます",
    "表示が誤る箇所のため早めにご確認いただけると助かります",
    "とくにC-2（修正版DDLの受領時期）の目安をいただけると助かります",
]

miss = [m for m in must if re.sub(r"\s+", "", m) not in flat]
print("検査項目:", len(must), " 欠落:", len(miss))
for m in miss:
    print("  ★欠落★", m)

print("\n== 4. 判定 ==")
ok = (not miss) and bool(biz) and (not hit)
print("結果:", "OK" if ok else "NG")
sys.exit(0 if ok else 1)
