# 來源註記：這是 Codex 弄的。
"""教學用 OHLCV 稽核器；不取代交易所日曆與 vendor-specific 驗證。"""

from __future__ import annotations

import pandas as pd


REQUIRED_COLUMNS = ("open", "high", "low", "close", "volume")


def audit_ohlcv(frame: pd.DataFrame) -> list[str]:
    issues: list[str] = []
    missing = [column for column in REQUIRED_COLUMNS if column not in frame.columns]
    if missing:
        return [f"缺少欄位：{', '.join(missing)}"]
    if not isinstance(frame.index, pd.DatetimeIndex):
        issues.append("index 不是 DatetimeIndex")
        return issues
    if frame.index.tz is None:
        issues.append("timestamp 沒有 timezone")
    if frame.index.has_duplicates:
        issues.append(f"重複 timestamp：{int(frame.index.duplicated().sum())} 筆")
    if not frame.index.is_monotonic_increasing:
        issues.append("timestamp 未依序遞增")

    numeric = frame.loc[:, REQUIRED_COLUMNS].apply(pd.to_numeric, errors="coerce")
    if numeric.isna().any().any():
        issues.append(f"OHLCV 含缺值或非數字：{int(numeric.isna().sum().sum())} 格")
    if (numeric[["open", "high", "low", "close"]] <= 0).any().any():
        issues.append("價格含 0 或負值")
    if (numeric["volume"] < 0).any():
        issues.append(f"負 volume：{int((numeric['volume'] < 0).sum())} 筆")

    row_max = numeric[["open", "low", "close"]].max(axis=1)
    row_min = numeric[["open", "high", "close"]].min(axis=1)
    bad_high = numeric["high"] < row_max
    bad_low = numeric["low"] > row_min
    if bad_high.any():
        issues.append(f"high 小於 open/low/close：{int(bad_high.sum())} 筆")
    if bad_low.any():
        issues.append(f"low 大於 open/high/close：{int(bad_low.sum())} 筆")
    return issues


def _demo() -> None:
    index = pd.DatetimeIndex(
        ["2026-01-02 09:30", "2026-01-02 09:31", "2026-01-02 09:31"],
        tz="America/New_York",
    )
    deliberately_bad = pd.DataFrame(
        {
            "open": [100.0, 101.0, 102.0],
            "high": [101.0, 100.5, 103.0],
            "low": [99.5, 100.8, 101.0],
            "close": [100.5, 101.2, 102.5],
            "volume": [1_000, -5, 800],
        },
        index=index,
    )
    issues = audit_ohlcv(deliberately_bad)
    print("Audit issues:")
    for issue in issues:
        print(f"- {issue}")
    assert len(issues) == 4, f"預期 4 項 issue，實際 {len(issues)}"


if __name__ == "__main__":
    _demo()

