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

/**
 * ライセンス・利用制限管理
 *
 * Freemius を介して Free / Pro プランを判定し、Free プランの場合は
 * 暦月単位で投稿数を制限する。
 *
 * Pro 機能（現在）:
 *   - 投稿し損ねた予定の処理(後処理)
 *   - SEO 最適化(全項目)
 *
 * Free プランの制限:
 *   - 月10投稿まで(Quick Post + cron 投稿の合計)
 *
 * Freemius SDK が未配置の場合は常に Free 扱いで動作する。
 * これにより SDK 統合前でも開発・テストが可能。
 *
 * 月跨ぎは get_counter() で自動リセット(暦月の 1 日 00:00 を境界)。
 */
class AIAP_License {

    /**
     * 投稿カウンタ option キー
     */
    const OPTION_COUNTER = 'aiap_post_counter';

    /**
     * Free プランの月間投稿上限
     */
    const FREE_MONTHLY_LIMIT = 10;

    /**
     * Pro 機能識別子
     */
    const FEATURE_SCHEDULE_RECOVERY = 'schedule_recovery';
    const FEATURE_SEO               = 'seo';

    // ====================================================================
    // ライセンス判定
    // ====================================================================

    /**
     * Pro 機能が利用可能か?(有料 or トライアル中)
     *
     * Freemius が初期化済みでない場合は常に false (= Free 扱い)。
     */
    public static function is_pro() {
        if ( function_exists( 'aiap_fs' ) ) {
            $fs = aiap_fs();
            // 有料プラン or トライアル中なら Pro 機能解禁
            if ( method_exists( $fs, 'can_use_premium_code__premium_only' ) && $fs->can_use_premium_code__premium_only() ) {
                return true;
            }
            if ( method_exists( $fs, 'is_paying' ) && $fs->is_paying() ) {
                return true;
            }
            if ( method_exists( $fs, 'is_trial' ) && $fs->is_trial() ) {
                return true;
            }
        }
        return false;
    }

    /**
     * トライアル中か?
     */
    public static function is_trial() {
        if ( function_exists( 'aiap_fs' ) && method_exists( aiap_fs(), 'is_trial' ) ) {
            return aiap_fs()->is_trial();
        }
        return false;
    }

    /**
     * トライアル残り日数(整数。トライアルでない場合は 0)
     */
    public static function trial_days_remaining() {
        if ( ! self::is_trial() ) {
            return 0;
        }
        $fs = aiap_fs();
        if ( ! method_exists( $fs, 'get_trial_plan' ) ) {
            return 0;
        }
        // Freemius は trial_ends を秒単位で持つ
        if ( method_exists( $fs, 'get_site' ) ) {
            $site = $fs->get_site();
            if ( $site && ! empty( $site->trial_ends ) ) {
                $ends   = strtotime( $site->trial_ends );
                $remain = $ends - time();
                return max( 0, (int) ceil( $remain / DAY_IN_SECONDS ) );
            }
        }
        return 0;
    }

    /**
     * 個別 Pro 機能の解禁判定
     *
     * 現状は is_pro() に集約しているが、将来的にプラン階層で分けられるよう
     * フィーチャ単位の API を提供しておく。
     */
    public static function has_feature( $feature ) {
        unset( $feature );
        return self::is_pro();
    }

    /**
     * アップグレード URL
     */
    public static function upgrade_url() {
        if ( function_exists( 'aiap_fs' ) && method_exists( aiap_fs(), 'get_upgrade_url' ) ) {
            return aiap_fs()->get_upgrade_url();
        }
        return admin_url( 'admin.php?page=ai-auto-poster' );
    }

    /**
     * トライアル開始 URL
     */
    public static function trial_url() {
        if ( function_exists( 'aiap_fs' ) && method_exists( aiap_fs(), 'get_trial_url' ) ) {
            return aiap_fs()->get_trial_url();
        }
        return self::upgrade_url();
    }

    /**
     * Freemius が利用可能か(SDK + キーが設定されているか)
     */
    public static function is_freemius_active() {
        return function_exists( 'aiap_fs' );
    }

    // ====================================================================
    // 月次投稿カウンタ
    // ====================================================================

    /**
     * 今月の投稿数(Free/Pro 問わず累積されるが、Pro では未使用)
     */
    public static function monthly_post_count() {
        $counter = self::get_counter();
        return (int) $counter['count'];
    }

    /**
     * 今月の上限(Pro なら PHP_INT_MAX)
     */
    public static function monthly_limit() {
        return self::is_pro() ? PHP_INT_MAX : self::FREE_MONTHLY_LIMIT;
    }

    /**
     * 今月の残り投稿可能数
     */
    public static function remaining_posts() {
        if ( self::is_pro() ) {
            return PHP_INT_MAX;
        }
        return max( 0, self::FREE_MONTHLY_LIMIT - self::monthly_post_count() );
    }

    /**
     * もう投稿できるか?
     */
    public static function can_post_more() {
        return self::remaining_posts() > 0;
    }

    /**
     * ダッシュボード向けヘルパー (Phase 1).
     * 直近 N 日間の日別投稿数を返す。
     * 形式: array( 'YYYY-MM-DD' => int, ... ) - 古い日付から新しい日付の順
     */
    public static function daily_post_counts( $days = 7 ) {
        $days = max( 1, min( 90, (int) $days ) );
        $tz   = wp_timezone();

        // 結果初期化 (古い→新しい)
        $result = array();
        for ( $i = $days - 1; $i >= 0; $i-- ) {
            $d = ( new DateTimeImmutable( 'now', $tz ) )->modify( "-{$i} day" );
            $result[ $d->format( 'Y-m-d' ) ] = 0;
        }

        // 期間の start/end (タイムゾーン考慮)
        $start = ( new DateTimeImmutable( 'now', $tz ) )->modify( '-' . ( $days - 1 ) . ' day' )->setTime( 0, 0, 0 );
        $end   = ( new DateTimeImmutable( 'now', $tz ) )->setTime( 23, 59, 59 );

        $q = new WP_Query( array(
            'post_type'      => 'post',
            // increment_post_count() は post_status を問わず加算する (= aiap_post_counter)。
            // ここの集計と整合させるため、draft/pending/private も含める (Phase 13 self-audit #1)。
            'post_status'    => array( 'publish', 'draft', 'pending', 'future', 'private' ),
            'posts_per_page' => -1,
            'fields'         => 'ids',
            'no_found_rows'  => true,
            'meta_key'       => '_aiap_generated',
            'meta_value'     => '1',
            'date_query'     => array(
                array(
                    'after'     => $start->format( 'Y-m-d H:i:s' ),
                    'before'    => $end->format( 'Y-m-d H:i:s' ),
                    'inclusive' => true,
                ),
            ),
        ) );
        if ( ! empty( $q->posts ) ) {
            foreach ( $q->posts as $pid ) {
                $post_date = get_post_field( 'post_date', $pid );
                if ( ! $post_date ) continue;
                $key = substr( $post_date, 0, 10 );
                if ( isset( $result[ $key ] ) ) {
                    $result[ $key ]++;
                }
            }
        }
        return $result;
    }

    /**
     * 本日の投稿数を自動 / 手動に分けて取得
     *
     * 区別の根拠:
     *   - 全 AI 生成 post には _aiap_generated=1 が付く
     *   - クイック投稿 (手動) には追加で _aiap_manual=1 が付く (admin.php の ajax_run_now_step 経由)
     *   - cron 経由 (自動) には _aiap_manual は付かない
     *
     * @return array {
     *   @type int auto    今日 0:00〜23:59 に作成された自動投稿の件数
     *   @type int manual  同 手動投稿の件数
     * }
     */
    public static function today_post_breakdown() {
        $tz    = wp_timezone();
        $start = ( new DateTimeImmutable( 'now', $tz ) )->setTime( 0, 0, 0 );
        $end   = ( new DateTimeImmutable( 'now', $tz ) )->setTime( 23, 59, 59 );

        $date_query = array(
            array(
                'after'     => $start->format( 'Y-m-d H:i:s' ),
                'before'    => $end->format( 'Y-m-d H:i:s' ),
                'inclusive' => true,
            ),
        );

        // 手動: _aiap_manual=1
        $manual_q = new WP_Query( array(
            'post_type'      => 'post',
            'post_status'    => array( 'publish', 'draft', 'pending', 'future', 'private' ),
            'posts_per_page' => -1,
            'fields'         => 'ids',
            'no_found_rows'  => true,
            'date_query'     => $date_query,
            'meta_query'     => array(
                array( 'key' => '_aiap_manual', 'value' => '1' ),
            ),
        ) );
        $manual_count = is_array( $manual_q->posts ) ? count( $manual_q->posts ) : 0;

        // 自動: _aiap_generated=1 かつ _aiap_manual が存在しない
        $auto_q = new WP_Query( array(
            'post_type'      => 'post',
            'post_status'    => array( 'publish', 'draft', 'pending', 'future', 'private' ),
            'posts_per_page' => -1,
            'fields'         => 'ids',
            'no_found_rows'  => true,
            'date_query'     => $date_query,
            'meta_query'     => array(
                'relation' => 'AND',
                array( 'key' => '_aiap_generated', 'value' => '1' ),
                array( 'key' => '_aiap_manual',    'compare' => 'NOT EXISTS' ),
            ),
        ) );
        $auto_count = is_array( $auto_q->posts ) ? count( $auto_q->posts ) : 0;

        return array(
            'auto'   => (int) $auto_count,
            'manual' => (int) $manual_count,
        );
    }

    /**
     * 投稿成功時にカウンタを 1 増やす
     *
     * Pro でもカウンタは記録する(将来分析用 + 解約時に Free に戻った際の
     * 連続性のため)。Free 制限は can_post_more() で判定する。
     *
     * 並列実行ロック:
     *   同時投稿が走った場合に「読み取り → +1 → 書き込み」が競合して
     *   カウンタが 1 増えない事故を防ぐため、ロックを取得してから更新する。
     *
     *   AIAP_Lock は wp_options への atomic INSERT (UNIQUE 制約) を使った
     *   真のロックなので、別プロセスが既に取得していれば必ず false を返す。
     *   - ロック取得成功時: try-finally で確実に release する
     *   - ロック取得失敗時 ($locked === false): それでもカウンタは更新する
     *     (理由: AIAP_Scheduler::run() の scheduler_run ロックにより cron 経路の
     *      並列実行は実質直列化されており、ここのロック失敗は極めて稀。
     *      取得失敗で更新を諦めると Free プランの投稿が過小カウントされ、
     *      実際の投稿数より少なく表示されるため、敢えて更新を続行する)
     */
    public static function increment_post_count() {
        // Bug fix (audit r9): release はトークンを渡してレース時の他者ロック誤削除を防ぐ。
        $lock_token = false;
        if ( class_exists( 'AIAP_Lock' ) ) {
            $lock_token = AIAP_Lock::acquire_with_retry( AIAP_Lock_Keys::POST_COUNTER, 10, 10, 20000 );
        }

        try {
            $counter           = self::get_counter();
            $counter['count']  = (int) $counter['count'] + 1;
            $counter['last']   = current_time( 'mysql' );
            update_option( self::OPTION_COUNTER, $counter, false );
        } finally {
            if ( $lock_token && class_exists( 'AIAP_Lock' ) ) {
                AIAP_Lock::release( AIAP_Lock_Keys::POST_COUNTER, $lock_token );
            }
        }
    }

    /**
     * 月跨ぎの自動リセット込みでカウンタを取得
     */
    private static function get_counter() {
        $current_month = wp_date( 'Y-m' );
        $counter       = get_option( self::OPTION_COUNTER, array() );

        if ( ! is_array( $counter ) || empty( $counter['month'] ) || $counter['month'] !== $current_month ) {
            // 月が変わった or 初回 → リセット
            $counter = array(
                'month' => $current_month,
                'count' => 0,
                'last'  => '',
            );
            update_option( self::OPTION_COUNTER, $counter, false );
        }

        return $counter;
    }

    /**
     * 制限到達時のログ用メッセージキー
     *
     * AIAP_Logger::add() に渡す英語ソース文字列。
     * 翻訳は po/mo 側で対応する。
     */
    public static function limit_reached_message() {
        return __(
            'Free plan monthly limit reached (10 posts). Upgrade to Pro for unlimited posting.',
            'ai-auto-poster'
        );
    }
}
