<?php
/**
 * DESCRIPTION: Records the first-occurrence timestamp of each activation milestone
 * (first event, first booking, first successful payment) locally and, when the user
 * has opted in, forwards the funnel to the telemetry report.
 *
 * @since   PBOOKING_SINCE
 * @package NemtlyBooking
 */

namespace NemtlyBooking\Core;

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * Activation Milestones tracker.
 *
 * Two layers:
 *  1. Local, always-on recording of when a site first reaches each milestone. This
 *     powers in-product moments (empty states, the post-booking review nudge) and
 *     never leaves the site.
 *  2. Opt-in reporting: the recorded funnel is appended to the telemetry payload via
 *     the `nemtly_booking_telemetry_additional_data` filter, so it is only transmitted
 *     when the admin has opted in to usage tracking.
 *
 * Milestones are write-once: the first event/booking/payment sets the timestamp and
 * subsequent occurrences are ignored. Seeded demo data and the setup-wizard sample
 * event are excluded — neither dispatches `event.created`/`booking.created` nor fires
 * the payment-success action, and the first-event recorder additionally guards against
 * the stored sample event id.
 *
 * @since PBOOKING_SINCE
 */
class Activation_Milestones {

    /**
     * Option key holding the milestone timestamps.
     *
     * @var string
     */
    const OPTION = 'nemtly_booking_activation_milestones';

    /**
     * Milestone key: first event created by the user.
     *
     * @var string
     */
    const FIRST_EVENT = 'first_event_at';

    /**
     * Milestone key: first booking received.
     *
     * @var string
     */
    const FIRST_BOOKING = 'first_booking_at';

    /**
     * Milestone key: first successful payment (Stripe).
     *
     * @var string
     */
    const FIRST_PAYMENT = 'first_payment_at';

    /**
     * Wire the tracker into the internal event manager and WordPress hooks.
     *
     * @since PBOOKING_SINCE
     *
     * @param \NemtlyBooking\Core\Event_Manager $event_manager Internal event manager.
     *
     * @return void
     */
    public static function register( $event_manager ) {
        // Layer 1 — local recording via the internal dispatch events.
        $event_manager->add_listener( 'event.created', [ __CLASS__, 'on_event_created' ] );
        $event_manager->add_listener( 'booking.created', [ __CLASS__, 'on_booking_created' ] );

        // Layer 1 — first successful payment fires as a WordPress action.
        add_action( 'nemtly_booking_payment_succeeded_booking', [ __CLASS__, 'on_payment_succeeded' ], 10, 2 );

        // Layer 2 — append the funnel to the opt-in telemetry report.
        add_filter( 'nemtly_booking_telemetry_additional_data', [ __CLASS__, 'add_to_telemetry' ] );
    }

    /**
     * Record the first-event milestone when a real event is created.
     *
     * Runs on the `event.created` dispatch event. Returns the event data unchanged so
     * the dispatch pipeline continues intact.
     *
     * @since PBOOKING_SINCE
     *
     * @param array $data Event data (from Event::to_array()).
     *
     * @return array The unmodified event data.
     */
    public static function on_event_created( $data ) {
        $event_id = isset( $data['id'] ) ? absint( $data['id'] ) : 0;

        if ( $event_id && ! self::is_sample_event( $event_id ) ) {
            self::record( self::FIRST_EVENT );
        }

        return $data;
    }

    /**
     * Record the first-booking milestone when a booking is created.
     *
     * Runs on the `booking.created` dispatch event. Seeded bookings insert directly and
     * never reach this event.
     *
     * @since PBOOKING_SINCE
     *
     * @param array $data Booking data (from Booking::to_array()).
     *
     * @return array The unmodified booking data.
     */
    public static function on_booking_created( $data ) {
        self::record( self::FIRST_BOOKING );

        return $data;
    }

    /**
     * Record the first-payment milestone when a booking payment succeeds.
     *
     * @since PBOOKING_SINCE
     *
     * @param \NemtlyBooking\Models\Payment $payment    Payment object (unused).
     * @param int                           $booking_id Booking id (unused).
     *
     * @return void
     */
    public static function on_payment_succeeded( $payment, $booking_id ) {
        self::record( self::FIRST_PAYMENT );
    }

    /**
     * Record a milestone, write-once.
     *
     * The first time a milestone key is seen its timestamp is stored; later calls are
     * ignored. Fires `nemtly_booking_activation_milestone` on first record so features
     * such as the post-booking review nudge can react.
     *
     * @since PBOOKING_SINCE
     *
     * @param string $key Milestone key (one of the FIRST_* constants).
     *
     * @return bool True if this call recorded the milestone, false if already set.
     */
    public static function record( $key ) {
        $milestones = self::raw();

        if ( ! empty( $milestones[ $key ] ) ) {
            return false;
        }

        $milestones[ $key ] = time();
        update_option( self::OPTION, $milestones );

        /**
         * Fires the first time an activation milestone is reached.
         *
         * @since PBOOKING_SINCE
         *
         * @param string $key        Milestone key (first_event_at|first_booking_at|first_payment_at).
         * @param array  $milestones Full milestone map after recording.
         */
        do_action( 'nemtly_booking_activation_milestone', $key, $milestones );

        return true;
    }

    /**
     * Get the milestone map with every key present (null when not yet reached).
     *
     * @since PBOOKING_SINCE
     *
     * @return array{first_event_at:?int,first_booking_at:?int,first_payment_at:?int}
     */
    public static function get() {
        return wp_parse_args(
            self::raw(),
            [
                self::FIRST_EVENT   => null,
                self::FIRST_BOOKING => null,
                self::FIRST_PAYMENT => null,
            ]
        );
    }

    /**
     * Append the activation funnel to the telemetry payload (opt-in only).
     *
     * This callback is only consumed when the telemetry report is sent, which requires
     * the admin to have opted in. Recording itself (Layer 1) is unconditional.
     *
     * @since PBOOKING_SINCE
     *
     * @param array $data Telemetry additional-data array.
     *
     * @return array Data with the `activation_milestones` funnel merged in.
     */
    public static function add_to_telemetry( $data ) {
        $data['activation_milestones'] = self::get();

        return $data;
    }

    /**
     * Whether the given event id is the setup-wizard sample event.
     *
     * @since PBOOKING_SINCE
     *
     * @param int $event_id Event post id.
     *
     * @return bool
     */
    public static function is_sample_event( $event_id ) {
        $sample_id = absint( get_option( 'nemtly_booking_sample_event_id', 0 ) );

        return $sample_id > 0 && absint( $event_id ) === $sample_id;
    }

    /**
     * Read the raw stored milestone array, normalised to an array.
     *
     * @since PBOOKING_SINCE
     *
     * @return array
     */
    private static function raw() {
        $milestones = get_option( self::OPTION, [] );

        return is_array( $milestones ) ? $milestones : [];
    }
}
