<?php
/**
 * Queue Manager — Action Scheduler integration, atomic processing, backoff retry.
 *
 * Handles: queue processing, atomic lock (prevents concurrent double-send),
 * exponential backoff retry, event_time drift protection.
 *
 * @package CapiFlow
 */

namespace CapiFlow\Core;

defined( 'ABSPATH' ) || exit;

class Queue_Manager {

    /**
     * Register hooks.
     */
    public function register_hooks() {
        add_action( 'capiflow_process_queue', array( $this, 'process_queue' ) );
    }

    /**
     * Process the event queue.
     *
     * Fetches pending events, claims them with atomic lock,
     * sends to CAPI, and handles retry/failure classification.
     */
    public function process_queue() {
        $batch_size = (int) get_option( 'capiflow_batch_size', 50 );
        $events     = DB_Manager::get_pending_events( $batch_size );

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

        $sender = new CAPI_Sender();

        foreach ( $events as $event ) {
            // Atomic lock — prevents concurrent worker double-send.
            if ( ! DB_Manager::claim_event( $event->id ) ) {
                // Another worker claimed this event — skip.
                continue;
            }

            // Event time drift protection.
            $event_time = $this->validate_event_time( (int) $event->event_time, $event->id );
            if ( null === $event_time ) {
                // Event expired (>7 days old) — already marked failed.
                continue;
            }

            // Decode payload.
            $payload = json_decode( $event->payload, true );
            if ( ! is_array( $payload ) ) {
                DB_Manager::mark_failed( $event->id, 'Invalid JSON payload' );
                Logger::error( $event->event_name, 'Invalid JSON payload for event #' . $event->id );
                continue;
            }

            // Update event_time in payload (in case it was clamped).
            if ( isset( $payload['event_time'] ) ) {
                $payload['event_time'] = $event_time;
            }

            // Send to CAPI.
            $result = $sender->send( array( $payload ), $event->pixel_id );

            // Classify and handle response.
            $classification = $sender->classify_response( $result['http_code'] );

            switch ( $classification ) {
                case 'success':
                    DB_Manager::mark_sent( $event->id, $result['http_code'], $result['response'] );

                    Logger::log(
                        $event->event_name,
                        $event->event_id,
                        null,
                        $result['http_code'],
                        'Sent successfully',
                        'info'
                    );
                    break;

                case 'permanent_fail':
                    // 4xx (except 429) — DO NOT retry.
                    DB_Manager::mark_failed( $event->id, $result['response'], $result['http_code'] );

                    Logger::log(
                        $event->event_name,
                        $event->event_id,
                        null,
                        $result['http_code'],
                        'Permanent failure: ' . $result['response'],
                        'error'
                    );
                    break;

                case 'rate_limited':
                    // 429 — retry with DOUBLE delay.
                    DB_Manager::schedule_retry( $event->id, (int) $event->retry_count, 2 );

                    Logger::log(
                        $event->event_name,
                        $event->event_id,
                        null,
                        429,
                        'Rate limited — retry scheduled with 2x delay',
                        'warning'
                    );
                    break;

                case 'retry':
                default:
                    // 5xx or timeout — normal backoff retry.
                    DB_Manager::schedule_retry( $event->id, (int) $event->retry_count, 1 );

                    Logger::log(
                        $event->event_name,
                        $event->event_id,
                        null,
                        $result['http_code'],
                        'Retry scheduled: ' . $result['response'],
                        'warning'
                    );
                    break;
            }

            // Extensible hook.
            do_action( 'capiflow_event_processed', $event->event_name, $event->wc_order_id, $result );
        }
    }

    /**
     * Validate event_time: clamp future timestamps, reject >7 days old.
     *
     * @param int $event_time Original event timestamp.
     * @param int $db_id      Database row ID (for marking failed).
     * @return int|null Validated timestamp, or null if expired.
     */
    private function validate_event_time( $event_time, $db_id ) {
        $now            = time();
        $seven_days_ago = $now - ( 7 * DAY_IN_SECONDS );

        // Future timestamp → clamp to now.
        if ( $event_time > $now ) {
            return $now;
        }

        // Older than 7 days → Meta will reject.
        if ( $event_time < $seven_days_ago ) {
            DB_Manager::mark_failed( $db_id, 'Event older than 7 days — expired' );
            Logger::warning( 'Queue', 'Event #' . $db_id . ' expired (>7 days old)' );
            return null;
        }

        return $event_time;
    }

    /**
     * Enqueue an event into the queue (license gatekeeper).
     *
     * Only callable if the license has 'all_events' feature.
     *
     * @param string $event_name  Event name.
     * @param string $event_id    Event ID.
     * @param int    $event_time  Event timestamp.
     * @param string $pixel_id    Pixel ID.
     * @param int    $order_id    WC Order ID.
     * @param array  $payload     CAPI payload array.
     * @return int|false Insert ID or false.
     */
    public function enqueue( $event_name, $event_id, $event_time, $pixel_id, $order_id, $payload ) {
        return DB_Manager::insert_event( array(
            'event_name'  => $event_name,
            'event_id'    => $event_id,
            'event_time'  => $event_time,
            'pixel_id'    => $pixel_id,
            'wc_order_id' => $order_id,
            'payload'     => $payload,
        ) );
    }
}
