import { GRADE_THRESHOLDS } from "@/lib/config";
import type { Grade } from "@/lib/types";
/**
* ◎○△× の判定
*
* ★必ず丸める前の生値を渡すこと★
* 完了率 89.5% は、表示は「90%」だが判定は ○。
* 丸めてから判定すると ★無いはずの ◎ が生まれる★(CLAUDE.md 4.3 / D-020)。
* ★D-139 で境界が「以上」になり、事故の向きが逆になった★ —
* 旧規則では「90.4 を丸めると ◎ が消える」側だった。★どちらにせよ丸めた値で判定しない★。
*
* ★★境界はすべて「上のランクに入る」★★(ユーザー指示 2026-09-16 /
* ★D-139★。「◯、×は90%以上のように境はふくめてください」)。
* ★D-020b(境界はすべて下のランクに落ちる)は破棄★ — 90.0 は ◎、70.0 は ○。
* ★× の境界だけは変わらない★(「10%未満を×としてください」でユーザー確定)。
*
* | 判定 | 条件 |
* |---|---|
* | ◎ | 完了率 90%以上 |
* | ○ | 完了率 70%以上 |
* | △ | 完了率 10%以上 70%未満 |
* | × | 完了率 10%未満、または未実施 |
*
* 閾値は `lib/config` から読む(ハードコード禁止)。
*
* @param rate 清掃完了率(%)。未実施は null
*/
export function judgeGrade(rate: number | null): Grade {
if (rate === null) return "×";
if (rate >= GRADE_THRESHOLDS.excellent) return "◎";
if (rate >= GRADE_THRESHOLDS.good) return "○";
if (rate < GRADE_THRESHOLDS.poor) return "×";
return "△";
}
/**
* 表示用の丸め
*
* 小数第1位を四捨五入して整数にする(例: 91.4 → 91、89.5 → 90)。
* ★この値で judgeGrade を呼ばないこと★
*/
export function displayRate(rate: number | null): string {
if (rate === null) return "-";
return `${Math.round(rate)}%`;
}
/**
* 「問題あり」= 判定が △ または ×(★出発の有無を見ない版★)
*
* ★閾値を直書きしない★ — 判定は `judgeGrade()` が済ませているので、
* ここは「どの判定を問題とみなすか」だけを持つ。
*
* ★★`isProblemCompletion()`(`lib/db/clean-log-mapping.ts`)との関係★★ —
* あちらは ★完了率(生値)★ を入力に取り `rate === null || rate < GRADE_THRESHOLDS.good`
* で判定する。★判定結果は本関数と常に一致する★
* (`judgeGrade()` の「○ = 70%以上」より、△× ⟺ `null` または 70 未満。★D-139★)。
* ★入力が違うだけの同じ規則★なので、片方の閾値だけを動かさないこと。
* ★★本関数は「grade を既に持っている呼び出し側」用★★
* (`buildAreaCompletions()` は行ごとに `judgeGrade()` を通したあとの `grade` を持っている。
* そこから完了率に戻して `isProblemCompletion()` を呼ぶと、同じ値を2回別の関数で
* 判定することになる)。★`isProblemCompletion()` は連続日数の算出が使っており、
* あちらは grade を作らないまま完了率だけを見ている★。
*/
export function isProblemGrade(grade: Grade): boolean {
return grade === "△" || grade === "×";
}
/** 「問題あり」= △以下、または出発失敗 */
export function isProblem(input: {
departed: boolean;
completionRate: number | null;
}): boolean {
if (!input.departed) return true;
return isProblemGrade(judgeGrade(input.completionRate));
}