import csv
import io
from collections.abc import Iterable, Mapping, Sequence

UTF8_BOM = "\ufeff"
DANGEROUS_CSV_PREFIXES = ("=", "+", "-", "@", "\t", "\r")


def serialize_csv(
    headers: Sequence[str],
    rows: Iterable[Mapping[str, object | None]],
    *,
    bom: bool = True,
) -> str:
    """ヘッダー順にCSV文字列を生成する。"""
    output = io.StringIO()
    writer = csv.DictWriter(output, fieldnames=headers, quoting=csv.QUOTE_ALL)
    writer.writeheader()

    for row in rows:
        writer.writerow({header: _escape_csv_injection_value(row.get(header)) for header in headers})

    content = output.getvalue()
    if bom:
        return UTF8_BOM + content
    return content


def _escape_csv_injection_value(value: object | None) -> str:
    """CSVインジェクション対策として危険な先頭文字をエスケープする。"""
    text = "" if value is None else str(value)
    if text and text[0] in DANGEROUS_CSV_PREFIXES:
        return "'" + text
    return text
