"""
UniversalMigrator 設計書生成スクリプト
ソースコードを解析し、design_document.xlsx を生成する。
"""
import os
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter

# ── スタイル定義 ──────────────────────────────────────────────
HEADER_FONT = Font(name="Meiryo", size=10, bold=True, color="FFFFFF")
HEADER_FILL = PatternFill(start_color="2C3E50", end_color="2C3E50", fill_type="solid")
SUB_HEADER_FILL = PatternFill(start_color="3498DB", end_color="3498DB", fill_type="solid")
BODY_FONT = Font(name="Meiryo", size=10)
CODE_FONT = Font(name="Consolas", size=9)
WRAP_ALIGN = Alignment(wrap_text=True, vertical="top")
CENTER_ALIGN = Alignment(horizontal="center", vertical="center", wrap_text=True)
THIN_BORDER = Border(
    left=Side(style="thin"), right=Side(style="thin"),
    top=Side(style="thin"), bottom=Side(style="thin"),
)


def style_header_row(ws, row, ncols):
    for c in range(1, ncols + 1):
        cell = ws.cell(row=row, column=c)
        cell.font = HEADER_FONT
        cell.fill = HEADER_FILL
        cell.alignment = CENTER_ALIGN
        cell.border = THIN_BORDER


def style_body(ws, start_row, end_row, ncols):
    for r in range(start_row, end_row + 1):
        for c in range(1, ncols + 1):
            cell = ws.cell(row=r, column=c)
            cell.font = BODY_FONT
            cell.alignment = WRAP_ALIGN
            cell.border = THIN_BORDER


def auto_width(ws, ncols, min_w=12, max_w=60):
    for c in range(1, ncols + 1):
        max_len = min_w
        for row in ws.iter_rows(min_col=c, max_col=c, values_only=False):
            for cell in row:
                if cell.value:
                    lines = str(cell.value).split("\n")
                    line_max = max(len(l) for l in lines)
                    max_len = max(max_len, min(line_max + 2, max_w))
        ws.column_dimensions[get_column_letter(c)].width = max_len


# ═══════════════════════════════════════════════════════════════
# Sheet 1: 機能一覧表
# ═══════════════════════════════════════════════════════════════
def build_feature_list(wb):
    ws = wb.active
    ws.title = "1_機能一覧表"

    headers = ["機能ID", "機能名", "機能概要", "対象ユーザー", "実行モード", "対応モジュール"]
    ws.append(headers)
    style_header_row(ws, 1, len(headers))

    features = [
        ["F-001", "コード変換（単一ファイル）",
         "Claude APIを使用して、ソースコードを指定された変換元言語から変換先言語に翻訳する。\n"
         "変換時にはターゲット言語のイディオムに従い、コメントや構造を保持する。",
         "開発者 / テック企業", "GUI / CLI",
         "src/migrator.py (Migrator.migrate_code)"],

        ["F-002", "フォルダ一括変換",
         "入力フォルダ内の全対象ファイルを再帰的に走査し、サブフォルダ構造を維持しながら一括変換する。\n"
         "対象ファイルは変換元言語の拡張子で自動フィルタリングされる。",
         "開発者 / テック企業", "GUI / CLI",
         "src/migrator.py (Migrator.migrate_folder)"],

        ["F-003", "66言語サポート",
         "ABAP, Ada, C, C++, C#, COBOL, Go, Java, JavaScript, Kotlin, Python, Rust, TypeScript, VBA等\n"
         "66種類のプログラミング言語を変換元・変換先として選択可能。各言語の拡張子を自動認識する。",
         "全ユーザー", "GUI / CLI",
         "src/languages.py (LANGUAGES辞書, 66エントリ)"],

        ["F-004", "レジューム機能",
         "SHA-256ハッシュで変換済みファイルを管理。中断後の再実行時に変換済みファイルをスキップし、\n"
         "途中から再開する。ソースが変更された場合は再変換する。状態は.migration_state.jsonに保存。",
         "大規模プロジェクト変換時の全ユーザー", "GUI / CLI",
         "src/resume_state.py (MigrationState, FileEntry)"],

        ["F-005", "指数バックオフリトライ",
         "429 (RateLimit) / Timeout / 529 (Overloaded) エラー発生時に、\n"
         "指数バックオフ+ジッター付きで最大5回自動リトライする。初期待機2秒、最大待機120秒。",
         "全ユーザー（自動）", "GUI / CLI",
         "src/migrator.py (Migrator._call_api)"],

        ["F-006", "出力サニタイズ",
         "AIが出力に付加するMarkdownコードフェンス（```...```）、自然言語のプリアンブル/エピローグ、\n"
         "多言語の解説文を自動除去し、純粋なソースコードのみを抽出する。7言語の自然言語パターンに対応。",
         "全ユーザー（自動）", "GUI / CLI",
         "src/sanitizer.py (sanitize_code_output)"],

        ["F-007", "コンテキスト注入",
         "定数/設定/型定義ファイルを優先的に先行変換し、その変換結果を後続ファイルのプロンプトに注入。\n"
         "これにより後続ファイルの変換精度を向上させる。最大5ファイルまでコンテキストとして使用。",
         "全ユーザー（自動）", "GUI / CLI",
         "src/ecosystem.py (PRIORITY_PATTERNS, get_priority_files)"],

        ["F-008", "エコシステムファイル変換",
         "requirements.txt → package.json, pom.xml → requirements.txt 等、\n"
         "プロジェクトの設定ファイル・ビルドファイルを対象エコシステムの等価ファイルに自動変換する。\n"
         "14パターンのエコシステムマッピング（python⇔node, python⇔jvm, python⇔go 等）に対応。",
         "全ユーザー", "GUI / CLI",
         "src/ecosystem.py (ECOSYSTEM_MAP, get_ecosystem_mappings)"],

        ["F-009", "GUI（8言語UI）",
         "tkinterベースのGUIアプリケーション。MVC（Model-View-Controller）パターンで実装。\n"
         "日本語/English/中文/한국어/Español/Français/Deutsch/Português の8言語でUI表示を切替可能。\n"
         "フォルダ選択ダイアログ、プログレスバー、リアルタイムログ表示を提供。",
         "全ユーザー", "GUI",
         "src/gui_view.py, src/gui_controller.py, src/gui_model.py, src/i18n.py"],

        ["F-010", "CLIモード",
         "コマンドライン引数による非対話的な変換実行。CI/CDパイプラインやバッチ処理に対応。\n"
         "argparseによるオプション解析。--list-languages で対応言語一覧を表示可能。",
         "DevOps / 自動化担当者", "CLI",
         "main.py (run_cli, list_languages)"],

        ["F-011", "言語自動検出",
         "ファイル拡張子からプログラミング言語を自動判定する補助機能。\n"
         "66言語の拡張子マッピングに基づいて判定する。",
         "全ユーザー（内部利用）", "GUI / CLI",
         "src/languages.py (detect_language_by_ext)"],

        ["F-012", "EXEビルド",
         "PyInstallerを使用してWindows/macOS/Linux用のスタンドアロン実行ファイルを生成。\n"
         "Python環境不要で動作するポータブルバイナリを出力する。",
         "配布担当者", "ビルド時",
         "build.bat, build.sh"],
    ]

    for row_data in features:
        ws.append(row_data)

    style_body(ws, 2, len(features) + 1, len(headers))
    auto_width(ws, len(headers))
    ws.column_dimensions["C"].width = 70
    ws.column_dimensions["F"].width = 50


# ═══════════════════════════════════════════════════════════════
# Sheet 2: API仕様書
# ═══════════════════════════════════════════════════════════════
def build_api_spec(wb):
    ws = wb.create_sheet("2_API仕様書")

    # セクション1: 外部API（Claude API）
    ws.append(["外部API呼び出し仕様（Anthropic Claude API）"])
    ws.merge_cells("A1:G1")
    ws["A1"].font = Font(name="Meiryo", size=12, bold=True, color="FFFFFF")
    ws["A1"].fill = SUB_HEADER_FILL

    headers = ["API ID", "呼び出し先", "メソッド", "入力パラメータ", "出力", "リトライ条件", "備考"]
    ws.append(headers)
    style_header_row(ws, 2, len(headers))

    api_rows = [
        ["API-001", "Anthropic Messages API\n(messages.create)",
         "POST /v1/messages",
         "model: str (デフォルト: claude-sonnet-4-20250514)\n"
         "max_tokens: 16384\n"
         "system: str (SYSTEM_PROMPT)\n"
         "messages: [{role: 'user', content: str}]",
         "response.content[0].text\n(変換後のソースコード文字列)",
         "429 RateLimitError → リトライ\n"
         "APITimeoutError → リトライ\n"
         "529 Overloaded → リトライ\n"
         "最大5回、指数バックオフ(2s〜120s)+ジッター",
         "APIキーは引数または環境変数ANTHROPIC_API_KEYから取得"],

        ["API-002", "Anthropic Messages API\n(エコシステムファイル用)",
         "POST /v1/messages",
         "model: str\n"
         "max_tokens: 16384\n"
         "system: str (ECOSYSTEM_SYSTEM_PROMPT)\n"
         "messages: [{role: 'user', content: str}]",
         "response.content[0].text\n(変換後の設定ファイル内容)",
         "API-001と同一のリトライロジック",
         "プロジェクトファイル変換専用のシステムプロンプトを使用"],
    ]

    for r in api_rows:
        ws.append(r)
    style_body(ws, 3, 2 + len(api_rows), len(headers))

    # セクション2: 内部メソッドインターフェース
    row_offset = 2 + len(api_rows) + 2
    ws.cell(row=row_offset, column=1, value="内部メソッドインターフェース（公開API）")
    ws.merge_cells(start_row=row_offset, start_column=1, end_row=row_offset, end_column=7)
    ws.cell(row=row_offset, column=1).font = Font(name="Meiryo", size=12, bold=True, color="FFFFFF")
    ws.cell(row=row_offset, column=1).fill = SUB_HEADER_FILL

    headers2 = ["メソッドID", "クラス.メソッド名", "所属モジュール", "引数", "戻り値", "処理概要", "例外"]
    row_offset += 1
    for c, h in enumerate(headers2, 1):
        ws.cell(row=row_offset, column=c, value=h)
    style_header_row(ws, row_offset, len(headers2))

    methods = [
        ["M-001", "Migrator.__init__",
         "src/migrator.py",
         "api_key: str = ''\nmodel: str = DEFAULT_MODEL\nmax_retries: int = 5",
         "None",
         "Anthropicクライアントを初期化。api_keyが空の場合は環境変数を使用する。",
         "anthropic.AuthenticationError（APIキー未設定時）"],

        ["M-002", "Migrator.migrate_code",
         "src/migrator.py",
         "source_code: str\nsource_lang: str\ntarget_lang: str\n"
         "extra_instructions: str = ''\ncontext_snippets: list[tuple[str,str]] | None",
         "str（変換後のソースコード）",
         "単一のソースコードを変換する。プロンプトを構築し、Claude APIを呼び出し、出力をサニタイズして返す。",
         "RateLimitError, APITimeoutError（リトライ上限超過時）"],

        ["M-003", "Migrator.migrate_ecosystem_file",
         "src/migrator.py",
         "source_content: str\nsource_filename: str\ntarget_filename: str\n"
         "source_lang: str\ntarget_lang: str",
         "str（変換後の設定ファイル内容）",
         "エコシステムファイル（requirements.txt等）を対象言語の等価ファイルに変換する。",
         "API呼び出しエラー"],

        ["M-004", "Migrator.migrate_folder",
         "src/migrator.py",
         "input_dir: str, output_dir: str\n"
         "source_lang: str, target_lang: str\n"
         "source_extensions: list[str]\ntarget_extension: str\n"
         "extra_instructions: str = ''\n"
         "on_progress: Callable | None\non_log: Callable | None\n"
         "cancel_check: Callable | None\nencoding: str = 'utf-8'\n"
         "enable_resume: bool = True\nenable_context: bool = True\n"
         "enable_ecosystem: bool = True",
         "dict {\n  success: int,\n  errors: int,\n  skipped: int,\n  resumed: int,\n  details: list\n}",
         "フォルダ内の全対象ファイルを一括変換する。\n"
         "Phase 1: エコシステムファイル変換\n"
         "Phase 2: ソースコードファイル変換（優先度順）\n"
         "各フェーズでレジューム/コンテキスト注入/キャンセルチェックを実行。",
         "個別ファイルエラーはcatchしてdetailsに記録"],

        ["M-005", "sanitize_code_output",
         "src/sanitizer.py",
         "text: str\ntarget_lang_hint: str = ''",
         "str（サニタイズ済みコード）",
         "AI出力からMarkdownフェンス、自然言語プリアンブル/エピローグを除去し純粋なコードを返す。\n"
         "5段階処理: 空白除去→フェンス除去→プリアンブル除去→エピローグ除去→行末正規化",
         "なし"],

        ["M-006", "load_state / save_state",
         "src/resume_state.py",
         "output_dir: str\n(save_stateはstate: MigrationStateも必要)",
         "MigrationState / None",
         "出力ディレクトリの.migration_state.jsonからレジューム状態を読み込み/保存する。\n"
         "JSONが壊れている場合は空のMigrationStateを返す。",
         "OSError（ファイルアクセスエラー時）"],

        ["M-007", "file_hash",
         "src/resume_state.py",
         "filepath: str\nencoding: str = 'utf-8'",
         "str（SHA-256ハッシュ16進数）",
         "ファイル内容のSHA-256ハッシュを計算する。文字エンコーディングを考慮して読み込む。",
         "OSError / UnicodeDecodeError"],

        ["M-008", "get_ecosystem_mappings",
         "src/ecosystem.py",
         "source_lang: str\ntarget_lang: str",
         "dict {source_filename: target_filename}",
         "言語ペアに対応するエコシステムファイルマッピングを返す。\n"
         "言語名をグループ（python, node, jvm, go, rust等）に変換してからルックアップ。",
         "なし（該当なしの場合空dict）"],

        ["M-009", "sort_files_by_priority",
         "src/ecosystem.py",
         "filepaths: list[str]",
         "list[str]（優先度順のファイルパス）",
         "types/constants/config等の優先パターンに基づいてファイルをソートする。\n"
         "15種類のパターン（types, interfaces, models, schema, constants, config等）で判定。",
         "なし"],

        ["M-010", "I18n.t",
         "src/i18n.py",
         "key: str\n**kwargs（format用パラメータ）",
         "str（翻訳済み文字列）",
         "現在のUI言語に基づいて翻訳文字列を返す。キーが見つからない場合はenのフォールバック。\n"
         "8言語（en/ja/zh/ko/es/fr/de/pt）、各67キーに対応。",
         "なし"],
    ]

    for i, m in enumerate(methods):
        r = row_offset + 1 + i
        for c, v in enumerate(m, 1):
            ws.cell(row=r, column=c, value=v)

    style_body(ws, row_offset + 1, row_offset + len(methods), len(headers2))
    auto_width(ws, len(headers2))
    ws.column_dimensions["D"].width = 45
    ws.column_dimensions["E"].width = 35
    ws.column_dimensions["F"].width = 55


# ═══════════════════════════════════════════════════════════════
# Sheet 3: テーブル定義書（データ構造定義）
# ═══════════════════════════════════════════════════════════════
def build_table_definition(wb):
    ws = wb.create_sheet("3_データ構造定義書")

    ws.append(["本プロジェクトはRDBを使用しないため、永続化ファイルとインメモリデータ構造を定義する。"])
    ws.merge_cells("A1:G1")
    ws["A1"].font = Font(name="Meiryo", size=10, italic=True, color="2C3E50")

    # ── .migration_state.json ──
    row = 3
    ws.cell(row=row, column=1, value="ファイル定義: .migration_state.json（レジューム状態）")
    ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=7)
    ws.cell(row=row, column=1).font = Font(name="Meiryo", size=11, bold=True, color="FFFFFF")
    ws.cell(row=row, column=1).fill = SUB_HEADER_FILL

    row += 1
    headers = ["フィールド名", "データ型", "必須", "一意", "デフォルト", "論理名", "説明"]
    for c, h in enumerate(headers, 1):
        ws.cell(row=row, column=c, value=h)
    style_header_row(ws, row, len(headers))

    json_fields = [
        ["source_lang", "string", "Yes", "No", '""', "変換元言語", "変換元のプログラミング言語名（例: 'Python'）"],
        ["target_lang", "string", "Yes", "No", '""', "変換先言語", "変換先のプログラミング言語名（例: 'Rust'）"],
        ["files", "object", "Yes", "No", "{}", "ファイルエントリ集合", "キー: 相対パス、値: FileEntryオブジェクト"],
        ["files.<rel_path>.source_hash", "string", "Yes", "No", '""', "ソースハッシュ", "ソースファイルのSHA-256ハッシュ（16進64文字）"],
        ["files.<rel_path>.source_path", "string", "Yes", "No", '""', "ソースパス", "変換元ファイルの絶対パス"],
        ["files.<rel_path>.output_path", "string", "Yes", "No", '""', "出力パス", "変換先ファイルの絶対パス"],
        ["files.<rel_path>.status", "string", "Yes", "No", '""', "ステータス", "'ok' または 'error'"],
        ["files.<rel_path>.timestamp", "string", "No", "No", '""', "タイムスタンプ", "変換完了時のISO 8601形式タイムスタンプ"],
    ]

    for i, f in enumerate(json_fields):
        r = row + 1 + i
        for c, v in enumerate(f, 1):
            ws.cell(row=r, column=c, value=v)
    style_body(ws, row + 1, row + len(json_fields), len(headers))

    # ── LANGUAGES辞書 ──
    row = row + len(json_fields) + 2
    ws.cell(row=row, column=1, value="データ構造: LANGUAGES（言語定義辞書 - src/languages.py）")
    ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=7)
    ws.cell(row=row, column=1).font = Font(name="Meiryo", size=11, bold=True, color="FFFFFF")
    ws.cell(row=row, column=1).fill = SUB_HEADER_FILL

    row += 1
    for c, h in enumerate(headers, 1):
        ws.cell(row=row, column=c, value=h)
    style_header_row(ws, row, len(headers))

    lang_fields = [
        ["<lang_name> (key)", "string", "Yes", "Yes", "-", "言語名", "辞書キー。例: 'Python', 'Java', 'COBOL'。全66言語。"],
        ["[0] (extensions)", "list[string]", "Yes", "No", "-", "ファイル拡張子リスト", "対象言語のファイル拡張子。例: ['.py', '.pyw']"],
        ["[1] (syntax_name)", "string", "Yes", "No", "-", "構文名", "プロンプトで使用する言語名。例: 'Python', 'C++'"],
    ]

    for i, f in enumerate(lang_fields):
        r = row + 1 + i
        for c, v in enumerate(f, 1):
            ws.cell(row=r, column=c, value=v)
    style_body(ws, row + 1, row + len(lang_fields), len(headers))

    # ── ConversionConfig ──
    row = row + len(lang_fields) + 2
    ws.cell(row=row, column=1, value="データ構造: ConversionConfig（GUI設定 - src/gui_model.py）")
    ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=7)
    ws.cell(row=row, column=1).font = Font(name="Meiryo", size=11, bold=True, color="FFFFFF")
    ws.cell(row=row, column=1).fill = SUB_HEADER_FILL

    row += 1
    for c, h in enumerate(headers, 1):
        ws.cell(row=row, column=c, value=h)
    style_header_row(ws, row, len(headers))

    config_fields = [
        ["input_dir", "str", "Yes", "No", '""', "入力ディレクトリ", "変換対象のソースファイルが格納されたフォルダパス"],
        ["output_dir", "str", "Yes", "No", '""', "出力ディレクトリ", "変換結果を出力するフォルダパス"],
        ["source_lang", "str", "Yes", "No", '""', "変換元言語", "LANGUAGE_NAMESに含まれる言語名"],
        ["target_lang", "str", "Yes", "No", '""', "変換先言語", "LANGUAGE_NAMESに含まれる言語名"],
        ["api_key", "str", "No", "No", '""', "APIキー", "Anthropic APIキー。空の場合は環境変数を使用"],
        ["model", "str", "No", "No", "claude-sonnet-4-20250514", "モデル名", "使用するClaudeモデルID"],
        ["encoding", "str", "No", "No", "utf-8", "エンコーディング", "ソースファイルの文字エンコーディング"],
        ["extra_instructions", "str", "No", "No", '""', "追加指示", "変換時の追加プロンプト指示"],
        ["enable_resume", "bool", "No", "No", "True", "レジューム有効", "変換済みファイルをスキップするか"],
        ["enable_context", "bool", "No", "No", "True", "コンテキスト有効", "優先ファイルのコンテキスト注入を行うか"],
        ["enable_ecosystem", "bool", "No", "No", "True", "エコシステム有効", "エコシステムファイル変換を行うか"],
    ]

    for i, f in enumerate(config_fields):
        r = row + 1 + i
        for c, v in enumerate(f, 1):
            ws.cell(row=r, column=c, value=v)
    style_body(ws, row + 1, row + len(config_fields), len(headers))

    # ── ConversionState ──
    row = row + len(config_fields) + 2
    ws.cell(row=row, column=1, value="データ構造: ConversionState（GUI実行状態 - src/gui_model.py）")
    ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=7)
    ws.cell(row=row, column=1).font = Font(name="Meiryo", size=11, bold=True, color="FFFFFF")
    ws.cell(row=row, column=1).fill = SUB_HEADER_FILL

    row += 1
    for c, h in enumerate(headers, 1):
        ws.cell(row=row, column=c, value=h)
    style_header_row(ws, row, len(headers))

    state_fields = [
        ["is_running", "bool", "Yes", "No", "False", "実行中フラグ", "変換処理が実行中かどうか"],
        ["cancel_flag", "bool", "Yes", "No", "False", "キャンセルフラグ", "ユーザーがキャンセルを要求したか"],
        ["total_files", "int", "Yes", "No", "0", "総ファイル数", "変換対象の全ファイル数"],
        ["current_index", "int", "Yes", "No", "0", "現在インデックス", "現在処理中のファイル番号"],
        ["current_file", "str", "Yes", "No", '""', "現在ファイル名", "現在処理中のファイル名"],
        ["success_count", "int", "Yes", "No", "0", "成功数", "変換に成功したファイル数"],
        ["error_count", "int", "Yes", "No", "0", "エラー数", "変換に失敗したファイル数"],
        ["skipped_count", "int", "Yes", "No", "0", "スキップ数", "スキップされたファイル数"],
        ["details", "list", "Yes", "No", "[]", "詳細リスト", "(filename, status, info) のタプルリスト"],
    ]

    for i, f in enumerate(state_fields):
        r = row + 1 + i
        for c, v in enumerate(f, 1):
            ws.cell(row=r, column=c, value=v)
    style_body(ws, row + 1, row + len(state_fields), len(headers))

    # ── ECOSYSTEM_MAP ──
    row = row + len(state_fields) + 2
    ws.cell(row=row, column=1, value="データ構造: ECOSYSTEM_MAP（エコシステムマッピング - src/ecosystem.py）")
    ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=7)
    ws.cell(row=row, column=1).font = Font(name="Meiryo", size=11, bold=True, color="FFFFFF")
    ws.cell(row=row, column=1).fill = SUB_HEADER_FILL

    row += 1
    eco_headers = ["マッピングID", "ソースグループ", "ターゲットグループ", "ソースファイル例", "ターゲットファイル例", "エントリ数", "備考"]
    for c, h in enumerate(eco_headers, 1):
        ws.cell(row=row, column=c, value=h)
    style_header_row(ws, row, len(eco_headers))

    eco_rows = [
        ["ECO-001", "python", "node", "requirements.txt, setup.py, pyproject.toml", "package.json, .eslintrc.json, tsconfig.json", "12", "Python→Node.js"],
        ["ECO-002", "node", "python", "package.json, tsconfig.json, .eslintrc.*", "requirements.txt, mypy.ini, .flake8", "11", "Node.js→Python"],
        ["ECO-003", "python", "jvm", "requirements.txt, setup.py, pyproject.toml", "build.gradle", "6", "Python→Java/Kotlin/Scala"],
        ["ECO-004", "jvm", "python", "pom.xml, build.gradle, build.gradle.kts", "requirements.txt, pyproject.toml", "6", "Java/Kotlin→Python"],
        ["ECO-005", "python", "go", "requirements.txt, setup.py", "go.mod, Makefile", "5", "Python→Go"],
        ["ECO-006", "go", "python", "go.mod, go.sum", "requirements.txt", "4", "Go→Python"],
        ["ECO-007", "python", "rust", "requirements.txt, setup.py", "Cargo.toml", "5", "Python→Rust"],
        ["ECO-008", "rust", "python", "Cargo.toml, Cargo.lock", "requirements.txt", "3", "Rust→Python"],
        ["ECO-009", "node", "jvm", "package.json, tsconfig.json", "build.gradle", "4", "Node.js→Java/Kotlin"],
        ["ECO-010", "jvm", "node", "pom.xml, build.gradle", "package.json", "4", "Java/Kotlin→Node.js"],
        ["ECO-011", "dotnet", "node", "*.csproj, *.sln", "package.json", "3", "C#→Node.js"],
        ["ECO-012", "node", "dotnet", "package.json, tsconfig.json", "Project.csproj", "3", "Node.js→C#"],
        ["ECO-013", "ruby", "python", "Gemfile, Rakefile", "requirements.txt, Makefile", "3", "Ruby→Python"],
        ["ECO-014", "python", "ruby", "requirements.txt", "Gemfile, Rakefile", "3", "Python→Ruby"],
    ]

    for i, f in enumerate(eco_rows):
        r = row + 1 + i
        for c, v in enumerate(f, 1):
            ws.cell(row=r, column=c, value=v)
    style_body(ws, row + 1, row + len(eco_rows), len(eco_headers))

    auto_width(ws, len(headers))
    ws.column_dimensions["G"].width = 50


# ═══════════════════════════════════════════════════════════════
# Sheet 4: エラー・ログ定義書
# ═══════════════════════════════════════════════════════════════
def build_error_log_definition(wb):
    ws = wb.create_sheet("4_エラー・ログ定義書")

    headers = ["ID", "分類", "レベル", "発生箇所", "条件 / トリガー", "メッセージ（テンプレート）", "対処方法"]
    ws.append(headers)
    style_header_row(ws, 1, len(headers))

    entries = [
        ["ERR-001", "API認証エラー", "ERROR",
         "Migrator.__init__\n(anthropic.Anthropic初期化時)",
         "APIキーが未設定かつ環境変数ANTHROPIC_API_KEYも未設定",
         "AuthenticationError",
         "APIキーをGUI入力欄に入力するか、環境変数ANTHROPIC_API_KEYを設定する"],

        ["ERR-002", "APIレート制限", "WARNING",
         "Migrator._call_api",
         "Claude API から 429 RateLimitError が返却された",
         "Retry {attempt}/{max} in {wait:.1f}s: {err}",
         "自動リトライ（最大5回、指数バックオフ2s〜120s）。頻発する場合はAPIプランの確認"],

        ["ERR-003", "APIタイムアウト", "WARNING",
         "Migrator._call_api",
         "Claude API呼び出しがタイムアウトした（APITimeoutError）",
         "Retry {attempt}/{max} in {wait:.1f}s: {err}",
         "自動リトライ。継続する場合はネットワーク環境の確認"],

        ["ERR-004", "API過負荷", "WARNING",
         "Migrator._call_api",
         "Claude APIが 529 (Overloaded) ステータスを返却した",
         "Retry {attempt}/{max} in {wait:.1f}s: {err}",
         "自動リトライ。時間を空けて再実行"],

        ["ERR-005", "APIリトライ上限超過", "ERROR",
         "Migrator._call_api",
         "最大リトライ回数（5回）を超過した",
         "最後に受信したエラーをそのまま raise",
         "該当ファイルはerrorとして記録され、次回レジュームで再試行可能"],

        ["ERR-006", "ファイル変換エラー", "ERROR",
         "Migrator.migrate_folder\n(Phase 1/Phase 2)",
         "個別ファイルの変換中に任意の例外が発生",
         "details配列に (filename, 'ERROR', str(e)) として記録",
         "エラーは記録のみで処理は続行。残りのファイルの変換は中断しない"],

        ["ERR-007", "入力フォルダ不在", "ERROR",
         "main.py (run_cli)",
         "CLIモードで指定された入力フォルダが存在しない",
         "[ERROR] Input directory does not exist: {path}",
         "正しいパスを指定して再実行"],

        ["ERR-008", "言語名不正", "ERROR",
         "main.py (run_cli)",
         "指定された言語名がLANGUAGE_NAMESに含まれない",
         "[ERROR] Unknown source/target language: {lang}\nAvailable: {list}",
         "--list-languages で対応言語一覧を確認し、正しい言語名を使用"],

        ["ERR-009", "同一言語指定", "ERROR",
         "main.py (run_cli)\ngui_controller.py (_validate)",
         "変換元と変換先に同じ言語が指定された",
         "[ERROR] Source and target languages must be different.\n(GUI: err_same_lang キーの翻訳文)",
         "異なる言語を選択する"],

        ["VAL-001", "未入力チェック", "WARNING",
         "gui_controller.py (_validate)",
         "必須項目（変換元言語/変換先言語/入力フォルダ/出力フォルダ）が未入力",
         "err_no_source / err_no_target / err_no_input / err_no_output",
         "GUIの該当フィールドを入力してから実行する"],

        ["VAL-002", "ファイル未検出", "WARNING",
         "gui_controller.py (_start_migration)",
         "入力フォルダ内に変換元言語の拡張子に一致するファイルが存在しない",
         "err_no_files",
         "正しいフォルダを選択するか、変換元言語の選択を確認する"],

        ["LOG-001", "変換開始", "INFO",
         "gui_controller.py (_run_migration)\nmain.py (run_cli)",
         "変換処理の開始時",
         "Starting migration: {src} → {tgt}\n(GUI: log_start キーの翻訳文)",
         "-"],

        ["LOG-002", "ファイル処理中", "INFO",
         "Migrator.migrate_folder",
         "各ファイルの変換開始時",
         "[{i}/{n}] Processing: {file}\n(GUI: log_processing キーの翻訳文)",
         "-"],

        ["LOG-003", "変換成功", "SUCCESS",
         "gui_controller.py (_run_migration)",
         "ファイル変換が正常に完了した",
         "OK → {out}\n(GUI: log_success キーの翻訳文)",
         "-"],

        ["LOG-004", "レジュームスキップ", "INFO",
         "Migrator.migrate_folder",
         "レジューム有効かつ変換済みファイル（ハッシュ一致）を検出した",
         "[RESUME] {filename} (already converted)",
         "-"],

        ["LOG-005", "空ファイルスキップ", "WARNING",
         "Migrator.migrate_folder",
         "ソースファイルの内容が空白のみだった",
         "[SKIP] {filename} (empty file)",
         "-"],

        ["LOG-006", "エコシステムファイル処理", "INFO",
         "Migrator.migrate_folder",
         "エコシステムファイル（requirements.txt等）の変換を実行",
         "Ecosystem: {src_name} → {tgt_name}",
         "-"],

        ["LOG-007", "変換完了サマリー", "INFO",
         "gui_controller.py (_run_migration)\nmain.py (run_cli)",
         "全ファイルの変換処理が完了した",
         "Migration complete. Success: {s}, Errors: {e}, Skipped: {k}",
         "-"],

        ["LOG-008", "キャンセル要求", "WARNING",
         "gui_controller.py (_cancel_migration)",
         "ユーザーがキャンセルボタンを押下した",
         "Cancel requested... / Cancelled",
         "次のファイル処理開始時に中断される"],

        ["LOG-009", "致命的エラー", "ERROR",
         "gui_controller.py (_run_migration)",
         "変換処理全体が予期せぬ例外で中断した",
         "Fatal error: {str(e)}\n+ traceback出力",
         "ログとスタックトレースを確認して原因を調査する"],
    ]

    for row_data in entries:
        ws.append(row_data)

    style_body(ws, 2, len(entries) + 1, len(headers))
    auto_width(ws, len(headers))
    ws.column_dimensions["E"].width = 45
    ws.column_dimensions["F"].width = 50
    ws.column_dimensions["G"].width = 45


# ═══════════════════════════════════════════════════════════════
# Sheet 5: アーキテクチャ図解（Mermaidコード集）
# ═══════════════════════════════════════════════════════════════
def build_architecture_diagrams(wb):
    ws = wb.create_sheet("5_アーキテクチャ図解")

    diagrams = [
        ("システム構成図", """\
graph TB
    subgraph User["ユーザー"]
        GUI["GUI (tkinter)"]
        CLI["CLI (argparse)"]
    end

    subgraph Application["アプリケーション層"]
        MAIN["main.py<br/>エントリポイント"]
        CTRL["gui_controller.py<br/>MigrationController"]
        VIEW["gui_view.py<br/>MigratorView"]
        MODEL["gui_model.py<br/>ConversionConfig / ConversionState"]
        I18N["i18n.py<br/>I18n (8言語)"]
    end

    subgraph Core["コアエンジン層"]
        MIG["migrator.py<br/>Migrator"]
        SAN["sanitizer.py<br/>sanitize_code_output"]
        RES["resume_state.py<br/>MigrationState / FileEntry"]
        ECO["ecosystem.py<br/>ECOSYSTEM_MAP / Priority"]
        LANG["languages.py<br/>LANGUAGES (66言語)"]
    end

    subgraph External["外部サービス"]
        API["Anthropic Claude API<br/>messages.create"]
    end

    subgraph Storage["永続化"]
        STATE[".migration_state.json"]
        FILES["変換後ソースファイル"]
    end

    GUI --> MAIN
    CLI --> MAIN
    MAIN --> CTRL
    MAIN -->|"CLI mode"| MIG
    CTRL --> VIEW
    CTRL --> MODEL
    VIEW --> I18N
    CTRL --> MIG
    MIG --> SAN
    MIG --> RES
    MIG --> ECO
    MIG --> LANG
    ECO --> LANG
    MIG --> API
    RES --> STATE
    MIG --> FILES"""),

        ("MVC構成図（GUI）", """\
graph LR
    subgraph Model
        CC["ConversionConfig<br/>(設定データ)"]
        CS["ConversionState<br/>(実行状態)"]
        MO["MODEL_OPTIONS<br/>(モデル選択肢)"]
    end

    subgraph View
        MV["MigratorView<br/>(ウィジェット生成/配置)"]
        ST["Styles<br/>(clam テーマ)"]
        VARS["tkinter Variables<br/>(StringVar/BoolVar/DoubleVar)"]
    end

    subgraph Controller
        MC["MigrationController<br/>(イベントハンドリング)"]
        TH["Threading<br/>(バックグラウンド変換)"]
    end

    MC -->|"読み取り"| CC
    MC -->|"更新"| CS
    MC -->|"操作"| MV
    MV -->|"参照"| MO
    MV -->|"バインド"| VARS
    MC -->|"起動"| TH
    TH -->|"root.after()"| MV"""),

        ("変換処理シーケンス図", """\
sequenceDiagram
    actor User as ユーザー
    participant CTRL as Controller
    participant MIG as Migrator
    participant ECO as ecosystem.py
    participant RES as resume_state.py
    participant API as Claude API
    participant SAN as sanitizer.py

    User->>CTRL: 変換開始クリック
    CTRL->>CTRL: _validate() 入力チェック
    CTRL->>CTRL: threading.Thread起動

    Note over MIG: Phase 1: エコシステムファイル
    CTRL->>MIG: migrate_folder()
    MIG->>RES: load_state(output_dir)
    RES-->>MIG: MigrationState

    MIG->>ECO: get_ecosystem_mappings(src, tgt)
    ECO-->>MIG: {src_file: tgt_file}

    loop 各エコシステムファイル
        MIG->>RES: file_hash() + is_done()
        alt 変換済み
            MIG-->>MIG: スキップ (RESUME)
        else 未変換
            MIG->>API: messages.create(ECOSYSTEM_SYSTEM_PROMPT)
            API-->>MIG: 変換結果
            MIG->>SAN: sanitize_code_output()
            SAN-->>MIG: サニタイズ済みコード
            MIG->>RES: mark_done() + save_state()
        end
    end

    Note over MIG: Phase 2: ソースコードファイル
    MIG->>ECO: sort_files_by_priority()
    MIG->>ECO: get_priority_files()

    loop 各ソースファイル (優先度順)
        MIG->>MIG: cancel_check()
        MIG->>RES: file_hash() + is_done()
        alt 変換済み
            MIG-->>MIG: スキップ (RESUME)
        else 未変換
            MIG->>API: messages.create(SYSTEM_PROMPT + context)
            API-->>MIG: 変換結果
            MIG->>SAN: sanitize_code_output()
            SAN-->>MIG: サニタイズ済みコード
            MIG->>MIG: priority? → context_snippetsに追加
            MIG->>RES: mark_done() + save_state()
        end
    end

    MIG-->>CTRL: results {success, errors, skipped, resumed}
    CTRL->>CTRL: root.after() でUI更新"""),

        ("出力サニタイズ処理フロー", """\
graph TD
    A["AI生成テキスト入力"] --> B["Step 1: 前後空白除去<br/>text.strip()"]
    B --> C["Step 2: Markdownフェンス除去<br/>_strip_all_fences()"]
    C --> C1["正規表現で ```lang...``` を除去"]
    C1 --> C2["先頭/末尾の孤立した ``` を除去"]
    C2 --> D["Step 3: プリアンブル除去<br/>_strip_preamble_epilogue()"]
    D --> D1["先頭5行を走査<br/>7言語のNLパターンに一致する行を除去"]
    D1 --> E["Step 4: エピローグ除去"]
    E --> E1["'Key changes', '注意', '説明' 等の<br/>パターンから後を除去"]
    E1 --> F["Step 5: 行末正規化<br/>各行の末尾空白除去 + 末尾改行"]
    F --> G["サニタイズ済みコード出力"]"""),

        ("ファイル構成図", """\
graph TD
    ROOT["UniversalMigrator/"]
    ROOT --> MAIN["main.py<br/>エントリポイント (182行)"]
    ROOT --> REQ["requirements.txt<br/>anthropic>=0.40.0"]
    ROOT --> BUILD_BAT["build.bat"]
    ROOT --> BUILD_SH["build.sh"]
    ROOT --> README["README.md / README_EN.md"]

    ROOT --> SRC["src/"]
    SRC --> INIT["__init__.py"]
    SRC --> LANG_PY["languages.py (102行)<br/>66言語定義"]
    SRC --> MIG_PY["migrator.py (441行)<br/>変換エンジン"]
    SRC --> SAN_PY["sanitizer.py (125行)<br/>出力サニタイズ"]
    SRC --> RES_PY["resume_state.py (86行)<br/>レジューム管理"]
    SRC --> ECO_PY["ecosystem.py (219行)<br/>エコシステムマッピング"]
    SRC --> GUI_M["gui_model.py (53行)<br/>MVCモデル"]
    SRC --> GUI_V["gui_view.py (335行)<br/>MVCビュー"]
    SRC --> GUI_C["gui_controller.py (221行)<br/>MVCコントローラー"]
    SRC --> I18N_PY["i18n.py (430行)<br/>多言語対応"]

    ROOT --> DIST["dist/<br/>ビルド成果物"]"""),

        ("リトライ戦略図", """\
graph TD
    A["API呼び出し"] --> B{成功?}
    B -->|Yes| C["結果を返す"]
    B -->|No| D{例外の種類}
    D -->|"429 RateLimitError"| E["リトライ対象"]
    D -->|"APITimeoutError"| E
    D -->|"529 Overloaded"| E
    D -->|"その他の APIStatusError"| F["即座に raise"]
    E --> G{"attempt < MAX_RETRIES(5)?"}
    G -->|Yes| H["待機時間を計算<br/>backoff = min(2 * 2^attempt, 120)<br/>jitter = random(0, backoff*0.3)<br/>wait = backoff + jitter"]
    H --> I["on_retry コールバック通知"]
    I --> J["time.sleep(wait)"]
    J --> A
    G -->|No| K["最後のエラーを raise"]"""),
    ]

    row = 1
    for title, mermaid_code in diagrams:
        # タイトル行
        ws.cell(row=row, column=1, value=title)
        ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=2)
        ws.cell(row=row, column=1).font = Font(name="Meiryo", size=12, bold=True, color="FFFFFF")
        ws.cell(row=row, column=1).fill = SUB_HEADER_FILL

        row += 1
        ws.cell(row=row, column=1, value="種別")
        ws.cell(row=row, column=2, value="Mermaidコード")
        ws.cell(row=row, column=1).font = HEADER_FONT
        ws.cell(row=row, column=1).fill = HEADER_FILL
        ws.cell(row=row, column=1).alignment = CENTER_ALIGN
        ws.cell(row=row, column=1).border = THIN_BORDER
        ws.cell(row=row, column=2).font = HEADER_FONT
        ws.cell(row=row, column=2).fill = HEADER_FILL
        ws.cell(row=row, column=2).alignment = CENTER_ALIGN
        ws.cell(row=row, column=2).border = THIN_BORDER

        row += 1
        ws.cell(row=row, column=1, value=title)
        ws.cell(row=row, column=1).font = BODY_FONT
        ws.cell(row=row, column=1).alignment = WRAP_ALIGN
        ws.cell(row=row, column=1).border = THIN_BORDER

        ws.cell(row=row, column=2, value=mermaid_code)
        ws.cell(row=row, column=2).font = CODE_FONT
        ws.cell(row=row, column=2).alignment = WRAP_ALIGN
        ws.cell(row=row, column=2).border = THIN_BORDER

        line_count = mermaid_code.count("\n") + 1
        ws.row_dimensions[row].height = max(15, line_count * 13)

        row += 2

    ws.column_dimensions["A"].width = 25
    ws.column_dimensions["B"].width = 100


# ═══════════════════════════════════════════════════════════════
# メイン
# ═══════════════════════════════════════════════════════════════
def main():
    wb = Workbook()

    build_feature_list(wb)
    build_api_spec(wb)
    build_table_definition(wb)
    build_error_log_definition(wb)
    build_architecture_diagrams(wb)

    output_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "design_document.xlsx")
    wb.save(output_path)
    print(f"設計書を生成しました: {output_path}")


if __name__ == "__main__":
    main()
