#!/usr/bin/env python3
"""
CSV文字化け防止スクリプト: UTF-8 → UTF-8 BOM付き変換
対象ディレクトリ内の全CSVを一括変換。Excelで文字化けなく開けるようになる。

使用方法:
  python3 convert_csv_to_utf8bom.py                # /var/www/html/ 内のCSVを変換
  python3 convert_csv_to_utf8bom.py /path/to/dir   # 任意ディレクトリを指定

サーバーでの実行例:
  cd /var/www/html
  python3 convert_csv_to_utf8bom.py
"""

import os
import sys
import shutil
from pathlib import Path
from datetime import datetime

# ===== 設定 =====
TARGET_DIR = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("/var/www/html")
BACKUP_DIR = TARGET_DIR / "_csv_backup"  # バックアップ保存先
DRY_RUN    = False  # True にすると変換せず確認だけ
# ================

def detect_and_read(filepath: Path):
    """複数エンコーディングを試みてCSVを読み込む。不正バイトはsurrogateescapeで保護。"""
    for enc in ("utf-8-sig", "utf-8", "cp932", "shift_jis"):
        try:
            # errors='surrogatepass' で一度試み、失敗したら surrogateescape にフォールバック
            try:
                text = filepath.read_text(encoding=enc, errors="strict")
                return text, enc
            except UnicodeDecodeError:
                continue
        except (ValueError, LookupError):
            continue

    # どのエンコーディングでも厳密読み込みが失敗した場合:
    # utf-8 + surrogateescape で読み込み、不正バイトを安全に置換して返す
    raw = filepath.read_bytes()
    for enc in ("utf-8", "cp932", "shift_jis", "latin-1"):
        try:
            text = raw.decode(enc, errors="surrogateescape")
            # surrogate文字を U+FFFD（代替文字）に置換して安全なstrにする
            text = text.encode("utf-8", errors="replace").decode("utf-8")
            return text, f"{enc}+replace"
        except Exception:
            continue

    raise ValueError(f"読み込み失敗: {filepath}")

def convert_file(filepath: Path, backup_dir: Path):
    """1ファイルをUTF-8 BOM付きに変換（元ファイルをバックアップ）"""
    text, src_enc = detect_and_read(filepath)

    # すでにBOM付きUTF-8なら何もしない
    if src_enc == "utf-8-sig":
        return "skip", src_enc

    if DRY_RUN:
        return "dry_run", src_enc

    # バックアップ
    rel = filepath.relative_to(filepath.parent.parent) if backup_dir.parent != filepath.parent else filepath.name
    backup_path = backup_dir / filepath.name
    backup_dir.mkdir(parents=True, exist_ok=True)
    shutil.copy2(filepath, backup_path)

    # UTF-8 BOM付きで上書き保存
    filepath.write_text(text, encoding="utf-8-sig")
    return "converted", src_enc

def main():
    if not TARGET_DIR.exists():
        print(f"[ERROR] ディレクトリが見つかりません: {TARGET_DIR}")
        sys.exit(1)

    csv_files = sorted(TARGET_DIR.glob("*.csv"))
    if not csv_files:
        print(f"[INFO] CSVファイルが見つかりませんでした: {TARGET_DIR}")
        sys.exit(0)

    print(f"{'[DRY RUN] ' if DRY_RUN else ''}変換開始: {TARGET_DIR}")
    print(f"対象ファイル数: {len(csv_files)}")
    print(f"バックアップ先: {BACKUP_DIR}")
    print("-" * 60)

    stats = {"converted": 0, "skip": 0, "error": 0}

    for fp in csv_files:
        try:
            result, enc = convert_file(fp, BACKUP_DIR)
            safe_name = fp.name.encode("utf-8", errors="replace").decode("utf-8")
            if result == "converted":
                print(f"  [変換] {safe_name}  ({enc} → utf-8-sig)")
                stats["converted"] += 1
            elif result == "skip":
                print(f"  [スキップ] {safe_name}  (すでにBOM付きUTF-8)")
                stats["skip"] += 1
            elif result == "dry_run":
                print(f"  [DRY RUN] {safe_name}  ({enc} → utf-8-sig になる予定)")
                stats["converted"] += 1
        except Exception as e:
            try:
                safe_name = fp.name.encode("utf-8", errors="replace").decode("utf-8")
                safe_err  = str(e).encode("utf-8", errors="replace").decode("utf-8")
                print(f"  [ERROR] {safe_name}: {safe_err}")
            except Exception:
                print(f"  [ERROR] (表示不可): {type(e).__name__}")
            stats["error"] += 1

    print("-" * 60)
    print(f"完了: 変換={stats['converted']}件, スキップ={stats['skip']}件, エラー={stats['error']}件")
    if not DRY_RUN and stats["converted"] > 0:
        print(f"バックアップ保存済み: {BACKUP_DIR}")

if __name__ == "__main__":
    main()
