<?php

if ( ! defined( 'ABSPATH' ) ) {

    exit;

}



/**

 * 管理画面のビュー (描画) 層。

 *

 * メインページ・ウィザード・各タブ (設定/プロバイダ/画像/システムプロンプト/

 * タスク/ログ) の描画と、描画用ヘルパ・通知 notice をまとめた層。リクエスト処理

 * (AIAP_Admin_Controller) や AJAX/実行エンジン (AIAP_Admin) から描画ロジックを

 * 分離する (Phase 3 ステップ4)。

 *

 * 共有グルー (メニュースラッグ・nonce・USER_META 定数・プロバイダ定義の静的

 * プロパティ) は AIAP_Admin に残り、本クラスは AIAP_Admin::MENU_SLUG のように

 * 明示参照する。エントリポイント render_page はメニューコールバックとして、

 * maybe_render_* は admin_notices フックとして AIAP_Admin から登録される。

 */

class AIAP_Admin_View {



    /**

     * AUTH_KEY 未定義環境向けの管理画面 notice。

     *

     * AIAP 管理画面のみで表示する (他のページに出すと全管理者を混乱させる)。

     * dismissible にして頻繁に再表示されないようにする。

     */

    public static function maybe_render_auth_key_warning() {

        // current_user_can で権限をチェック

        if ( ! current_user_can( 'manage_options' ) ) {

            return;

        }

        // AIAP 配下のページのみで出す (screen ID は menu slug を含む)

        $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;

        if ( ! $screen || strpos( (string) $screen->id, AIAP_Admin::MENU_SLUG ) === false ) {

            return;

        }

        // AUTH_KEY と SECURE_AUTH_KEY のいずれかが空 / 未定義なら警告

        $auth_missing = ! defined( 'AUTH_KEY' ) || AUTH_KEY === '';

        if ( ! $auth_missing ) {

            return;

        }

        ?>

        <div class="notice notice-error is-dismissible">

            <p>

                <strong><?php echo esc_html( __( 'AI Auto Poster: AUTH_KEY is not configured', 'ai-auto-poster' ) ); ?></strong>

            </p>

            <p>

                <?php echo esc_html( __( 'WordPress security keys (AUTH_KEY) are missing in wp-config.php. Without them, AI Auto Poster cannot encrypt your API keys at rest, so saving settings will silently clear them. Generate fresh keys at https://api.wordpress.org/secret-key/1.1/salt/ and paste them into wp-config.php, then re-enter your API keys.', 'ai-auto-poster' ) ); ?>

            </p>

        </div>

        <?php

    }



    /**

     * AIAP_Model_Sync が記録した discontinued モデルの警告 notice。

     *

     * AIAP 管理画面のみで表示する。プロバイダ側で saved_model が利用不能になっていれば

     * その情報をユーザーに伝え、設定画面のモデル選択を促す。

     *

     * 設定画面側 (render_provider) には既存の inline 警告 ($discontinued) が出るが、

     * それは設定画面を開いたときのみ。本 notice は AIAP 全ページで気付かせる役割。

     */

    public static function maybe_render_discontinued_model_warning() {

        if ( ! current_user_can( 'manage_options' ) ) {

            return;

        }

        if ( ! class_exists( 'AIAP_Model_Sync' ) ) {

            return;

        }

        $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;

        if ( ! $screen || strpos( (string) $screen->id, AIAP_Admin::MENU_SLUG ) === false ) {

            return;

        }

        $discontinued = AIAP_Model_Sync::get_discontinued();

        if ( empty( $discontinued ) ) {

            return;

        }

        $items = array();

        foreach ( $discontinued as $provider => $model ) {

            $items[] = sprintf( '%s: %s', esc_html( $provider ), esc_html( $model ) );

        }

        $settings_url = admin_url( 'admin.php?page=' . AIAP_Admin::MENU_SLUG . '&tab=provider' );

        ?>

        <div class="notice notice-warning is-dismissible">

            <p>

                <strong><?php echo esc_html( __( 'AI Auto Poster: some selected models are no longer available', 'ai-auto-poster' ) ); ?></strong>

            </p>

            <p>

                <?php echo esc_html( __( 'The weekly model check detected that the following models are not in the provider\'s current list, which usually means they have been deprecated or retired:', 'ai-auto-poster' ) ); ?>

            </p>

            <ul style="list-style: disc; padding-left: 1.5em;">

                <?php foreach ( $items as $line ) : ?>

                    <li><?php echo $line; // already escaped in the sprintf above ?></li>

                <?php endforeach; ?>

            </ul>

            <p>

                <a href="<?php echo esc_url( $settings_url ); ?>" class="button button-primary"><?php echo esc_html( __( 'Open AI Provider settings', 'ai-auto-poster' ) ); ?></a>

            </p>

        </div>

        <?php

    }



    public static function tab_label( $key ) {

        $label = self::resolve_dynamic_key( $key );

        return preg_replace( '/^[^\p{L}\p{N}]+/u', '', $label );

    }



    public static function meta_label( $meta ) {

        // プロバイダ名(Claude, OpenAI等)は両言語共通の英字表記

        // 'label' があればそれを使い、'label_key' があれば i18n で展開する

        if ( ! empty( $meta['label'] ) ) {

            return $meta['label'];

        }

        if ( ! empty( $meta['label_key'] ) ) {

            return self::resolve_dynamic_key( $meta['label_key'] );

        }

        return '';

    }



    /**

     * 動的キーを WordPress 標準翻訳に解決する

     *

     * 動的に組み立てられたキー(例: 'imgfmt_16_9', 'dedup_sens_high')を

     * switch で個別の __( '...', 'ai-auto-poster' ) に振り分けて翻訳する。

     * これにより WordPress.org の翻訳ツール (makepot) がソースから抽出可能になる。

     *

     * @param string $key

     * @return string 翻訳後の文字列(キーが未定義ならキー自体を返す)

     */

    public static function resolve_dynamic_key( $key ) {

        switch ( $key ) {

            // 曜日

            case 'mon': return __( 'Mon', 'ai-auto-poster' );

            case 'tue': return __( 'Tue', 'ai-auto-poster' );

            case 'wed': return __( 'Wed', 'ai-auto-poster' );

            case 'thu': return __( 'Thu', 'ai-auto-poster' );

            case 'fri': return __( 'Fri', 'ai-auto-poster' );

            case 'sat': return __( 'Sat', 'ai-auto-poster' );

            case 'sun': return __( 'Sun', 'ai-auto-poster' );

            case 'dow_mon': return __( 'Mon', 'ai-auto-poster' );

            case 'dow_tue': return __( 'Tue', 'ai-auto-poster' );

            case 'dow_wed': return __( 'Wed', 'ai-auto-poster' );

            case 'dow_thu': return __( 'Thu', 'ai-auto-poster' );

            case 'dow_fri': return __( 'Fri', 'ai-auto-poster' );

            case 'dow_sat': return __( 'Sat', 'ai-auto-poster' );

            case 'dow_sun': return __( 'Sun', 'ai-auto-poster' );



            // 投稿ステータス

            case 'draft':   return __( 'Draft',          'ai-auto-poster' );

            case 'pending': return __( 'Pending Review', 'ai-auto-poster' );

            case 'publish': return __( 'Publish',        'ai-auto-poster' );



            // 有効/無効

            case 'enabled':  return __( 'Enabled',  'ai-auto-poster' );

            case 'disabled': return __( 'Disabled', 'ai-auto-poster' );

            case 'activate':   return __( 'Activate',   'ai-auto-poster' );

            case 'deactivate': return __( 'Disable', 'ai-auto-poster' );

            case 'auto_posting_turned_on':  return __( 'Auto posting turned ON',  'ai-auto-poster' );

            case 'auto_posting_turned_off': return __( 'Auto posting turned OFF', 'ai-auto-poster' );



            // dedup_sens_*

            case 'dedup_sens_high':   return __( 'Strict (catch only barely similar)', 'ai-auto-poster' );

            case 'dedup_sens_medium': return __( 'Balanced',                            'ai-auto-poster' );

            case 'dedup_sens_low':    return __( 'Loose (only flag clearly similar)',  'ai-auto-poster' );



            // imgfmt_*

            case 'imgfmt_16_9': return __( 'Landscape (16:9) - Blog headers', 'ai-auto-poster' );

            case 'imgfmt_1_1':  return __( 'Square (1:1) - Social posts',     'ai-auto-poster' );

            case 'imgfmt_9_16': return __( 'Portrait (9:16) - Stories',       'ai-auto-poster' );

            case 'imgfmt_4_3':  return __( 'Wide (4:3) - Thumbnails',         'ai-auto-poster' );



            // imgqual_*

            case 'imgqual_low':    return __( 'Low - Faster & Cheaper',  'ai-auto-poster' );

            case 'imgqual_medium': return __( 'Medium - Balanced',       'ai-auto-poster' );

            case 'imgqual_high':   return __( 'High - Best Quality',     'ai-auto-poster' );



            // imgprov_*(画像プロバイダ名)

            case 'imgprov_dalle3':    return __( 'DALL-E 3',     'ai-auto-poster' );

            case 'imgprov_imagen':    return __( 'Imagen',       'ai-auto-poster' );

            case 'imgprov_stability': return __( 'Stability AI', 'ai-auto-poster' );

            case 'imgprov_flux':      return __( 'Flux',         'ai-auto-poster' );

            case 'imgprov_unsplash':  return __( 'Unsplash',     'ai-auto-poster' );

            case 'imgprov_pixabay':   return __( 'Pixabay',      'ai-auto-poster' );

            case 'imgprov_pexels':    return __( 'Pexels',       'ai-auto-poster' );

            case 'imgprov_openverse': return __( 'Openverse',    'ai-auto-poster' );



            // imgstyle_*(画像スタイル)

            case 'imgstyle_none':              return __( 'None (Default)',     'ai-auto-poster' );

            case 'imgstyle_natural':           return __( 'Natural',            'ai-auto-poster' );

            case 'imgstyle_vivid':             return __( 'Vivid',              'ai-auto-poster' );

            case 'imgstyle_realistic':         return __( 'Realistic',          'ai-auto-poster' );

            case 'imgstyle_cartoon':           return __( 'Cartoon',            'ai-auto-poster' );

            case 'imgstyle_anime':             return __( 'Anime',              'ai-auto-poster' );

            case 'imgstyle_3d-render':         return __( '3D Render',          'ai-auto-poster' );

            case 'imgstyle_3d-model':          return __( '3D Model',           'ai-auto-poster' );

            case 'imgstyle_watercolor':        return __( 'Watercolor',         'ai-auto-poster' );

            case 'imgstyle_oil-painting':      return __( 'Oil Painting',       'ai-auto-poster' );

            case 'imgstyle_flat-design':       return __( 'Flat Design',        'ai-auto-poster' );

            case 'imgstyle_pixel-art':         return __( 'Pixel Art',          'ai-auto-poster' );

            case 'imgstyle_analog-film':       return __( 'Analog Film',        'ai-auto-poster' );

            case 'imgstyle_cinematic':         return __( 'Cinematic',          'ai-auto-poster' );

            case 'imgstyle_comic-book':        return __( 'Comic Book',         'ai-auto-poster' );

            case 'imgstyle_digital-art':       return __( 'Digital Art',        'ai-auto-poster' );

            case 'imgstyle_enhance':           return __( 'Enhance',            'ai-auto-poster' );

            case 'imgstyle_fantasy-art':       return __( 'Fantasy Art',        'ai-auto-poster' );

            case 'imgstyle_isometric':         return __( 'Isometric',          'ai-auto-poster' );

            case 'imgstyle_line-art':          return __( 'Line Art',           'ai-auto-poster' );

            case 'imgstyle_low-poly':          return __( 'Low Poly',           'ai-auto-poster' );

            case 'imgstyle_modeling-compound': return __( 'Modeling Compound',  'ai-auto-poster' );

            case 'imgstyle_neon-punk':         return __( 'Neon Punk',          'ai-auto-poster' );

            case 'imgstyle_origami':           return __( 'Origami',            'ai-auto-poster' );

            case 'imgstyle_photographic':      return __( 'Photographic',       'ai-auto-poster' );

            case 'imgstyle_tile-texture':      return __( 'Tile Texture',       'ai-auto-poster' );



            // SEO グループ

            case 'seo_content_group':   return __( 'Content Optimization',     'ai-auto-poster' );

            case 'seo_image_group':     return __( 'Image Optimization',       'ai-auto-poster' );

            case 'seo_structure_group': return __( 'Site Structure',           'ai-auto-poster' );

            case 'seo_meta_group':      return __( 'Meta and Structured Data', 'ai-auto-poster' );

            case 'seo_other_group':     return __( 'Other Settings',           'ai-auto-poster' );



            // SEO 個別フィールド

            case 'seo_meta_desc':     return __( 'Auto-generate meta description',                'ai-auto-poster' );

            case 'seo_heading':       return __( 'Optimize heading structure',                    'ai-auto-poster' );

            case 'seo_focus_kw':      return __( 'Generate and apply focus keyword',              'ai-auto-poster' );

            case 'seo_faq':           return __( 'Auto-generate FAQ section',                     'ai-auto-poster' );

            case 'seo_toc':           return __( 'Auto-insert table of contents',                 'ai-auto-poster' );

            case 'seo_template':      return __( 'Apply article template',                        'ai-auto-poster' );

            case 'seo_img_alt':       return __( 'Auto-set image alt text',                       'ai-auto-poster' );

            case 'seo_img_webp':      return __( 'WebP conversion and compression',               'ai-auto-poster' );

            case 'seo_internal_link': return __( 'Auto-insert internal links',                    'ai-auto-poster' );

            case 'seo_auto_tag':      return __( 'Auto-generate and set tags',                    'ai-auto-poster' );

            case 'seo_related':       return __( 'Add related posts section',                     'ai-auto-poster' );

            case 'seo_schema':        return __( 'Auto-output Article JSON-LD schema',            'ai-auto-poster' );

            case 'seo_ogp':           return __( 'Auto-set OGP meta tags',                        'ai-auto-poster' );

            case 'seo_canonical':     return __( 'Set canonical URL',                             'ai-auto-poster' );

            case 'seo_plugin_link':   return __( 'Integrate with SEO plugins',                    'ai-auto-poster' );

            case 'seo_reading_time':  return __( 'Calculate and save reading time',               'ai-auto-poster' );

            case 'seo_sitemap_ping':  return __( 'Update XML sitemap and notify search engines',  'ai-auto-poster' );

            case 'seo_llms_txt':      return __( 'Generate llms.txt',                             'ai-auto-poster' );



            // タブ系

            case 'tab_settings':        return __( 'General Settings',     'ai-auto-poster' );

            case 'tab_provider':        return __( 'AI Providers',         'ai-auto-poster' );

            case 'tab_image_provider':  return __( 'Image AI Providers',   'ai-auto-poster' );

            case 'tab_image_settings':  return __( 'Image Settings',       'ai-auto-poster' );

            case 'tab_system_prompt':   return __( 'System Prompt',        'ai-auto-poster' );

            case 'tab_rss_feeds':       return __( 'RSS Feeds',            'ai-auto-poster' );

            case 'tab_prompts':         return __( 'Tasks',                'ai-auto-poster' );

            case 'tab_logs':            return __( 'Post history',         'ai-auto-poster' );

            case 'tab_run_logs':        return __( 'Execution log',        'ai-auto-poster' );

            case 'tab_quick_post':      return __( 'Quick Post',           'ai-auto-poster' );



            // notes (画像プロバイダの説明)

            case 'shared_openai':   return __( 'Uses the OpenAI API key already configured in AI Providers.', 'ai-auto-poster' );

            case 'shared_gemini':   return __( 'Uses the Gemini API key already configured in AI Providers.', 'ai-auto-poster' );

            case 'openverse_desc':  return __( 'Open-licensed image search by WordPress.org. Ready to use immediately.', 'ai-auto-poster' );

        }

        // 未定義: キーをそのまま返す(開発時の発見性重視)

        return $key;

    }



    /**

     * 暗号文を安全に復号して平文を返す。

     *

     * 復号失敗 / AUTH_KEY 未定義 / 空文字入力時は空文字を返す。

     * 画面表示用に保存済み暗号文 (SMTPパスワード・APIキー等) を復号する場面で共通利用する。

     *

     * @param string $cipher AIAP_Crypto で暗号化された値 (空可)

     * @return string 平文 (失敗時は '')

     */

    public static function safe_decrypt( $cipher ) {

        if ( ! class_exists( 'AIAP_Crypto' ) || empty( $cipher ) ) {

            return '';

        }

        $decrypted = AIAP_Crypto::decrypt( (string) $cipher );

        return is_string( $decrypted ) ? $decrypted : '';

    }



    /**

     * パスワード型入力欄 (目アイコン付き表示切替トグル) のレンダラ。

     *

     * SMTPパスワード・テキスト生成APIキー・画像生成APIキーで共通に使用する。

     * 復号済みの平文を value に出力し、目アイコン押下で password ↔ text を切り替える。

     * クラス aiap-password-wrapper / aiap-password-toggle のCSS / JSは

     * admin-components.css と admin.js (initPasswordToggle) で共通定義済み。

     *

     * autofill 対策: 同一ページ内に複数の type="password" が存在すると、Chrome 等の

     * パスワードマネージャーが保存済みパスワードを heuristic で全 input に書き込む

     * (例: OpenAI のキーが Claude の input に注入される)。autocomplete="new-password"

     * は無視されることがあるため、readonly 属性で確実に抑止し、JS 側で focus 時に

     * 解除する (initPasswordToggle 参照)。

     *

     * 注: 機密情報を画面に出すため規約 (claude.md 基本方針) と緊張関係にあるが、

     *     管理者画面 (manage_options 権限保護下) のみで利用する想定。

     *

     * @param string $id            input 要素の id

     * @param string $name          input 要素の name

     * @param string $plain_value   復号済みの平文 (= 暗号化解除後の値、空可)

     * @param string $wrapper_style wrapper div への inline style (例: 'max-width:420px;'、空可)

     */

    public static function render_password_field( $id, $name, $plain_value = '', $wrapper_style = '' ) {

        $style_attr = $wrapper_style !== '' ? ' style="' . esc_attr( $wrapper_style ) . '"' : '';

        ?>

        <div class="aiap-password-wrapper"<?php echo $style_attr; ?>>

            <input type="password" id="<?php echo esc_attr( $id ); ?>" class="aiap-input-text" name="<?php echo esc_attr( $name ); ?>" value="<?php echo esc_attr( $plain_value ); ?>" autocomplete="new-password" readonly>

            <button type="button" class="aiap-password-toggle" data-aiap-password-target="<?php echo esc_attr( $id ); ?>" aria-label="<?php echo esc_attr( __( 'Show password', 'ai-auto-poster' ) ); ?>" aria-pressed="false">

                <span class="dashicons dashicons-visibility" aria-hidden="true"></span>

            </button>

        </div>

        <?php

    }



    public static function log_message( $log ) {

        // 新形式(キー+パラメータ)は Logger::format_message で展開、旧形式はメッセージそのまま

        $message = AIAP_Logger::format_message( $log );



        // 過去ログ互換: 英語UIで日本語の曜日が混入していたら英語表記に置換

        $locale = function_exists( 'determine_locale' ) ? determine_locale() : get_locale();

        $is_ja = ( strpos( $locale, 'ja' ) === 0 );

        if ( ! $is_ja ) {

            $dow_replace = array(

                '月曜日' => 'Mon', '火曜日' => 'Tue', '水曜日' => 'Wed', '木曜日' => 'Thu',

                '金曜日' => 'Fri', '土曜日' => 'Sat', '日曜日' => 'Sun',

            );

            $message = strtr( $message, $dow_replace );

        }



        return $message;

    }



    /**

     * ログ詳細 (<details>) で表示する context 配列のキー名を、ユーザー表示用に翻訳する。

     *

     * ログ生成側 (AIAP_AI_Client / AIAP_Image_Providers の log_request_payload 等) は

     * 機械可読な英語キー (provider / model / payload など) を context 配列に詰める。

     * これにより DB に保存されるログデータは後方互換性が保たれる (claude.md L19 遵守)。

     * 表示時のみ本関数を通して人間可読な日本語ラベルに変換する。

     *

     * 辞書に無いキーは大文字化した生のキー名をそのまま返す (= 新規 context キー追加時の

     * 安全フォールバック)。CSS の text-transform:uppercase はラテン文字のみに作用するため、

     * 翻訳済みの日本語ラベルは大文字化されず、そのまま表示される。

     *

     * @param string $key context 配列のキー名

     * @return string 翻訳済みラベル (or 辞書にないキーはそのまま)

     */

    public static function translate_context_key( $key ) {

        $key = (string) $key;

        switch ( $key ) {

            case 'provider':           return __( 'Provider',                       'ai-auto-poster' );

            case 'model':              return __( 'Model',                          'ai-auto-poster' );

            case 'is_manual':          return __( 'Manual run',                     'ai-auto-poster' );

            // 旧形式 (全文保存) のキー。互換のため翻訳ラベルは残す。

            case 'system_text':        return __( 'System instruction',             'ai-auto-poster' );

            case 'user_prompt':        return __( 'User prompt',                    'ai-auto-poster' );

            case 'payload':            return __( 'Payload',                        'ai-auto-poster' );

            // 新形式 (長さ + 先頭プレビュー、機密保護のため H4 で導入)

            case 'system_text_len':    return __( 'System instruction (length)',    'ai-auto-poster' );

            case 'system_text_head':   return __( 'System instruction (preview)',   'ai-auto-poster' );

            case 'user_prompt_len':    return __( 'User prompt (length)',           'ai-auto-poster' );

            case 'user_prompt_head':   return __( 'User prompt (preview)',          'ai-auto-poster' );

            case 'payload_len':        return __( 'Payload (length)',               'ai-auto-poster' );

            case 'payload_head':       return __( 'Payload (preview)',              'ai-auto-poster' );

            case 'parameters':         return __( 'Parameters',                     'ai-auto-poster' );

            // Bug fix (audit r13): ログ画面 (AI 送信内容など) の context キーラベルが英語の

            // まま大文字表示される問題の解消。translate_context_key の辞書に登録されていない

            // キーは生キー名をそのまま返し、CSS の text-transform:uppercase で

            // 「OUTPUT_TOKEN_LIMIT:」のような英語大文字表示になっていた。

            // AIAP_Logger::add の context 引数で使われる全キーを辞書に追加。

            case 'output_token_limit': return __( 'Output token limit',              'ai-auto-poster' );

            case 'content':            return __( 'Content',                         'ai-auto-poster' );

            case 'focus_keyword':      return __( 'Focus keyword',                   'ai-auto-poster' );

            case 'id':                 return __( 'ID',                              'ai-auto-poster' );

            case 'image_alt':          return __( 'Image alt text',                  'ai-auto-poster' );

            case 'image_prompt':       return __( 'Image prompt',                    'ai-auto-poster' );

            case 'instances':          return __( 'Instances',                       'ai-auto-poster' );

            case 'is_similar':         return __( 'Similar',                         'ai-auto-poster' );

            case 'kind':               return __( 'Kind',                            'ai-auto-poster' );

            case 'message':            return __( 'Message',                         'ai-auto-poster' );

            case 'meta_description':   return __( 'Meta description',                'ai-auto-poster' );

            case 'method':             return __( 'Method',                          'ai-auto-poster' );

            case 'original_prompt':    return __( 'Original prompt',                 'ai-auto-poster' );

            case 'overrides':          return __( 'Overrides',                       'ai-auto-poster' );

            case 'post_date':          return __( 'Post date',                       'ai-auto-poster' );

            case 'post_id':            return __( 'Post ID',                         'ai-auto-poster' );

            case 'prompt':             return __( 'Prompt',                          'ai-auto-poster' );

            case 'reason':             return __( 'Reason',                          'ai-auto-poster' );

            case 'retryable':          return __( 'Retryable',                       'ai-auto-poster' );

            case 'rss_settings':       return __( 'RSS settings',                    'ai-auto-poster' );

            case 'rss_used_articles':  return __( 'RSS articles used',               'ai-auto-poster' );

            case 'schedule_run_key':   return __( 'Schedule run key',                'ai-auto-poster' );

            case 'started_at':         return __( 'Started at',                      'ai-auto-poster' );

            case 'status':             return __( 'Status',                          'ai-auto-poster' );

            case 'step':               return __( 'Step',                            'ai-auto-poster' );

            case 'task_id':            return __( 'Task ID',                         'ai-auto-poster' );

            case 'time':               return __( 'Time',                            'ai-auto-poster' );

            case 'title':              return __( 'Title',                           'ai-auto-poster' );

            case 'warnings':           return __( 'Warnings',                        'ai-auto-poster' );

        }

        return $key;

    }



    public static function render_page() {

        if ( ! current_user_can( 'manage_options' ) ) return;



        // Phase 3: ウィザードを表示すべきならそちらに分岐

        if ( AIAP_Admin::should_show_wizard() ) {

            AIAP_Admin_View_Wizard::render_wizard();

            return;

        }



        $tab  = sanitize_key( wp_unslash( $_GET['tab'] ?? AIAP_Tabs::DEFAULT_TAB ) );

        $s    = AIAP_AI_Client::get_settings();

        $im   = AIAP_Image_Client::get_settings();

        ?>

        <?php

        $verified_providers = AIAP_Settings::verified_providers();

        $verified_keys = array_keys( $verified_providers );

        ?>

        <div class="wrap aiap-wrap" data-aiap-ajax-url="<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>" data-aiap-nonce="<?php echo esc_attr( wp_create_nonce( AIAP_Admin::NONCE ) ); ?>" data-aiap-test-no-key="<?php echo esc_attr( __( 'Enter a key first', 'ai-auto-poster' ) ); ?>" data-aiap-password-show-label="<?php echo esc_attr( __( 'Show password', 'ai-auto-poster' ) ); ?>" data-aiap-password-hide-label="<?php echo esc_attr( __( 'Hide password', 'ai-auto-poster' ) ); ?>" data-aiap-verified="<?php echo esc_attr( wp_json_encode( $verified_keys ) ); ?>" data-aiap-not-tested-msg="<?php echo esc_attr( __( 'Please test the API key first.', 'ai-auto-poster' ) ); ?>">

            <div class="aiap-page-head">

                <h1 class="aiap-page-title">AI Auto Poster</h1>

            </div>

            <?php self::render_notices(); ?>

            <?php

            $tabs = array(

                AIAP_Tabs::SETTINGS => array(

                    'key'  => 'tab_settings',

                    'icon' => 'dashicons-admin-generic',

                ),

                AIAP_Tabs::QUICK_POST => array(

                    'key'  => 'tab_quick_post',

                    'icon' => 'dashicons-welcome-write-blog',

                ),

                AIAP_Tabs::PROVIDER => array(

                    'key'  => 'tab_provider',

                    'icon' => 'dashicons-edit-page',

                ),

                AIAP_Tabs::IMAGE_PROVIDER => array(

                    'key'  => 'tab_image_provider',

                    'icon' => 'dashicons-format-image',

                ),

                AIAP_Tabs::IMAGE_SETTINGS => array(

                    'key'  => 'tab_image_settings',

                    'icon' => 'dashicons-admin-tools',

                ),

                AIAP_Tabs::SYSTEM_PROMPT => array(

                    'key'  => 'tab_system_prompt',

                    'icon' => 'dashicons-editor-code',

                ),

                AIAP_Tabs::RSS_FEEDS => array(

                    'key'  => 'tab_rss_feeds',

                    'icon' => 'dashicons-rss',

                ),

                AIAP_Tabs::PROMPTS => array(

                    'key'  => 'tab_prompts',

                    'icon' => 'dashicons-list-view',

                ),

                AIAP_Tabs::LOGS => array(

                    'key'  => 'tab_logs',

                    'icon' => 'dashicons-media-text',

                ),

                AIAP_Tabs::RUN_LOGS => array(

                    'key'  => 'tab_run_logs',

                    'icon' => 'dashicons-media-document',

                ),

            );

            // サイドバーのセクションごとのグループ化 (#nav-redesign)

            $groups = array(

                'setup' => array(

                    'label' => __( 'Setup', 'ai-auto-poster' ),

                    'tabs'  => array( AIAP_Tabs::SETTINGS, AIAP_Tabs::PROVIDER, AIAP_Tabs::IMAGE_PROVIDER, AIAP_Tabs::RSS_FEEDS, AIAP_Tabs::IMAGE_SETTINGS, AIAP_Tabs::SYSTEM_PROMPT ),

                ),

                'operation' => array(

                    'label' => __( 'Operation', 'ai-auto-poster' ),

                    'tabs'  => array( AIAP_Tabs::PROMPTS, AIAP_Tabs::QUICK_POST, AIAP_Tabs::LOGS, AIAP_Tabs::RUN_LOGS ),

                ),

            );

            ?>

            <button type="button" class="aiap-sidebar-toggle" data-aiap-sidebar-toggle aria-label="<?php echo esc_attr( __( 'Toggle menu', 'ai-auto-poster' ) ); ?>" aria-expanded="false">

                <span class="dashicons dashicons-menu" aria-hidden="true"></span>

                <span class="aiap-sidebar-toggle-label"><?php echo esc_html( self::tab_label( $tabs[ $tab ]['key'] ?? 'tab_settings' ) ); ?></span>

            </button>



            <div class="aiap-shell">

                <aside class="aiap-sidebar" data-aiap-sidebar>

                    <nav class="aiap-tabs" role="tablist">

                        <?php foreach ( $groups as $group_key => $group ) : ?>

                            <div class="aiap-sidebar-section">

                                <div class="aiap-sidebar-section-title"><?php echo esc_html( $group['label'] ); ?></div>

                                <?php foreach ( $group['tabs'] as $slug ) : if ( ! isset( $tabs[ $slug ] ) ) continue; $meta = $tabs[ $slug ]; ?>

                                    <a href="?page=<?php echo esc_attr( AIAP_Admin::MENU_SLUG ); ?>&tab=<?php echo esc_attr( $slug ); ?>"

                                       class="aiap-tab<?php echo $tab === $slug ? ' is-active' : ''; ?>"

                                       role="tab"

                                       aria-selected="<?php echo $tab === $slug ? 'true' : 'false'; ?>">

                                        <span class="dashicons <?php echo esc_attr( $meta['icon'] ); ?> aiap-tab-icon" aria-hidden="true"></span>

                                        <span class="aiap-tab-label"><?php echo esc_html( self::tab_label( $meta['key'] ) ); ?></span>

                                    </a>

                                <?php endforeach; ?>

                            </div>

                        <?php endforeach; ?>

                    </nav>

                </aside>



                <main class="aiap-main">

                <div class="aiap-tab-panel">

            <?php

            switch ( $tab ) {

                case AIAP_Tabs::QUICK_POST:     AIAP_Admin_View_QuickPost::render_quick_post(); break;

                case AIAP_Tabs::PROVIDER:       AIAP_Admin_View_Provider::render_provider( $s ); break;

                case AIAP_Tabs::IMAGE_PROVIDER: AIAP_Admin_View_Provider::render_image_provider( $s, $im ); break;

                case AIAP_Tabs::IMAGE_SETTINGS: AIAP_Admin_View_Provider::render_image_settings( $im ); break;

                case AIAP_Tabs::PROMPTS:        AIAP_Admin_View_Tasks::render_tasks( $s ); break;

                case AIAP_Tabs::SYSTEM_PROMPT:  AIAP_Admin_View_Prompt::render_system_prompt( $s ); break;

                case AIAP_Tabs::RSS_FEEDS:      AIAP_Admin_RSS::render_rss_feeds(); break;

                case AIAP_Tabs::LOGS:           AIAP_Admin_View_Logs::render_logs(); break;

                case AIAP_Tabs::RUN_LOGS:       AIAP_Admin_View_Logs::render_run_logs(); break;

                default:               AIAP_Admin_View_Settings::render_settings( $s ); break;

            }

            ?>

                </div>

                </main>

            </div><!-- /.aiap-shell -->

        </div>

        <?php

    }



    private static function render_notices() {

        if ( ! empty( $_GET['aiap_msg'] ) ) {

            $msg     = sanitize_text_field( rawurldecode( wp_unslash( $_GET['aiap_msg'] ) ) );

            $raw     = sanitize_key( wp_unslash( $_GET['aiap_type'] ?? 'success' ) );

            $allowed = array( 'success', 'error', 'warning' );

            $type    = in_array( $raw, $allowed, true ) ? $raw : 'success';

            printf( '<div class="notice notice-%s is-dismissible"><p>%s</p></div>', esc_attr( $type ), esc_html( $msg ) );

        }

    }



}

