<?php
/**
 * Progress state for asynchronous sync operations.
 *
 * Single global progress transient holds the state of the currently running
 * sync (full or partial). The Dispatcher writes it on enqueue, BatchProcessor
 * increments it after each batch, the AJAX progress endpoint reads it, and
 * JS polls every 1.5 s to update the bar + counters in the admin UI.
 *
 * The cancel mechanism is a separate transient keyed by sync_id so the user
 * can request a halt mid-sync; BatchProcessor checks it before each chunk
 * and exits gracefully.
 *
 * @package SkyEmagSync
 */

namespace SkyEmagSync\Sync;

defined( 'ABSPATH' ) || exit;

class ProgressStore {

    const TRANSIENT_PROGRESS = 'skyemag_sync_progress';
    const TRANSIENT_CANCEL   = 'skyemag_sync_cancel_'; // suffix: sync_id
    const TTL                = HOUR_IN_SECONDS;
    const LOG_CAP            = 10;

    public static function init( string $sync_id, int $total, string $trigger ): void {
        $state = [
            'sync_id'     => $sync_id,
            'trigger'     => $trigger,
            'total'       => $total,
            'processed'   => 0,
            'synced'      => 0,
            'failed'      => 0,
            'skipped'     => 0,
            'started_at'  => time(),
            'last_update' => time(),
            'state'       => 'running',
            'recent_log'  => [],
        ];
        set_transient( self::TRANSIENT_PROGRESS, $state, self::TTL );
    }

    public static function get(): ?array {
        $state = get_transient( self::TRANSIENT_PROGRESS );
        return is_array( $state ) ? $state : null;
    }

    public static function increment( int $synced, int $failed, int $skipped, string $log_msg = '' ): void {
        $state = self::get();
        if ( null === $state ) {
            return;
        }

        $state['synced']      += $synced;
        $state['failed']      += $failed;
        $state['skipped']     += $skipped;
        $state['processed']   += ( $synced + $failed + $skipped );
        $state['last_update'] = time();

        if ( '' !== $log_msg ) {
            array_unshift( $state['recent_log'], [
                'time' => time(),
                'msg'  => $log_msg,
            ] );
            $state['recent_log'] = array_slice( $state['recent_log'], 0, self::LOG_CAP );
        }

        set_transient( self::TRANSIENT_PROGRESS, $state, self::TTL );
    }

    public static function complete(): void {
        $state = self::get();
        if ( null === $state ) {
            return;
        }
        $state['state']       = 'complete';
        $state['last_update'] = time();
        set_transient( self::TRANSIENT_PROGRESS, $state, self::TTL );
    }

    public static function fail( string $message ): void {
        $state = self::get();
        if ( null === $state ) {
            return;
        }
        $state['state']       = 'failed';
        $state['last_update'] = time();
        array_unshift( $state['recent_log'], [
            'time' => time(),
            'msg'  => $message,
        ] );
        $state['recent_log'] = array_slice( $state['recent_log'], 0, self::LOG_CAP );
        set_transient( self::TRANSIENT_PROGRESS, $state, self::TTL );
    }

    public static function eta_seconds(): int {
        $state = self::get();
        if ( null === $state || $state['processed'] === 0 ) {
            return 0;
        }
        $elapsed   = max( 1, time() - (int) $state['started_at'] );
        $rate      = (float) $state['processed'] / (float) $elapsed; // products/sec
        $remaining = (int) $state['total'] - (int) $state['processed'];
        if ( $remaining <= 0 || $rate <= 0 ) {
            return 0;
        }
        return (int) ceil( $remaining / $rate );
    }

    public static function cancel( string $sync_id ): void {
        set_transient( self::TRANSIENT_CANCEL . $sync_id, 1, self::TTL );

        $state = self::get();
        if ( null !== $state && $state['sync_id'] === $sync_id ) {
            $state['state']       = 'cancelled';
            $state['last_update'] = time();
            set_transient( self::TRANSIENT_PROGRESS, $state, self::TTL );
        }
    }

    public static function is_cancelled( string $sync_id ): bool {
        return (bool) get_transient( self::TRANSIENT_CANCEL . $sync_id );
    }

    public static function clear_cancel( string $sync_id ): void {
        delete_transient( self::TRANSIENT_CANCEL . $sync_id );
    }

    public static function clear(): void {
        delete_transient( self::TRANSIENT_PROGRESS );
    }
}
