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

/**
 * RSS フィードライブラリ（CRUD + フェッチ + 未使用記事選別）
 *
 * aiap_rss_feeds option を独占管理する（直接 get_option を呼ばないこと）。
 *
 * 構造:
 *   array(
 *       array(
 *           'id'              => 'feed_xxx',
 *           'name'            => '表示名',
 *           'url'             => 'https://...',
 *           'enabled'         => 1,
 *           'created_at'      => 'YYYY-MM-DD HH:MM:SS',
 *           'last_fetched_at' => 'YYYY-MM-DD HH:MM:SS' or '',
 *           'last_error'      => '',
 *       ),
 *       ...
 *   )
 */
class AIAP_RSS_Feeds {

    const OPTION_KEY          = 'aiap_rss_feeds';
    const FEEDS_MAX           = 30;
    const COUNT_MAX           = 10;
    const CACHE_TTL_SECONDS   = 3600;
    const FETCH_TIMEOUT       = 15;

    /**
     * 全フィードを取得
     *
     * @return array
     */
    public static function get_all() {
        $feeds = get_option( self::OPTION_KEY, array() );
        return is_array( $feeds ) ? $feeds : array();
    }

    /**
     * 全フィードを保存（autoload=no）
     *
     * @param array $feeds
     */
    private static function save_all( array $feeds ) {
        update_option( self::OPTION_KEY, array_values( $feeds ), false );
    }

    /**
     * ID でフィードを取得
     *
     * @param string $feed_id
     * @return array|null
     */
    public static function get_by_id( $feed_id ) {
        if ( $feed_id === '' ) {
            return null;
        }
        foreach ( self::get_all() as $feed ) {
            if ( isset( $feed['id'] ) && $feed['id'] === $feed_id ) {
                return $feed;
            }
        }
        return null;
    }

    /**
     * フィードを追加（FEEDS_MAX 件まで）
     *
     * @param array $input { name, url, enabled }
     * @return string|WP_Error 新規 feed_id or エラー
     */
    public static function add( $input ) {
        $name    = isset( $input['name'] ) ? sanitize_text_field( (string) $input['name'] ) : '';
        $url     = isset( $input['url'] ) ? esc_url_raw( (string) $input['url'] ) : '';
        $enabled = ! empty( $input['enabled'] ) ? 1 : 0;

        if ( $name === '' ) {
            return new WP_Error( 'rss_invalid_name', __( 'Feed name is required.', 'ai-auto-poster' ) );
        }
        if ( $url === '' || ! wp_http_validate_url( $url ) ) {
            return new WP_Error( 'rss_invalid_url', __( 'Feed URL is invalid (must be http or https).', 'ai-auto-poster' ) );
        }

        $feeds = self::get_all();
        if ( count( $feeds ) >= self::FEEDS_MAX ) {
            return new WP_Error( 'rss_feeds_max', sprintf(
                /* translators: %d = max feed count */
                __( 'Cannot register more than %d RSS feeds.', 'ai-auto-poster' ),
                self::FEEDS_MAX
            ) );
        }

        $id     = 'feed_' . uniqid();
        $feeds[] = array(
            'id'              => $id,
            'name'            => $name,
            'url'             => $url,
            'enabled'         => $enabled,
            'created_at'      => current_time( 'mysql' ),
            'last_fetched_at' => '',
            'last_error'      => '',
        );
        self::save_all( $feeds );
        return $id;
    }

    /**
     * フィードを更新（id は変更不可、created_at は保持）
     *
     * @param string $feed_id
     * @param array  $input    { name?, url?, enabled? }
     * @return true|WP_Error
     */
    public static function update( $feed_id, $input ) {
        $feeds = self::get_all();
        $found = false;
        foreach ( $feeds as &$feed ) {
            if ( ! isset( $feed['id'] ) || $feed['id'] !== $feed_id ) {
                continue;
            }
            $found = true;
            if ( isset( $input['name'] ) ) {
                $name = sanitize_text_field( (string) $input['name'] );
                if ( $name === '' ) {
                    return new WP_Error( 'rss_invalid_name', __( 'Feed name is required.', 'ai-auto-poster' ) );
                }
                $feed['name'] = $name;
            }
            if ( isset( $input['url'] ) ) {
                $url = esc_url_raw( (string) $input['url'] );
                if ( $url === '' || ! wp_http_validate_url( $url ) ) {
                    return new WP_Error( 'rss_invalid_url', __( 'Feed URL is invalid (must be http or https).', 'ai-auto-poster' ) );
                }
                $feed['url'] = $url;
            }
            if ( array_key_exists( 'enabled', $input ) ) {
                $feed['enabled'] = ! empty( $input['enabled'] ) ? 1 : 0;
            }
            break;
        }
        unset( $feed );
        if ( ! $found ) {
            return new WP_Error( 'rss_not_found', __( 'Feed not found.', 'ai-auto-poster' ) );
        }
        self::save_all( $feeds );
        return true;
    }

    /**
     * 取得成功時刻と直近エラーを更新
     *
     * @param string $feed_id
     * @param string $when    YYYY-MM-DD HH:MM:SS
     */
    public static function update_last_fetched( $feed_id, $when ) {
        $feeds = self::get_all();
        foreach ( $feeds as &$feed ) {
            if ( isset( $feed['id'] ) && $feed['id'] === $feed_id ) {
                $feed['last_fetched_at'] = $when;
                $feed['last_error']      = '';
                self::save_all( $feeds );
                return;
            }
        }
    }

    /**
     * 直近エラーを記録
     *
     * @param string $feed_id
     * @param string $message
     */
    public static function update_last_error( $feed_id, $message ) {
        $feeds = self::get_all();
        foreach ( $feeds as &$feed ) {
            if ( isset( $feed['id'] ) && $feed['id'] === $feed_id ) {
                $feed['last_error'] = sanitize_text_field( $message );
                self::save_all( $feeds );
                return;
            }
        }
    }

    /**
     * フィードを削除（履歴も連動削除）
     *
     * @param string $feed_id
     * @return true|WP_Error
     */
    public static function delete( $feed_id ) {
        $feeds = self::get_all();
        $found = false;
        $new   = array();
        foreach ( $feeds as $feed ) {
            if ( isset( $feed['id'] ) && $feed['id'] === $feed_id ) {
                $found = true;
                continue;
            }
            $new[] = $feed;
        }
        if ( ! $found ) {
            return new WP_Error( 'rss_not_found', __( 'Feed not found.', 'ai-auto-poster' ) );
        }
        self::save_all( $new );
        if ( class_exists( 'AIAP_RSS_State' ) ) {
            AIAP_RSS_State::purge_feed( $feed_id );
        }
        return true;
    }

    /**
     * 接続テスト（指定 URL の取得が成功するかと、先頭 1 件のサンプルを返す）
     *
     * @param string $url
     * @return array|WP_Error  array( 'item_count' => int, 'sample_title' => string ) or WP_Error
     */
    public static function test_connection( $url ) {
        $url = esc_url_raw( (string) $url );
        if ( $url === '' || ! wp_http_validate_url( $url ) ) {
            return new WP_Error( 'rss_invalid_url', __( 'Feed URL is invalid (must be http or https).', 'ai-auto-poster' ) );
        }
        // 接続テストはキャッシュを無視してリアルタイム取得したいので、TTL=0 を一時的に適用
        $ttl_filter = function () { return 0; };
        add_filter( 'wp_feed_cache_transient_lifetime', $ttl_filter );
        $rss = fetch_feed( $url );
        remove_filter( 'wp_feed_cache_transient_lifetime', $ttl_filter );

        if ( is_wp_error( $rss ) ) {
            return $rss;
        }
        $count  = $rss->get_item_quantity( 50 );
        $items  = $rss->get_items( 0, 1 );
        $sample = '';
        if ( ! empty( $items ) ) {
            $sample = wp_strip_all_tags( (string) $items[0]->get_title() );
            $sample = mb_substr( $sample, 0, 100 );
        }
        return array(
            'item_count'   => (int) $count,
            'sample_title' => $sample,
        );
    }

    /**
     * 選択フィードから未使用記事を最大 $count 件、公開日新しい順で返す
     *
     * フィード単位の取得失敗は当該フィードのみスキップし、他フィードで継続する。
     * SimplePie の transient キャッシュ TTL を CACHE_TTL_SECONDS に一時設定。
     *
     * @param array $feed_ids 選択フィードの id 配列
     * @param int   $count    取得件数（1〜COUNT_MAX にクランプ）
     * @return array 記事配列。各要素は feed_id/feed_name/guid/title/description/link/pub_date/pub_ts を持つ
     */
    public static function pick_unused( array $feed_ids, $count ) {
        $count = (int) $count;
        if ( $count < 1 ) {
            $count = 1;
        }
        if ( $count > self::COUNT_MAX ) {
            $count = self::COUNT_MAX;
        }
        if ( empty( $feed_ids ) ) {
            return array();
        }

        $ttl_filter = function () { return self::CACHE_TTL_SECONDS; };
        $all_articles = array();

        foreach ( $feed_ids as $feed_id ) {
            $feed_id = (string) $feed_id;
            $feed    = self::get_by_id( $feed_id );
            if ( ! $feed || empty( $feed['enabled'] ) ) {
                continue;
            }

            add_filter( 'wp_feed_cache_transient_lifetime', $ttl_filter );
            $rss = fetch_feed( $feed['url'] );
            remove_filter( 'wp_feed_cache_transient_lifetime', $ttl_filter );

            if ( is_wp_error( $rss ) ) {
                AIAP_Logger::add( 'error', 'log_rss_fetch_failed', array(
                    $feed['name'], $rss->get_error_message(),
                ) );
                self::update_last_error( $feed_id, $rss->get_error_message() );
                continue;
            }

            self::update_last_fetched( $feed_id, current_time( 'mysql' ) );

            $max_items = $rss->get_item_quantity( 50 );
            foreach ( $rss->get_items( 0, $max_items ) as $item ) {
                $guid = trim( (string) $item->get_id() );
                if ( $guid === '' ) {
                    $guid = (string) $item->get_permalink();
                }
                if ( $guid === '' ) {
                    continue;
                }
                if ( class_exists( 'AIAP_RSS_State' ) ) {
                    if ( AIAP_RSS_State::is_used( $feed_id, $guid ) ) {
                        continue;
                    }
                    if ( AIAP_RSS_State::is_reserved( $feed_id, $guid ) ) {
                        // 他経路（並行 cron / Quick Post）が予約中の記事はスキップ。
                        // 並行採用レース対策（spec: 2026-05-15-concurrency-fixes-design）。
                        continue;
                    }
                }

                $pub_ts   = (int) $item->get_date( 'U' );
                $pub_date = $pub_ts > 0 ? wp_date( 'Y-m-d H:i:s', $pub_ts ) : '';

                $all_articles[] = array(
                    'feed_id'     => $feed_id,
                    'feed_name'   => isset( $feed['name'] ) ? $feed['name'] : '',
                    'guid'        => $guid,
                    'title'       => wp_strip_all_tags( (string) $item->get_title() ),
                    'description' => wp_strip_all_tags( (string) $item->get_description() ),
                    'link'        => esc_url_raw( (string) $item->get_permalink() ),
                    'pub_date'    => $pub_date,
                    'pub_ts'      => $pub_ts,
                );
            }
        }

        // 公開日の新しい順
        usort( $all_articles, function ( $a, $b ) {
            return $b['pub_ts'] <=> $a['pub_ts'];
        } );

        $picked = array_slice( $all_articles, 0, $count );

        // 採用候補を予約（並行する他経路が同じ記事を pick できないようにする）
        if ( ! empty( $picked ) && class_exists( 'AIAP_RSS_State' ) ) {
            $picked_by_feed = array();
            foreach ( $picked as $a ) {
                $fid  = isset( $a['feed_id'] ) ? (string) $a['feed_id'] : '';
                $guid = isset( $a['guid'] )    ? (string) $a['guid']    : '';
                if ( $fid === '' || $guid === '' ) {
                    continue;
                }
                $picked_by_feed[ $fid ][] = $guid;
            }
            foreach ( $picked_by_feed as $fid => $guids ) {
                AIAP_RSS_State::reserve( $fid, $guids );
            }
        }

        return $picked;
    }
}
