<?php
/**
 * Dispatch sync work to Action Scheduler in chunks.
 *
 * Splits the eligible-products list (full sync) or a passed ID set (partial
 * sync) into BATCH_SIZE chunks and enqueues one Action Scheduler action per
 * chunk. AS picks them up in the background; BatchProcessor handles each.
 * Returns the sync_id so the frontend can poll ProgressStore for updates.
 *
 * Falls back to the legacy synchronous Runner flow only when AS functions
 * are not available (extremely rare — WooCommerce bundles AS).
 *
 * @package SkyEmagSync
 */

namespace SkyEmagSync\Sync;

use SkyEmagSync\Options;

defined( 'ABSPATH' ) || exit;

class Dispatcher {

    const BATCH_SIZE = 25;
    const AS_HOOK    = 'skyemag_sync_batch';
    const AS_GROUP   = 'skyemag-sync';

    /**
     * Enqueue a full sync of all eligible products.
     *
     * @return array { success, sync_id?, total_products?, total_batches?, errors[] }
     */
    public static function enqueue_full( string $trigger = 'manual' ): array {
        if ( 'manual' !== $trigger && ! Options::is_master_enabled() ) {
            return self::error( 'Master sync switch is disabled.' );
        }

        $products = Runner::get_eligible_products();
        $ids      = array_map( static function ( $p ) {
            return $p->get_id();
        }, $products );

        return self::enqueue_ids( $ids, $trigger );
    }

    /**
     * Enqueue a partial sync for a specific set of product IDs.
     *
     * @param int[] $ids
     */
    public static function enqueue_for_ids( array $ids, string $trigger = 'manual' ): array {
        $ids = array_values( array_filter( array_map( 'intval', $ids ) ) );
        return self::enqueue_ids( $ids, $trigger );
    }

    /**
     * Shared enqueue path used by both full and partial dispatch.
     *
     * @param int[] $ids
     */
    private static function enqueue_ids( array $ids, string $trigger ): array {
        if ( empty( $ids ) ) {
            return self::error( 'No eligible products to sync.' );
        }

        if ( ! Runner::acquire_lock() ) {
            $existing = ProgressStore::get();
            return [
                'success'   => false,
                'errors'    => [ 'Sync already running.' ],
                'sync_id'   => $existing['sync_id'] ?? '',
                'already_running' => true,
            ];
        }

        // Action Scheduler missing: fall back to legacy synchronous run.
        if ( ! function_exists( 'as_enqueue_async_action' ) ) {
            Runner::release_lock(); // run_sync_for_ids re-acquires it
            $sync_result = Runner::run_sync_for_ids( $ids );
            return [
                'success'        => $sync_result['success'],
                'sync_id'        => '',
                'total_products' => count( $ids ),
                'total_batches'  => 1,
                'fallback_sync'  => true,
                'fallback_result' => $sync_result,
                'errors'         => $sync_result['errors'] ?? [],
            ];
        }

        $sync_id = uniqid( 'sync_', true );
        $total   = count( $ids );
        $chunks  = array_chunk( $ids, self::BATCH_SIZE );

        ProgressStore::clear_cancel( $sync_id );
        ProgressStore::init( $sync_id, $total, $trigger );

        foreach ( $chunks as $chunk ) {
            as_enqueue_async_action( self::AS_HOOK, [ $chunk, $sync_id ], self::AS_GROUP );
        }

        return [
            'success'        => true,
            'sync_id'        => $sync_id,
            'total_products' => $total,
            'total_batches'  => count( $chunks ),
            'errors'         => [],
        ];
    }

    private static function error( string $message ): array {
        return [
            'success' => false,
            'errors'  => [ $message ],
            'sync_id' => '',
        ];
    }
}
