"""再計算が止まる原因を、ブックを開かずに数える。

使い方:
    python calc-check.py ブック.xlsx

出るもの:
    計算方法が手動になっていないか。反復計算がONになっていないか。
    数式のつもりで文字列として入っているセル。再計算のたびに動く関数の数。

Python 3.9 以降。openpyxl が要る。
"""

from __future__ import annotations

import re
import sys

try:
    from openpyxl import load_workbook
except ImportError:
    print("openpyxl が要る。pip install openpyxl で入れる")
    raise SystemExit(2)

# 再計算のたびに必ず動く関数。多いとブックが重くなる
VOLATILE = ["TODAY", "NOW", "RAND", "RANDBETWEEN", "RANDARRAY",
            "OFFSET", "INDIRECT", "INFO", "CELL"]
FUNC_RE = {f: re.compile(r"\b%s\s*\(" % f) for f in VOLATILE}
MODE = {"auto": "自動", "manual": "手動", "autoNoTable": "データテーブル以外は自動"}


def main(argv: list[str]) -> int:
    if len(argv) < 2:
        print(__doc__)
        return 2
    try:
        sys.stdout.reconfigure(encoding="utf-8")
    except Exception:
        pass
    wb = load_workbook(argv[1])
    warn = []
    cp = wb.calculation
    mode = getattr(cp, "calcMode", None) or "auto"
    print("ブック全体")
    print("  計算方法 %s" % MODE.get(mode, mode))
    if mode == "manual":
        warn.append("計算方法が手動になっている。開いたExcel全体がこの設定になる")
    print("  開いたときに全部計算し直す %s"
          % ("する" if getattr(cp, "fullCalcOnLoad", False) else "しない"))
    it = bool(getattr(cp, "iterate", False))
    print("  反復計算 %s" % ("ON" if it else "OFF"))
    if it:
        warn.append("反復計算がONになっている。循環参照があっても止まらず、途中の数が入る")
    print("")

    total_f = 0
    total_str = 0
    vol = {f: 0 for f in VOLATILE}
    for ws in wb.worksheets:
        nf = ns = 0
        for row in ws.iter_rows():
            for c in row:
                v = c.value
                if c.data_type == "f" or (isinstance(v, str) and v.startswith("=")
                                          and c.data_type != "s"):
                    nf += 1
                    up = str(v).upper()
                    for f in VOLATILE:
                        if FUNC_RE[f].search(up):
                            vol[f] += 1
                elif c.data_type == "s" and isinstance(v, str) and v.startswith("="):
                    ns += 1
        total_f += nf
        total_str += ns
        print("%s  数式 %d / 文字列で入った数式らしきもの %d" % (ws.title, nf, ns))
        if ns:
            warn.append("%s に文字列のまま入った数式が %d 個ある。"
                        "表示形式を標準に戻すだけでは直らない" % (ws.title, ns))
    print("")
    used = {f: n for f, n in vol.items() if n}
    if used:
        print("再計算のたびに動く関数")
        for f, n in sorted(used.items(), key=lambda x: -x[1]):
            print("  %-12s %d か所" % (f, n))
        s = sum(used.values())
        if s >= 200:
            warn.append("再計算のたびに動く関数が %d か所ある。入力のたびに全部計算し直す" % s)
    else:
        print("再計算のたびに動く関数 なし")
    print("")
    print("数式の合計 %d / 文字列で入ったもの %d" % (total_f, total_str))
    print("")
    if warn:
        print("見るところ")
        for wline in warn:
            print("  " + wline)
    else:
        print("見るところ なし")
    return 0


if __name__ == "__main__":
    raise SystemExit(main(sys.argv))
