<?php

namespace WeDevs\Wpuf\Pro;

/**
 * Post Expiration
 *
 * Handles form-driven post expiration: persisting per-post expiration metadata
 * when a form is submitted and processing expired posts on the daily cron.
 *
 * Meta keys (defined by WPUF free for backward compatibility):
 *   - wpuf-post_expiration_date
 *   - wpuf-expired_post_status
 *   - wpuf-post_expiration_message
 *
 * @since 4.2.13
 */
class Post_Expiration {

    const META_EXPIRATION_DATE    = 'wpuf-post_expiration_date';
    const META_EXPIRED_STATUS     = 'wpuf-expired_post_status';
    const META_EXPIRATION_MESSAGE = 'wpuf-post_expiration_message';
    const META_ALREADY_EXPIRED    = 'wpuf-post_expired';

    public function __construct() {
        add_action( 'wpuf_after_post_submit_expiration_settings', [ $this, 'save_expiration_meta' ], 10, 3 );
        add_action( 'wpuf_remove_expired_post_hook', [ $this, 'process_expired_posts' ], 10 );
    }

    /**
     * Save per-post expiration metadata after a form submission.
     *
     * Called from WPUF free via do_action( 'wpuf_after_post_submit_expiration_settings', ... )
     * after the subscription-pack branch has been evaluated. Only writes metadata when
     * the form has expiration enabled and the user is NOT on an active subscription pack
     * that provides its own expiration.
     *
     * @param array      $form_settings               Form settings array.
     * @param int        $post_id                     Post ID of the newly submitted post.
     * @param array|string $user_wpuf_subscription_pack User subscription pack meta value, or empty.
     *
     * @return void
     */
    public function save_expiration_meta( $form_settings, $post_id, $user_wpuf_subscription_pack ) {
        if ( empty( $post_id ) ) {
            return;
        }

        if ( empty( $form_settings['expiration_settings']['enable_post_expiration'] ) ) {
            return;
        }

        $has_active_pack = ! empty( $user_wpuf_subscription_pack )
            && is_array( $user_wpuf_subscription_pack )
            && isset( $user_wpuf_subscription_pack['_enable_post_expiration'] )
            && isset( $user_wpuf_subscription_pack['expire'] )
            && strtotime( $user_wpuf_subscription_pack['expire'] ) >= time();

        // When an active subscription pack already set expiration, skip form-level writes.
        if ( $has_active_pack ) {
            return;
        }

        $time_value = isset( $form_settings['expiration_settings']['expiration_time_value'] ) ? $form_settings['expiration_settings']['expiration_time_value'] : '';
        $time_type  = isset( $form_settings['expiration_settings']['expiration_time_type'] ) ? $form_settings['expiration_settings']['expiration_time_type'] : 'day';

        if ( '' === $time_value ) {
            return;
        }

        $expire_date = gmdate( 'Y-m-d', strtotime( '+' . $time_value . ' ' . $time_type ) );
        update_post_meta( $post_id, self::META_EXPIRATION_DATE, $expire_date );

        if ( isset( $form_settings['expiration_settings']['expired_post_status'] ) ) {
            update_post_meta(
                $post_id,
                self::META_EXPIRED_STATUS,
                $form_settings['expiration_settings']['expired_post_status']
            );
        }

        if ( isset( $form_settings['expiration_settings']['enable_mail_after_expired'] )
            && 'on' === $form_settings['expiration_settings']['enable_mail_after_expired']
            && isset( $form_settings['expiration_settings']['post_expiration_message'] )
        ) {
            update_post_meta(
                $post_id,
                self::META_EXPIRATION_MESSAGE,
                $form_settings['expiration_settings']['post_expiration_message']
            );
        }
    }

    /**
     * Flip post status for posts whose expiration date has passed.
     *
     * Runs on the daily wpuf_remove_expired_post_hook cron. For each post where:
     *   - wpuf-post_expiration_date <= today, and
     *   - wpuf-post_expired guard meta is not set,
     * updates the post to the configured expired status (wpuf-expired_post_status),
     * sets the guard meta so the same post is never re-processed, and fires
     * wpuf_post_expired for downstream listeners (e.g., notification emails).
     *
     * @return void
     */
    public function process_expired_posts() {
        $today = gmdate( 'Y-m-d' );

        $expired_posts = get_posts(
            [
                'post_type'      => 'any',
                'post_status'    => 'any',
                'posts_per_page' => -1,
                'fields'         => 'ids',
                // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- daily cron, bounded by expiration date.
                'meta_query'     => [
                    'relation' => 'AND',
                    [
                        'key'     => self::META_EXPIRATION_DATE,
                        'value'   => $today,
                        'compare' => '<=',
                        'type'    => 'DATE',
                    ],
                    [
                        'key'     => self::META_ALREADY_EXPIRED,
                        'compare' => 'NOT EXISTS',
                    ],
                ],
            ]
        );

        if ( empty( $expired_posts ) ) {
            return;
        }

        foreach ( $expired_posts as $post_id ) {
            $new_status = get_post_meta( $post_id, self::META_EXPIRED_STATUS, true );

            if ( empty( $new_status ) ) {
                continue;
            }

            $previous_status = get_post_status( $post_id );

            if ( $previous_status === $new_status ) {
                // Already in the target state; still mark as processed so we stop revisiting.
                update_post_meta( $post_id, self::META_ALREADY_EXPIRED, gmdate( 'c' ) );
                continue;
            }

            $updated = wp_update_post(
                [
                    'ID'          => $post_id,
                    'post_status' => $new_status,
                ],
                true
            );

            if ( is_wp_error( $updated ) || ! $updated ) {
                continue;
            }

            update_post_meta( $post_id, self::META_ALREADY_EXPIRED, gmdate( 'c' ) );

            /**
             * Fires after a post has been transitioned to its expired status.
             *
             * @since 4.2.13
             *
             * @param int    $post_id         The expired post ID.
             * @param string $previous_status Post status before expiration.
             * @param string $new_status      Post status after expiration.
             */
            do_action( 'wpuf_post_expired', $post_id, $previous_status, $new_status );
        }
    }
}
