<?php

/**
 * Sentinel_Onboarding
 *
 * State helpers for the first-run / Quick Setup experience.
 *
 * Phase 1 scope: state storage only.
 * No site-option writes, no UI, no wizard logic yet.
 */

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

class Sentinel_Onboarding
{
    const OPTION         = 'sentinel_onboarding';
    const SCHEMA_VERSION = 1;
    const WIZARD_VERSION = '1.0';

    const STATUS_PENDING   = 'pending';
    const STATUS_COMPLETED = 'completed';
    const STATUS_SKIPPED   = 'skipped';
    const STATUS_DISMISSED = 'dismissed';

    /**
     * Canonical schema. Always returned (merged into) so callers can rely
     * on every key being present, even when older state is read back.
     */
    public static function default_state()
    {
        return array(
            'version'               => self::SCHEMA_VERSION,
            'status'                => self::STATUS_PENDING,
            'completed_at'          => null,
            'dismissed_at'          => null,
            'wizard_version'        => self::WIZARD_VERSION,
            'last_profile'          => null,
            'last_quick_setup'      => null,
            'first_run_at'          => null,
            'upgrade_nag_dismissed' => false,
        );
    }

    /**
     * Read state with full schema merge.
     */
    public static function get_state()
    {
        $stored = get_option(self::OPTION, array());
        if (!is_array($stored)) {
            $stored = array();
        }
        return array_merge(self::default_state(), $stored);
    }

    /**
     * Merge + save state. Capability checks are the caller's responsibility.
     */
    public static function update_state(array $partial)
    {
        $current         = self::get_state();
        $next            = array_merge($current, $partial);
        $next['version'] = self::SCHEMA_VERSION;
        update_option(self::OPTION, $next, false);
        return $next;
    }

    public static function is_pending()
    {
        $state = self::get_state();
        return $state['status'] === self::STATUS_PENDING;
    }

    public static function is_completed()
    {
        $state = self::get_state();
        return $state['status'] === self::STATUS_COMPLETED;
    }

    public static function is_skipped()
    {
        $state = self::get_state();
        return $state['status'] === self::STATUS_SKIPPED;
    }

    public static function is_dismissed()
    {
        $state = self::get_state();
        return $state['status'] === self::STATUS_DISMISSED;
    }

    /**
     * Activation-time seeding. Idempotent.
     * Called from Sentinel_Migration::set_default_options().
     */
    public static function seed_on_activation()
    {
        if (get_option('sentinel_activated_at', null) === null) {
            add_option('sentinel_activated_at', time(), '', false);
        }

        if (get_option(self::OPTION, false) !== false) {
            return;
        }

        $state                 = self::default_state();
        $state['first_run_at'] = current_time('mysql');

        // Upgrader heuristic: if Sentinel was already running on this site
        // (sentinel_db_version recorded), do not auto-prompt the wizard.
        // The dashboard card + notice will still surface Quick Setup.
        if (get_option('sentinel_db_version', false) !== false) {
            $state['status'] = self::STATUS_DISMISSED;
        }

        add_option(self::OPTION, $state, '', false);
    }

    /**
     * Load-time safety net for upgrades where the activation hook didn't
     * fire (e.g. plugin files replaced on disk). Idempotent.
     */
    public static function seed_on_load_if_missing()
    {
        if (get_option(self::OPTION, false) !== false) {
            return;
        }

        $state                 = self::default_state();
        $state['first_run_at'] = current_time('mysql');
        // Reaching this path implies the plugin has already been initialised
        // at least once (db version exists), so treat as upgrader.
        $state['status']       = self::STATUS_DISMISSED;

        add_option(self::OPTION, $state, '', false);
    }
}
