/**
* GET /reports/facility-performance/export の CSV 組み立て(D-163)。
*
* ルート(`app/api/reports/facility-performance/export/route.ts`)から切り出してある。
* 理由は清掃詳細ログ(`lib/clean-logs/csv.ts`)と同じで、★テストを書くため★
* (Next.js の App Router は `route.ts` から HTTP メソッド以外を export することを想定していない)。
*
* ★形式は清掃詳細ログと同じ★: UTF-8 BOM・CRLF・最後も CRLF・ヘッダ行あり・
* ★全セルを `csvCell()`(数式インジェクション対策 D-125)に通す★(`csvDocument()` の中で行う)。
* ★画面の言語によらず日本語★(CSV は言語を受け取らない。指示書AS D11)。
*/
import { csvDocument, csvRate } from "@/lib/csv";
import { prevPeriodLabel } from "@/lib/format/period";
import {
diffText,
FACILITY_STATUS_LABEL,
overallDiff,
} from "@/lib/reports/facility-performance-labels";
import type { ReportQuery } from "@/lib/reports/query";
import type { FacilityPerformance, Period } from "@/lib/types";
/**
* ヘッダ行の見出し。
*
* ★列は画面(`FacilityPerformanceTable`)の9列と同じ順に、施設名の直後へシリアルを足したもの★(D-163)。
* 列順を画面と変えると、突き合わせる人が毎回並べ替えることになる(清掃詳細ログと同じ考え方。指示書AO 段階3)。
* 「前◯」は `prevPeriodLabel()` の日本語(前週 / 前月 / 前年)で、画面の列見出しと同じ出所。
*/
export function facilityPerformanceCsvHeader(period: Period): string[] {
const prev = prevPeriodLabel(period);
return [
"施設名",
"シリアル",
"稼働率",
`稼働率(${prev})`,
"出発成功率",
"完了率",
`完了率(${prev})`,
"エラー率",
"変化",
"ステータス",
];
}
/**
* 施設別パフォーマンスの CSV 本体を組み立てる。
*
* - ★`items` はルートが絞り込み・並べ替えまで済ませた配列★。ここで並べ替えない(画面と同じ順で出す)
* - ★率は `csvRate()`(四捨五入の整数+`%`、★null は空欄★)★。★丸めは表示のみ★(CLAUDE.md §4.3)。
* ★null を空欄にするのはユーザー指示 2026-10-06「値がないとこは空欄で」(D-164)★ —
* 以前は `displayRate()` の `-` を出しており、`csvCell()` の数式対策で `'-` になって Excel に見えていた
* - ★ステータスは API の `status` を表示名にするだけ★。ここで判定し直さない(CLAUDE.md §9.6)
* - 「変化」は画面と同じ `overallDiff()` / `diffText()`(`lib/reports/facility-performance-labels.ts`)
* - ★`csvCell()` はここでは呼ばない★ — `csvDocument()` が全セルに通す(関門を1つにする。D-125)
*/
export function buildFacilityPerformanceCsv(
items: readonly FacilityPerformance[],
period: Period
): string {
return csvDocument(
facilityPerformanceCsvHeader(period),
items.map((item) => [
item.facility_name,
item.serial_number,
csvRate(item.operation_rate.value),
csvRate(item.operation_rate.prev),
csvRate(item.launch_rate.value),
csvRate(item.completion_rate.value),
csvRate(item.completion_rate.prev),
csvRate(item.error_rate.value),
diffText(overallDiff(item)),
FACILITY_STATUS_LABEL[item.status],
])
);
}
/**
* ダウンロード時のファイル名(`Content-Disposition` の `filename`)。
*
* ★ASCII のみ★(`filename="..."` に日本語を入れると、ブラウザによって文字化けする)。
* ★値は parse 済みの数値から組み立てる★(生のクエリ文字列を入れない。ヘッダへの注入を防ぐ)。
* ★月は2桁★。例:
* - weekly → `facility-performance_weekly_2026-10-w1.csv`
* - monthly → `facility-performance_monthly_2026-09.csv`
* - yearly → `facility-performance_yearly_2025.csv`(年次は month / week を持たない。CLAUDE.md §4.7)
*/
export function facilityPerformanceCsvFilename(
q: Pick<ReportQuery, "period" | "year" | "month" | "week">
): string {
const mm = String(q.month).padStart(2, "0");
const key =
q.period === "yearly"
? `${q.year}`
: q.period === "monthly"
? `${q.year}-${mm}`
: `${q.year}-${mm}-w${q.week}`;
return `facility-performance_${q.period}_${key}.csv`;
}