<?php
/**
 * CartValidator — validates cart contents and runs abandoned-cart cleanup cron.
 *
 * Called before checkout and on cart view to catch stale/invalid items.
 *
 * @package TitanCart\Modules\Cart
 */

namespace TitanCart\Modules\Cart;

use TitanCart\DB\Database;

defined( 'ABSPATH' ) || exit;

class CartValidator {

    /** WP-Cron hook name for abandoned-cart cleanup. */
    const CRON_HOOK = 'tc_cart_cleanup';

    /* ──────────────────────────────────────────────────────────────────────────
     * Validation
     * ────────────────────────────────────────────────────────────────────────── */

    /**
     * Validate every item in the given cart.
     *
     * Returns an array keyed by item_id. Each value is an array of issue arrays:
     *   [ 'code' => string, 'message' => string, 'action' => 'remove'|'adjust'|'flag' ]
     *
     * Empty array = cart is valid.
     *
     * @param  object $cart  Cart record from tc_cart.
     * @return array<int, array>
     */
    public static function validate( object $cart ): array {
        global $wpdb;

        $issues = [];

        $items = $wpdb->get_results( $wpdb->prepare(
            'SELECT ci.*, p.status AS product_status, p.amount AS stock, p.min_qty, p.max_qty, p.qty_step, p.tracking
               FROM `tc_cart_items` ci
               LEFT JOIN `tc_products` p ON p.product_id = ci.product_id
              WHERE ci.cart_id = %d',
            $cart->cart_id
        ) );

        foreach ( $items as $item ) {
            $item_issues = [];

            // ── Product no longer exists ──────────────────────────────────────
            if ( $item->product_id === null || $item->product_status === null ) {
                $item_issues[] = [
                    'code'    => 'product_not_found',
                    'message' => __( 'This product is no longer available.', 'titancart-lite' ),
                    'action'  => 'remove',
                ];
                $issues[ $item->item_id ] = $item_issues;
                continue;
            }

            // ── Product disabled or hidden ────────────────────────────────────
            if ( $item->product_status !== 'A' ) {
                $item_issues[] = [
                    'code'    => 'product_unavailable',
                    'message' => __( 'This product is no longer available.', 'titancart-lite' ),
                    'action'  => 'remove',
                ];
            }

            // ── Insufficient stock ────────────────────────────────────────────
            if ( $item->tracking === 'B' && $item->stock < $item->amount ) {
                if ( $item->stock <= 0 ) {
                    $item_issues[] = [
                        'code'    => 'out_of_stock',
                        'message' => __( 'This product is out of stock.', 'titancart-lite' ),
                        'action'  => 'remove',
                    ];
                } else {
                    // Auto-adjust to available stock
                    $wpdb->update( 'tc_cart_items', [ 'amount' => $item->stock ], [ 'item_id' => $item->item_id ] );
                    $item_issues[] = [
                        'code'    => 'qty_adjusted',
                        /* translators: %d: available quantity */
                        'message' => sprintf( __( 'Quantity adjusted to available stock (%d).', 'titancart-lite' ), $item->stock ),
                        'action'  => 'adjust',
                    ];
                }
            }

            // ── Quantity below minimum ────────────────────────────────────────
            if ( $item->min_qty > 0 && $item->amount < $item->min_qty ) {
                $wpdb->update( 'tc_cart_items', [ 'amount' => $item->min_qty ], [ 'item_id' => $item->item_id ] );
                $item_issues[] = [
                    'code'    => 'min_qty',
                    /* translators: %d: minimum quantity */
                    'message' => sprintf( __( 'Minimum order quantity is %d.', 'titancart-lite' ), $item->min_qty ),
                    'action'  => 'adjust',
                ];
            }

            // ── Quantity above maximum ────────────────────────────────────────
            if ( $item->max_qty > 0 && $item->amount > $item->max_qty ) {
                $wpdb->update( 'tc_cart_items', [ 'amount' => $item->max_qty ], [ 'item_id' => $item->item_id ] );
                $item_issues[] = [
                    'code'    => 'max_qty',
                    /* translators: %d: maximum quantity */
                    'message' => sprintf( __( 'Maximum order quantity is %d.', 'titancart-lite' ), $item->max_qty ),
                    'action'  => 'adjust',
                ];
            }

            // ── Quantity not aligned to step ─────────────────────────────────
            if ( $item->qty_step > 1 && ( $item->amount % $item->qty_step ) !== 0 ) {
                $adjusted = (int) ( round( $item->amount / $item->qty_step ) * $item->qty_step );
                $adjusted = max( $adjusted, $item->min_qty ?: $item->qty_step );
                $wpdb->update( 'tc_cart_items', [ 'amount' => $adjusted ], [ 'item_id' => $item->item_id ] );
                $item_issues[] = [
                    'code'    => 'qty_step',
                    /* translators: %1$d: step, %2$d: adjusted qty */
                    'message' => sprintf( __( 'Quantity must be in multiples of %1$d. Adjusted to %2$d.', 'titancart-lite' ), $item->qty_step, $adjusted ),
                    'action'  => 'adjust',
                ];
            }

            // ── Option validation ─────────────────────────────────────────────
            if ( ! empty( $item->options_json ) ) {
                $options = json_decode( $item->options_json, true );
                if ( is_array( $options ) ) {
                    foreach ( $options as $option_id => $variant_id ) {
                        $valid = $wpdb->get_var( $wpdb->prepare(
                            'SELECT variant_id FROM `tc_product_option_variants`
                              WHERE variant_id = %d AND option_id = %d AND status = "A"',
                            $variant_id, $option_id
                        ) );
                        if ( ! $valid ) {
                            $item_issues[] = [
                                'code'    => 'invalid_option',
                                'message' => __( 'A selected option is no longer available. Please re-select.', 'titancart-lite' ),
                                'action'  => 'flag',
                            ];
                            break;
                        }
                    }
                }
            }

            if ( ! empty( $item_issues ) ) {
                $issues[ $item->item_id ] = $item_issues;
            }
        }

        return $issues;
    }

    /* ──────────────────────────────────────────────────────────────────────────
     * WP-Cron cleanup job
     * ────────────────────────────────────────────────────────────────────────── */

    /**
     * Schedule the daily cleanup cron on plugin activation.
     */
    public static function schedule_cleanup(): void {
        if ( ! wp_next_scheduled( self::CRON_HOOK ) ) {
            wp_schedule_event( time(), 'daily', self::CRON_HOOK );
        }
    }

    /**
     * Unschedule the cleanup cron on plugin deactivation.
     */
    public static function unschedule_cleanup(): void {
        $timestamp = wp_next_scheduled( self::CRON_HOOK );
        if ( $timestamp ) {
            wp_unschedule_event( $timestamp, self::CRON_HOOK );
        }
    }

    /**
     * Run the cleanup — called by WP-Cron via the hook.
     *
     * Deletes:
     *   - Guest carts older than tc_settings.guest_cart_lifetime days (default 30)
     *   - Empty carts older than 7 days
     */
    public static function run_cleanup(): void {
        global $wpdb;

        // Retrieve configurable lifetime
        $guest_days = (int) ( $wpdb->get_var(
            "SELECT value FROM `tc_settings` WHERE section = 'cart' AND name = 'guest_cart_lifetime'"
        ) ?: 30 );

        $empty_days = 7;

        // Delete old guest carts (with their items)
        $guest_cutoff = gmdate( 'Y-m-d H:i:s', strtotime( "-{$guest_days} days" ) );
        $guest_ids    = $wpdb->get_col( $wpdb->prepare(
            'SELECT cart_id FROM `tc_cart` WHERE user_id IS NULL AND updated_at < %s',
            $guest_cutoff
        ) );

        if ( $guest_ids ) {
            $placeholders = implode( ',', array_fill( 0, count( $guest_ids ), '%d' ) );
            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- $placeholders is a %d list built from count($guest_ids) (non-empty, guarded above); values passed to prepare().
            $wpdb->query( $wpdb->prepare( "DELETE FROM `tc_cart_items` WHERE cart_id IN ({$placeholders})", ...$guest_ids ) );
            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- $placeholders is a %d list built from count($guest_ids) (non-empty, guarded above); values passed to prepare().
            $wpdb->query( $wpdb->prepare( "DELETE FROM `tc_cart` WHERE cart_id IN ({$placeholders})", ...$guest_ids ) );
        }

        // Delete empty carts older than 7 days
        $empty_cutoff = gmdate( 'Y-m-d H:i:s', strtotime( "-{$empty_days} days" ) );
        $empty_ids    = $wpdb->get_col( $wpdb->prepare(
            'SELECT c.cart_id FROM `tc_cart` c
              LEFT JOIN `tc_cart_items` ci ON ci.cart_id = c.cart_id
             WHERE ci.item_id IS NULL AND c.updated_at < %s',
            $empty_cutoff
        ) );

        if ( $empty_ids ) {
            $placeholders = implode( ',', array_fill( 0, count( $empty_ids ), '%d' ) );
            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- $placeholders is a %d list built from count($empty_ids) (non-empty, guarded above); values passed to prepare().
            $wpdb->query( $wpdb->prepare( "DELETE FROM `tc_cart` WHERE cart_id IN ({$placeholders})", ...$empty_ids ) );
        }

        if ( defined( 'TITANCART_DEBUG' ) && TITANCART_DEBUG ) {
            titancart_log( sprintf(
                'TitanCart cart cleanup: removed %d guest carts, %d empty carts.',
                count( $guest_ids ),
                count( $empty_ids )
            ) );
        }
    }

    /**
     * Register hooks — called from CartModule::init().
     */
    public static function register_hooks(): void {
        add_action( self::CRON_HOOK, [ __CLASS__, 'run_cleanup' ] );
    }
}
