<?php
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * 管理画面のフォーム保存コントローラ。
 *
 * 設定・プロバイダ・画像・システムプロンプト・タスクの各保存フォーム
 * (admin_post_aiap_* アクション) を処理するハンドラ群を集約する。描画ロジック
 * (render_*) からリクエスト処理を分離するための入れ物 (Phase 3 ステップ3)。
 *
 * 各ハンドラは外部ドメインクラス (AIAP_AI_Client / AIAP_Image_Client /
 * AIAP_Prompts 等) に委譲し、共有グルーである権限/nonce 検証とリダイレクトは
 * AIAP_Admin::verify() / AIAP_Admin::redirect() を明示的に呼ぶ。
 *
 * フック登録 (admin_post_aiap_*) は AIAP_Admin::init() が行う。
 */
class AIAP_Admin_Controller {

    /**
     * 「投稿し損ねたとき」セクションの保存。
     * cron_recover_missed / cron_recover_max のみ。
     * cron スケジュール変更を伴うので reschedule() を呼ぶ。
     */
    public static function handle_save_recover() {
        AIAP_Admin::verify();
        $s = AIAP_AI_Client::get_settings();
        $is_pro = AIAP_License::is_pro();
        $s['cron_recover_missed'] = isset( $_POST['cron_recover_missed'] ) ? 1 : 0;
        if ( $is_pro ) {
            $s['cron_recover_max'] = max( 1, min( 5, absint( wp_unslash( $_POST['cron_recover_max'] ?? 3 ) ) ) );
        } else {
            // Free プラン: 1 件固定 (UI の disabled を bypass されても強制)
            $s['cron_recover_max'] = 1;
        }
        AIAP_AI_Client::update_settings( $s );
        AIAP_Scheduler::reschedule();
        AIAP_Admin::redirect( AIAP_Tabs::SETTINGS, __( 'Settings saved.', 'ai-auto-poster' ) );
    }

    /**
     * 「投稿の出力」セクションの保存。
     * post_status / post_category / post_author のみ。
     */
    public static function handle_save_output() {
        AIAP_Admin::verify();
        $s = AIAP_AI_Client::get_settings();
        $posted_status        = sanitize_text_field( wp_unslash( $_POST['post_status'] ?? '' ) );
        $s['post_status']     = in_array( $posted_status, array('draft','pending','publish'), true ) ? $posted_status : 'draft';
        $s['post_category']   = absint( wp_unslash( $_POST['post_category'] ?? 0 ) );
        // Bug fix (audit r9 self-audit): post_author は absint だけだと存在しないユーザー ID を
        // 受け入れてしまう。設定後に管理者を削除した・user_id を手で書き換えた等のケースで、
        // wp_insert_post( ['post_author' => 999] ) が post_author を NULL のまま保存し、
        // フロントエンドで「by 」と作者欄が空になる事故が起きる。get_userdata でユーザーが
        // 存在することを確認し、存在しない場合は既存値(あれば)を維持、無ければ現在の管理者に
        // フォールバックする。
        $posted_author = absint( wp_unslash( $_POST['post_author'] ?? 1 ) );
        if ( $posted_author > 0 && get_userdata( $posted_author ) !== false ) {
            $s['post_author'] = $posted_author;
        } else {
            // 既存値が有効ならそれを維持。なければ現在の管理者にフォールバック。
            $existing_author = isset( $s['post_author'] ) ? (int) $s['post_author'] : 0;
            if ( $existing_author <= 0 || get_userdata( $existing_author ) === false ) {
                $s['post_author'] = (int) get_current_user_id();
            }
        }
        AIAP_AI_Client::update_settings( $s );
        AIAP_Admin::redirect( AIAP_Tabs::SETTINGS, __( 'Settings saved.', 'ai-auto-poster' ) );
    }

    /**
     * 「重複記事を防ぐ」セクションの保存。Pro のみフォーム反映。
     */
    public static function handle_save_dedup() {
        AIAP_Admin::verify();
        $s = AIAP_AI_Client::get_settings();
        $is_pro = AIAP_License::is_pro();
        // Free は既存値を保持 (UI 非活性 + サーバ側でも反映しない)
        if ( $is_pro ) {
            $s['dedup_enabled']   = isset( $_POST['dedup_enabled'] ) ? 1 : 0;
            $s['dedup_step1']     = isset( $_POST['dedup_step1'] ) ? 1 : 0;
            $s['dedup_step2']     = isset( $_POST['dedup_step2'] ) ? 1 : 0;
            $s['dedup_max_retry'] = max( 1, min( 10, absint( wp_unslash( $_POST['dedup_max_retry'] ?? 3 ) ) ) );
            $sens                 = sanitize_text_field( wp_unslash( $_POST['dedup_sensitivity'] ?? 'medium' ) );
            $s['dedup_sensitivity'] = in_array( $sens, array( 'low', 'medium', 'high' ), true ) ? $sens : 'medium';
        }
        AIAP_AI_Client::update_settings( $s );
        AIAP_Admin::redirect( AIAP_Tabs::SETTINGS, __( 'Settings saved.', 'ai-auto-poster' ) );
    }

    /**
     * 「SEO最適化」セクションの保存。Pro のみフォーム反映。
     */
    public static function handle_save_seo() {
        AIAP_Admin::verify();
        $s = AIAP_AI_Client::get_settings();
        $is_pro = AIAP_License::is_pro();
        $seo_fields = array('seo_enabled','seo_meta_desc','seo_heading','seo_focus_kw','seo_faq','seo_toc','seo_template','seo_img_alt','seo_img_webp','seo_internal_link','seo_auto_tag','seo_related','seo_schema','seo_ogp','seo_canonical','seo_plugin_link','seo_reading_time','seo_sitemap_ping','seo_llms_txt');
        // Free は既存値を保持 (UI 非活性 + サーバ側でも反映しない)
        if ( $is_pro ) {
            foreach ( $seo_fields as $f ) $s[ $f ] = isset( $_POST[ $f ] ) ? 1 : 0;
        }
        AIAP_AI_Client::update_settings( $s );
        AIAP_Admin::redirect( AIAP_Tabs::SETTINGS, __( 'Settings saved.', 'ai-auto-poster' ) );
    }

    public static function handle_save_provider() {
        AIAP_Admin::verify();
        $s   = AIAP_AI_Client::get_settings();
        $tab = sanitize_key( wp_unslash( $_POST['provider_tab'] ?? ( $s['provider'] ?? 'claude' ) ) );
        if ( ! in_array( $tab, AIAP_AI_Providers::get_providers(), true ) ) $tab = 'claude';

        // APIキー: 空文字 or '*' のみ(マスク表示)は「未変更」扱いで既存値を保持。
        foreach ( array('claude_api_key','openai_api_key','gemini_api_key','mistral_api_key','groq_api_key','deepseek_api_key','openrouter_api_key') as $f ) {
            if ( isset( $_POST[ $f ] ) ) {
                $val = trim( (string) wp_unslash( $_POST[ $f ] ) );
                // '*' のみの値はマスク表示なので保存しない
                if ( $val !== '' && $val !== str_repeat( '*', strlen( $val ) ) ) {
                    $s[ $f ] = $val;
                }
            }
        }

        // 「使用するプロバイダ」の切替
        if ( ! empty( $_POST['provider'] ) ) {
            $posted_provider = sanitize_key( wp_unslash( $_POST['provider'] ) );
            if ( in_array( $posted_provider, AIAP_AI_Providers::get_providers(), true ) ) {
                $s['provider'] = $posted_provider;
            }
        }
        foreach ( array('claude_model','openai_model','gemini_model','mistral_model','groq_model','deepseek_model','openrouter_model') as $f ) {
            if ( isset( $_POST[ $f ] ) ) $s[ $f ] = sanitize_text_field( wp_unslash( $_POST[ $f ] ) );
        }
        foreach ( AIAP_AI_Providers::get_providers() as $provider ) {
            $custom = sanitize_text_field( wp_unslash( $_POST[ $provider . '_custom_model' ] ?? '' ) );
            // カスタムモデルは英数字・ハイフン・スラッシュ・コロン・ピリオドのみ、最大128文字
            if ( $custom !== '' && preg_match( '#^[A-Za-z0-9._/:\-]{1,128}$#', $custom ) && isset( $s[ $provider . '_model' ] ) ) {
                $s[ $provider . '_model' ] = $custom;
            }
        }
        AIAP_AI_Client::update_settings( $s );
        AIAP_Admin::redirect( AIAP_Tabs::PROVIDER . '&sub=' . $tab, __( 'Settings saved.', 'ai-auto-poster' ) );
    }

    public static function handle_save_image_provider() {
        AIAP_Admin::verify();
        $im  = AIAP_Image_Client::get_settings();
        $tab = sanitize_key( wp_unslash( $_POST['image_tab'] ?? 'dalle3' ) );
        if ( ! isset( AIAP_Admin::$img_icons[ $tab ] ) ) $tab = 'dalle3';
        $src = array_keys( AIAP_Image_Client::get_sources() );
        if ( isset( $_POST['image_source'] ) ) {
            $posted_src = sanitize_text_field( wp_unslash( $_POST['image_source'] ) );
            if ( in_array( $posted_src, $src, true ) ) {
                $im['image_source'] = $posted_src;
            }
        }
        // APIキー: 空文字 or '*' のみ(マスク表示)は「未変更」扱い
        foreach ( array('image_openai_api_key','image_gemini_api_key','stability_api_key','flux_api_key','unsplash_access_key','pixabay_api_key','pexels_api_key') as $f ) {
            if ( isset( $_POST[ $f ] ) ) {
                $val = trim( (string) wp_unslash( $_POST[ $f ] ) );
                if ( $val !== '' && $val !== str_repeat( '*', strlen( $val ) ) ) {
                    $im[ $f ] = $val;
                }
            }
        }
        if ( isset( $_POST['stability_model'] ) ) $im['stability_model'] = sanitize_text_field( wp_unslash( $_POST['stability_model'] ) );
        if ( isset( $_POST['flux_model'] ) )       $im['flux_model']      = sanitize_text_field( wp_unslash( $_POST['flux_model'] ) );

        // 画像プロバイダ(dalle3/imagen)のモデル選択保存 (スタイル検証より前に確定させる)
        foreach ( array( 'dalle3', 'imagen' ) as $prov ) {
            $field = $prov . '_model';
            if ( isset( $_POST[ $field ] ) ) {
                $im[ $field ] = sanitize_text_field( wp_unslash( $_POST[ $field ] ) );
            }
        }

        // プロバイダごとのスタイル保存 (そのプロバイダの現在モデルがサポートするスタイルのみ受け入れる)
        foreach ( array( 'dalle3', 'imagen', 'stability', 'flux' ) as $prov ) {
            $field = $prov . '_style';
            if ( isset( $_POST[ $field ] ) ) {
                $val = sanitize_text_field( wp_unslash( $_POST[ $field ] ) );
                $current_model = $im[ $prov . '_model' ] ?? '';
                $allowed = array_keys( AIAP_Image_Styles::get_provider_styles( $prov, $current_model ) );
                if ( in_array( $val, $allowed, true ) ) {
                    $im[ $field ] = $val;
                }
                // サポート外の値が送られてきた場合は保存せず既存値を維持
            }
        }

        // 仕様変更: 「デフォルト画像ソースとして使用する」トグルを廃止したため、
        // image_enabled は常に 1 に固定する。タスク/手動側の featured_image_mode が
        // 'on'/'off' の二択で個別に制御する。
        $im['image_enabled'] = 1;
        AIAP_Image_Client::update_settings( $im );
        AIAP_Admin::redirect( AIAP_Tabs::IMAGE_PROVIDER . '&sub=' . $tab, __( 'Settings saved.', 'ai-auto-poster' ) );
    }

    public static function handle_save_image_settings() {
        AIAP_Admin::verify();
        $im = AIAP_Image_Client::get_settings();
        // image_preset は UI ヒント。許可リストで検証。
        $allowed_presets = array( 'blog', 'sns', 'lite', 'custom' );
        $posted_preset   = sanitize_text_field( wp_unslash( $_POST['image_preset'] ?? 'custom' ) );
        $im['image_preset'] = in_array( $posted_preset, $allowed_presets, true ) ? $posted_preset : 'custom';

        // image_format / image_quality は許可リストで厳密検証
        $posted_format  = sanitize_text_field( wp_unslash( $_POST['image_format'] ?? '16:9' ) );
        $posted_quality = sanitize_text_field( wp_unslash( $_POST['image_quality'] ?? 'low' ) );
        $im['image_format']    = in_array( $posted_format, AIAP_Admin::$image_format_keys, true ) ? $posted_format : '16:9';
        $im['image_quality']   = in_array( $posted_quality, AIAP_Admin::$image_quality_keys, true ) ? $posted_quality : 'low';
        $im['image_max_width'] = max( 100, min( 4096, absint( wp_unslash( $_POST['image_max_width'] ?? 1200 ) ) ) );
        $im['image_compress']  = max( 1, min( 100, absint( wp_unslash( $_POST['image_compress'] ?? 85 ) ) ) );
        AIAP_Image_Client::update_settings( $im );
        AIAP_Admin::redirect( AIAP_Tabs::IMAGE_SETTINGS, __( 'Settings saved.', 'ai-auto-poster' ) );
    }

    public static function handle_save_system_prompt() {
        AIAP_Admin::verify();
        $s = AIAP_AI_Client::get_settings();
        $posted_lang = sanitize_text_field( wp_unslash( $_POST['content_language'] ?? 'ja' ) );
        // 言語コードは get_language_options のキーと一致するもののみ受け入れる
        $valid_langs = array_keys( AIAP_AI_Client::get_language_options() );
        $s['content_language'] = in_array( $posted_lang, $valid_langs, true ) ? $posted_lang : 'ja';
        $s['content_length']   = isset( $_POST['content_length'] ) && wp_unslash( $_POST['content_length'] ) !== ''
            ? max( 0, min( 10000, absint( wp_unslash( $_POST['content_length'] ) ) ) )
            : 0;
        $s['system_prompt']    = sanitize_textarea_field( wp_unslash( $_POST['system_prompt'] ?? '' ) );
        AIAP_AI_Client::update_settings( $s );
        // Bug fix: 旧実装は 'prompts' (タスク一覧タブ) にリダイレクトしていたため、
        // システムプロンプト保存後にタスクメニューへ飛んでいた。System Prompt は Phase 9 で
        // 独立タブ ('system_prompt') に分離済みなので、保存後は自タブに戻す。
        AIAP_Admin::redirect( AIAP_Tabs::SYSTEM_PROMPT, __( 'Settings saved.', 'ai-auto-poster' ) );
    }

    public static function handle_add_task() {
        AIAP_Admin::verify();
        $prompt = sanitize_textarea_field( wp_unslash( $_POST['prompt'] ?? '' ) );
        if ( empty( $prompt ) ) { AIAP_Admin::redirect( AIAP_Tabs::PROMPTS, __( 'Prompt is required.', 'ai-auto-poster' ), 'error' ); return; }
        $language = sanitize_text_field( wp_unslash( $_POST['task_content_language'] ?? '' ) );
        $valid_langs = array_keys( AIAP_AI_Client::get_language_options() );
        if ( ! in_array( $language, $valid_langs, true ) ) {
            AIAP_Admin::redirect( AIAP_Tabs::PROMPTS, __( 'Content language is required.', 'ai-auto-poster' ), 'error' );
            return;
        }
        $schedule_raw = isset( $_POST['schedule'] ) ? wp_unslash( $_POST['schedule'] ) : array();
        $invalid_schedule = AIAP_Prompts::count_invalid_in_schedule_form( $schedule_raw );
        $schedule = AIAP_Prompts::parse_schedule_from_form( $schedule_raw );
        $length = isset( $_POST['task_content_length'] ) && wp_unslash( $_POST['task_content_length'] ) !== ''
            ? max( 0, min( 10000, absint( wp_unslash( $_POST['task_content_length'] ) ) ) )
            : 0;
        $image_mode = AIAP_Prompts::sanitize_image_mode( wp_unslash( $_POST['task_featured_image_mode'] ?? 'on' ) );
        // RSS 設定
        $rss_input = array(
            'rss_enabled'  => isset( $_POST['rss_enabled'] )  ? $_POST['rss_enabled']  : 0,
            'rss_feed_ids' => isset( $_POST['rss_feed_ids'] ) ? (array) wp_unslash( $_POST['rss_feed_ids'] ) : array(),
            'rss_count'    => isset( $_POST['rss_count'] )    ? $_POST['rss_count']    : 5,
            'rss_mode'     => isset( $_POST['rss_mode'] )     ? sanitize_text_field( wp_unslash( $_POST['rss_mode'] ) ) : 'pick',
            'rss_fallback' => isset( $_POST['rss_fallback'] ) ? sanitize_text_field( wp_unslash( $_POST['rss_fallback'] ) ) : 'skip',
        );
        $rss_normalized = AIAP_Prompts::get_rss_settings( $rss_input );
        // #47: ランダム投稿モード関連
        $random_mode      = AIAP_Prompts::sanitize_random_mode( $_POST['task_random_mode'] ?? 0 );
        $random_count_min = AIAP_Prompts::sanitize_random_count( $_POST['task_random_count_min'] ?? 0 );
        $random_count_max = AIAP_Prompts::sanitize_random_count( $_POST['task_random_count_max'] ?? 0 );
        if ( $random_count_max < $random_count_min ) { $random_count_max = $random_count_min; }
        // Phase 13: 旧 exclude_periods → 新 allowed_periods + min_interval_minutes
        $allowed_periods      = AIAP_Prompts::sanitize_allowed_periods( $_POST['task_allowed_periods'] ?? array() );
        $min_interval_minutes = AIAP_Prompts::sanitize_min_interval_minutes( $_POST['task_min_interval_minutes'] ?? 0 );
        $name             = sanitize_text_field( wp_unslash( $_POST['task_name'] ?? '' ) );
        AIAP_Prompts::add( $prompt, $schedule, isset( $_POST['task_enabled'] ) ? 1 : 0, $language, $length, $image_mode, $random_mode, $random_count_min, $random_count_max, $allowed_periods, $min_interval_minutes, $name );
        // RSS 設定を追加したタスクに反映（add() は ID を返さないため末尾タスクを更新）
        $all_tasks_after = AIAP_Prompts::get_all();
        $last_task = end( $all_tasks_after );
        if ( $last_task && isset( $last_task['id'] ) ) {
            AIAP_Prompts::update( $last_task['id'], $rss_normalized );
        }
        if ( $invalid_schedule > 0 ) {
            /* translators: %d = number of invalid schedule entries discarded */
            $msg = sprintf( __( 'Settings saved. %d invalid schedule time(s) were discarded. Use HH:MM (e.g. 09:00).', 'ai-auto-poster' ), $invalid_schedule );
            AIAP_Admin::redirect( AIAP_Tabs::PROMPTS, $msg, 'warning' );
            return;
        }
        AIAP_Admin::redirect( AIAP_Tabs::PROMPTS, __( 'Settings saved.', 'ai-auto-poster' ) );
    }

    public static function handle_edit_task() {
        AIAP_Admin::verify();
        $task_id = sanitize_text_field( wp_unslash( $_POST['task_id'] ?? '' ) );
        $prompt  = sanitize_textarea_field( wp_unslash( $_POST['prompt'] ?? '' ) );
        if ( empty( $task_id ) || empty( $prompt ) ) {
            AIAP_Admin::redirect( AIAP_Tabs::PROMPTS, __( 'Prompt is required.', 'ai-auto-poster' ), 'error' );
            return;
        }
        $language = sanitize_text_field( wp_unslash( $_POST['task_content_language'] ?? '' ) );
        $valid_langs = array_keys( AIAP_AI_Client::get_language_options() );
        if ( ! in_array( $language, $valid_langs, true ) ) {
            AIAP_Admin::redirect( AIAP_Tabs::PROMPTS, __( 'Content language is required.', 'ai-auto-poster' ), 'error' );
            return;
        }
        $length = isset( $_POST['task_content_length'] ) && wp_unslash( $_POST['task_content_length'] ) !== ''
            ? max( 0, min( 10000, absint( wp_unslash( $_POST['task_content_length'] ) ) ) )
            : 0;
        $image_mode = AIAP_Prompts::sanitize_image_mode( wp_unslash( $_POST['task_featured_image_mode'] ?? 'on' ) );
        // RSS 設定
        $rss_input = array(
            'rss_enabled'  => isset( $_POST['rss_enabled'] )  ? $_POST['rss_enabled']  : 0,
            'rss_feed_ids' => isset( $_POST['rss_feed_ids'] ) ? (array) wp_unslash( $_POST['rss_feed_ids'] ) : array(),
            'rss_count'    => isset( $_POST['rss_count'] )    ? $_POST['rss_count']    : 5,
            'rss_mode'     => isset( $_POST['rss_mode'] )     ? sanitize_text_field( wp_unslash( $_POST['rss_mode'] ) ) : 'pick',
            'rss_fallback' => isset( $_POST['rss_fallback'] ) ? sanitize_text_field( wp_unslash( $_POST['rss_fallback'] ) ) : 'skip',
        );
        $rss_normalized = AIAP_Prompts::get_rss_settings( $rss_input );
        // #47: ランダム投稿モード関連
        $random_mode      = AIAP_Prompts::sanitize_random_mode( $_POST['task_random_mode'] ?? 0 );
        $random_count_min = AIAP_Prompts::sanitize_random_count( $_POST['task_random_count_min'] ?? 0 );
        $random_count_max = AIAP_Prompts::sanitize_random_count( $_POST['task_random_count_max'] ?? 0 );
        if ( $random_count_max < $random_count_min ) { $random_count_max = $random_count_min; }
        // Phase 13: 旧 exclude_periods → 新 allowed_periods + min_interval_minutes
        $allowed_periods      = AIAP_Prompts::sanitize_allowed_periods( $_POST['task_allowed_periods'] ?? array() );
        $min_interval_minutes = AIAP_Prompts::sanitize_min_interval_minutes( $_POST['task_min_interval_minutes'] ?? 0 );
        $schedule_raw = isset( $_POST['schedule'] ) ? wp_unslash( $_POST['schedule'] ) : array();
        $invalid_schedule = AIAP_Prompts::count_invalid_in_schedule_form( $schedule_raw );
        AIAP_Prompts::update( $task_id, array(
            'name'                 => sanitize_text_field( wp_unslash( $_POST['task_name'] ?? '' ) ),
            'prompt'               => $prompt,
            'schedule'             => AIAP_Prompts::parse_schedule_from_form( $schedule_raw ),
            'enabled'              => isset( $_POST['task_enabled'] ) ? 1 : 0,
            'content_language'     => $language,
            'content_length'       => $length,
            'featured_image_mode'  => $image_mode,
            'random_mode'          => $random_mode,
            'random_count_min'     => $random_count_min,
            'random_count_max'     => $random_count_max,
            'allowed_periods'      => $allowed_periods,
            'min_interval_minutes' => $min_interval_minutes,
            'rss_enabled'          => $rss_normalized['rss_enabled'],
            'rss_feed_ids'         => $rss_normalized['rss_feed_ids'],
            'rss_count'            => $rss_normalized['rss_count'],
            'rss_mode'             => $rss_normalized['rss_mode'],
            'rss_fallback'         => $rss_normalized['rss_fallback'],
        ) );
        if ( $invalid_schedule > 0 ) {
            /* translators: %d = number of invalid schedule entries discarded */
            $msg = sprintf( __( 'Settings saved. %d invalid schedule time(s) were discarded. Use HH:MM (e.g. 09:00).', 'ai-auto-poster' ), $invalid_schedule );
            AIAP_Admin::redirect( AIAP_Tabs::PROMPTS, $msg, 'warning' );
            return;
        }
        AIAP_Admin::redirect( AIAP_Tabs::PROMPTS, __( 'Settings saved.', 'ai-auto-poster' ) );
    }

    public static function handle_toggle_task() { AIAP_Admin::verify(); AIAP_Prompts::toggle( sanitize_text_field( wp_unslash( $_POST['task_id'] ?? '' ) ) ); AIAP_Admin::redirect( AIAP_Tabs::PROMPTS, __( 'Settings saved.', 'ai-auto-poster' ) ); }
    public static function handle_delete_task() { AIAP_Admin::verify(); AIAP_Prompts::delete( sanitize_text_field( wp_unslash( $_POST['task_id'] ?? '' ) ) ); AIAP_Admin::redirect( AIAP_Tabs::PROMPTS, __( 'Settings saved.', 'ai-auto-poster' ) ); }
}
