<?php
/**
 * Setup wizard state and helpers (resume anytime).
 *
 * @package Maca_Menu_List_Pro
 */

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

define('MACA_MENULIST_ONBOARDING_OPTION', 'maca_menulist_onboarding');
define('MACA_MENULIST_ONBOARDING_ENABLED_OPTION', 'maca_menulist_onboarding_enabled');
define('MACA_MENULIST_ONBOARDING_REDIRECT_TRANSIENT', 'maca_menulist_onboarding_redirect');
define('MACA_MENULIST_ONBOARDING_VERSION', 2);

require_once MACA_MENULIST_PLUGIN_DIR . 'includes/onboarding-i18n.php';

/**
 * Content mode from wizard draft or saved option.
 *
 * @param array<string, mixed>|null $draft Draft values.
 * @return string
 */
function maca_menulist_onboarding_draft_content_mode($draft = null) {
    if (is_array($draft) && isset($draft['content_mode'])) {
        $mode = sanitize_key((string) $draft['content_mode']);
        if (in_array($mode, maca_menulist_get_content_modes(), true)) {
            return $mode;
        }
    }

    return maca_menulist_get_content_mode();
}

/**
 * Whether a wizard step should appear for the current draft.
 *
 * @param string                    $step  Step slug.
 * @param array<string, mixed>|null $draft Draft values.
 * @return bool
 */
function maca_menulist_onboarding_should_show_step($step, $draft = null) {
    if ($step !== 'first_dish') {
        return true;
    }

    return maca_menulist_onboarding_draft_content_mode($draft) === 'dishes'
        && maca_menulist_pro_has_feature('dishes');
}

/**
 * Wizard step definitions (order matters).
 *
 * @return array<string, array{label: string, optional?: bool}>
 */
function maca_menulist_onboarding_get_steps() {
    $steps = array(
        'welcome'       => array(
            'label' => maca_menulist_ob('Välkommen', 'Welcome'),
        ),
        'menu_type'     => array(
            'label' => maca_menulist_ob('Menytyp', 'Menu type'),
        ),
        'restaurant'    => array(
            'label' => maca_menulist_ob('Restaurang', 'Restaurant'),
        ),
        'opening_hours' => array(
            'label'    => maca_menulist_ob('Öppettider', 'Opening hours'),
            'optional' => true,
        ),
        'menu_category' => array(
            'label' => maca_menulist_ob('Menykategori', 'Menu category'),
        ),
        'first_dish'    => array(
            'label' => maca_menulist_ob('Första rätten', 'First dish'),
        ),
        'extras'        => array(
            'label'    => maca_menulist_ob('Tillval', 'Optional features'),
            'optional' => true,
        ),
        'publish'       => array(
            'label' => maca_menulist_ob('Publicera', 'Publish menu'),
        ),
        'complete'      => array(
            'label' => maca_menulist_ob('Klart', 'Done'),
        ),
    );

    /**
     * Filter onboarding wizard steps.
     *
     * @param array<string, array{label: string, optional?: bool}> $steps Step definitions.
     */
    return apply_filters('maca_menulist_onboarding_steps', $steps);
}

/**
 * Ordered step slugs.
 *
 * @return string[]
 */
function maca_menulist_onboarding_step_slugs() {
    return array_keys(maca_menulist_onboarding_get_steps());
}

/**
 * Default onboarding record.
 *
 * @return array<string, mixed>
 */
function maca_menulist_onboarding_default_state() {
    return array(
        'version'         => MACA_MENULIST_ONBOARDING_VERSION,
        'status'          => 'active',
        'step'            => 'welcome',
        'completed_steps' => array(),
        'skipped_steps'   => array(),
        'draft'           => array(),
        'started_at'      => maca_menulist_wp_now_mysql(),
        'updated_at'      => maca_menulist_wp_now_mysql(),
    );
}

/**
 * Stored onboarding state.
 *
 * @return array<string, mixed>
 */
function maca_menulist_onboarding_get_state() {
    $stored = get_option(MACA_MENULIST_ONBOARDING_OPTION, array());

    if (!is_array($stored)) {
        return array();
    }

    return maca_menulist_onboarding_migrate_state($stored);
}

/**
 * Upgrade saved wizard progress when step slugs change.
 *
 * @param array<string, mixed> $state Stored state.
 * @return array<string, mixed>
 */
function maca_menulist_onboarding_migrate_state($state) {
    if (isset($state['step']) && (string) $state['step'] === 'menu_setup') {
        $state['step'] = 'menu_category';
    }

    if (isset($state['completed_steps']) && is_array($state['completed_steps'])) {
        $state['completed_steps'] = array_values(
            array_unique(
                array_map(
                    static function ($slug) {
                        return $slug === 'menu_setup' ? 'menu_category' : $slug;
                    },
                    $state['completed_steps']
                )
            )
        );
    }

    return $state;
}

/**
 * Whether the install looks unused (fresh setup).
 *
 * @return bool
 */
function maca_menulist_onboarding_is_fresh_install() {
    if (!function_exists('maca_menulist_db_get_categories')) {
        require_once MACA_MENULIST_PLUGIN_DIR . 'includes/db.php';
    }
    if (!function_exists('maca_menulist_db_get_dish_categories')) {
        require_once MACA_MENULIST_PLUGIN_DIR . 'includes/db-dishes.php';
    }

    $categories = maca_menulist_db_get_categories();
    if (!empty($categories)) {
        return false;
    }

    $dish_categories = maca_menulist_db_get_dish_categories();
    if (!empty($dish_categories)) {
        return false;
    }

    $restaurant = trim((string) get_option('maca_menulist_menu_footer_restaurant_name', ''));
    if ($restaurant !== '') {
        return false;
    }

    return true;
}

/**
 * Whether the setup wizard is enabled site-wide.
 *
 * @return bool
 */
function maca_menulist_onboarding_is_enabled() {
    $value = get_option(MACA_MENULIST_ONBOARDING_ENABLED_OPTION, '1');

    return $value === '1' || $value === 1 || $value === true;
}

/**
 * Enable or disable the setup wizard site-wide.
 *
 * @param bool $enabled Whether onboarding should be offered.
 * @return bool
 */
function maca_menulist_onboarding_set_enabled($enabled) {
    return update_option(MACA_MENULIST_ONBOARDING_ENABLED_OPTION, $enabled ? '1' : '0', false);
}

/**
 * Align enabled flag with dismissed wizard state (pre-1.10.12 installs).
 *
 * @return void
 */
function maca_menulist_onboarding_sync_dismissed_enabled() {
    $state = maca_menulist_onboarding_get_state();
    $status = isset($state['status']) ? (string) $state['status'] : '';

    if ($status === 'dismissed' && maca_menulist_onboarding_is_enabled()) {
        maca_menulist_onboarding_set_enabled(false);
    }
}

/**
 * Admin URL for onboarding settings (restaurant tab).
 *
 * @return string
 */
function maca_menulist_onboarding_get_settings_url() {
    return add_query_arg(
        array(
            'page' => 'maca-restu-settings',
            'tab'  => 'restaurant',
        ),
        admin_url('admin.php')
    );
}

/**
 * Whether the wizard should be offered (not finished).
 *
 * @return bool
 */
function maca_menulist_onboarding_is_active() {
    if (!maca_menulist_onboarding_is_enabled()) {
        return false;
    }

    $state = maca_menulist_onboarding_get_state();
    $status = isset($state['status']) ? (string) $state['status'] : '';

    return $status === 'active';
}

/**
 * Whether onboarding is fully finished.
 *
 * @return bool
 */
function maca_menulist_onboarding_is_completed() {
    $state = maca_menulist_onboarding_get_state();

    if (empty($state)) {
        return !maca_menulist_onboarding_is_fresh_install();
    }

    $status = isset($state['status']) ? (string) $state['status'] : '';

    return in_array($status, array('completed', 'dismissed'), true);
}

/**
 * Start or resume the wizard.
 *
 * @return void
 */
function maca_menulist_onboarding_start() {
    if (!maca_menulist_onboarding_is_enabled()) {
        return;
    }

    $state = maca_menulist_onboarding_get_state();

    if (!empty($state['status']) && in_array($state['status'], array('completed', 'dismissed'), true)) {
        return;
    }

    if (empty($state['status'])) {
        $state = maca_menulist_onboarding_default_state();
    } else {
        $state['status'] = 'active';
        $state['updated_at'] = maca_menulist_wp_now_mysql();
    }

    update_option(MACA_MENULIST_ONBOARDING_OPTION, $state, false);
}

/**
 * Queue a one-time redirect to the wizard after legal acceptance.
 *
 * @return void
 */
function maca_menulist_onboarding_queue_redirect() {
    if (!maca_menulist_onboarding_is_enabled()) {
        return;
    }

    set_transient(MACA_MENULIST_ONBOARDING_REDIRECT_TRANSIENT, '1', MINUTE_IN_SECONDS);
}

/**
 * Persist onboarding state.
 *
 * @param array<string, mixed> $state State array.
 * @return bool
 */
function maca_menulist_onboarding_save_state($state) {
    $state['updated_at'] = maca_menulist_wp_now_mysql();

    return update_option(MACA_MENULIST_ONBOARDING_OPTION, $state, false);
}

/**
 * Mark a step completed.
 *
 * @param array<string, mixed> $state   Current state.
 * @param string               $step    Step slug.
 * @return array<string, mixed>
 */
function maca_menulist_onboarding_mark_step_done($state, $step) {
    if (!isset($state['completed_steps']) || !is_array($state['completed_steps'])) {
        $state['completed_steps'] = array();
    }

    if (!in_array($step, $state['completed_steps'], true)) {
        $state['completed_steps'][] = $step;
    }

    $skipped = isset($state['skipped_steps']) && is_array($state['skipped_steps'])
        ? $state['skipped_steps']
        : array();
    $state['skipped_steps'] = array_values(array_diff($skipped, array($step)));

    return $state;
}

/**
 * Step slugs visible for the current draft (skips licensed dish step when needed).
 *
 * @param array<string, mixed>|null $draft Draft values.
 * @return string[]
 */
function maca_menulist_onboarding_get_active_step_slugs($draft = null) {
    $all = maca_menulist_onboarding_step_slugs();

    return array_values(
        array_filter(
            $all,
            static function ($slug) use ($draft) {
                return maca_menulist_onboarding_should_show_step($slug, $draft);
            }
        )
    );
}

/**
 * Next step slug after the given step.
 *
 * @param string                    $step  Current step.
 * @param array<string, mixed>|null $draft Draft values.
 * @return string|null
 */
function maca_menulist_onboarding_next_step($step, $draft = null) {
    $slugs = maca_menulist_onboarding_get_active_step_slugs($draft);
    $index = array_search($step, $slugs, true);

    if ($index === false || !isset($slugs[ $index + 1 ])) {
        return null;
    }

    return $slugs[ $index + 1 ];
}

/**
 * Previous step slug.
 *
 * @param string                    $step  Current step.
 * @param array<string, mixed>|null $draft Draft values.
 * @return string|null
 */
function maca_menulist_onboarding_prev_step($step, $draft = null) {
    $slugs = maca_menulist_onboarding_get_active_step_slugs($draft);
    $index = array_search($step, $slugs, true);

    if ($index === false || $index <= 0) {
        return null;
    }

    return $slugs[ $index - 1 ];
}

/**
 * Normalize legacy step slugs from saved progress.
 *
 * @param string $step Step slug.
 * @return string
 */
function maca_menulist_onboarding_normalize_step_slug($step) {
    if ($step === 'menu_setup') {
        return 'menu_category';
    }

    return $step;
}

/**
 * Resolve a stored or requested step to a visible step slug.
 *
 * @param string                    $step  Step slug.
 * @param array<string, mixed>|null $draft Draft values.
 * @return string
 */
function maca_menulist_onboarding_resolve_visible_step($step, $draft = null) {
    $step = maca_menulist_onboarding_normalize_step_slug($step);
    $all = maca_menulist_onboarding_step_slugs();

    if (!in_array($step, $all, true)) {
        return 'welcome';
    }

    if (maca_menulist_onboarding_should_show_step($step, $draft)) {
        return $step;
    }

    $index = array_search($step, $all, true);
    if ($index !== false) {
        for ($i = $index + 1; $i < count($all); $i++) {
            if (maca_menulist_onboarding_should_show_step($all[ $i ], $draft)) {
                return $all[ $i ];
            }
        }
        for ($i = $index - 1; $i >= 0; $i--) {
            if (maca_menulist_onboarding_should_show_step($all[ $i ], $draft)) {
                return $all[ $i ];
            }
        }
    }

    $active = maca_menulist_onboarding_get_active_step_slugs($draft);

    return !empty($active[0]) ? $active[0] : 'welcome';
}

/**
 * Admin URL for the wizard.
 *
 * @param string $step Optional step slug.
 * @return string
 */
function maca_menulist_onboarding_get_admin_url($step = '') {
    $args = array(
        'page' => 'maca-restu-onboarding',
    );

    if ($step !== '') {
        $args['step'] = $step;
    }

    return add_query_arg($args, admin_url('admin.php'));
}

/**
 * Resolve active step from state and request.
 *
 * @param array<string, mixed> $state Stored state.
 * @return string
 */
function maca_menulist_onboarding_resolve_step($state) {
    $values = maca_menulist_onboarding_get_form_values($state);

    // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only navigation.
    if (isset($_GET['step'])) {
        $requested = sanitize_key(wp_unslash($_GET['step']));

        return maca_menulist_onboarding_resolve_visible_step($requested, $values);
    }

    $stored = isset($state['step']) ? sanitize_key((string) $state['step']) : 'welcome';

    return maca_menulist_onboarding_resolve_visible_step($stored, $values);
}

/**
 * Merge draft values with saved options for form display.
 *
 * @param array<string, mixed> $state Wizard state.
 * @return array<string, mixed>
 */
function maca_menulist_onboarding_get_form_values($state) {
    $draft = isset($state['draft']) && is_array($state['draft']) ? $state['draft'] : array();

    return array(
        'content_mode'     => isset($draft['content_mode'])
            ? sanitize_key((string) $draft['content_mode'])
            : maca_menulist_get_content_mode(),
        'restaurant_name'  => isset($draft['restaurant_name'])
            ? (string) $draft['restaurant_name']
            : (string) get_option('maca_menulist_menu_footer_restaurant_name', ''),
        'restaurant_email' => isset($draft['restaurant_email'])
            ? (string) $draft['restaurant_email']
            : (string) get_option('maca_menulist_menu_footer_email', ''),
        'restaurant_address' => isset($draft['restaurant_address'])
            ? (string) $draft['restaurant_address']
            : (string) get_option('maca_menulist_menu_footer_address', ''),
        'restaurant_phone' => isset($draft['restaurant_phone'])
            ? (string) $draft['restaurant_phone']
            : (string) get_option('maca_menulist_menu_footer_phone', ''),
        'restaurant_website' => isset($draft['restaurant_website'])
            ? (string) $draft['restaurant_website']
            : (string) get_option('maca_menulist_menu_footer_website', ''),
        'restaurant_vat_number' => isset($draft['restaurant_vat_number'])
            ? (string) $draft['restaurant_vat_number']
            : (string) (function_exists('maca_menulist_get_vat_number') ? maca_menulist_get_vat_number() : get_option('maca_menulist_menu_footer_vat_number', '')),
        'price_currency'   => isset($draft['price_currency'])
            ? (string) $draft['price_currency']
            : maca_menulist_get_price_currency(),
        'category_name'    => isset($draft['category_name']) ? (string) $draft['category_name'] : '',
        'dish_category'    => isset($draft['dish_category']) ? (string) $draft['dish_category'] : '',
        'dish_title'       => isset($draft['dish_title']) ? (string) $draft['dish_title'] : '',
        'dish_price'       => isset($draft['dish_price']) ? (string) $draft['dish_price'] : '',
        'enable_table_order' => !empty($draft['enable_table_order']),
        'enable_bookings'    => !empty($draft['enable_bookings']),
        'enable_info_hub'    => !empty($draft['enable_info_hub']),
        'hours'              => (isset($draft['hours']) && is_array($draft['hours']) && $draft['hours'] !== array())
            ? $draft['hours']
            : maca_menulist_onboarding_hours_to_draft(),
    );
}

/**
 * Convert stored booking hours to wizard POST-shaped draft.
 *
 * @param array<int, array{closed: bool, open: string, close: string}>|null $hours Hours.
 * @return array<int, array<string, mixed>>
 */
function maca_menulist_onboarding_hours_to_draft($hours = null) {
    if (!is_array($hours)) {
        $hours = maca_menulist_get_booking_hours();
    }

    $draft = array();

    for ($day = 0; $day <= 6; $day++) {
        $day_hours = isset($hours[ $day ])
            ? maca_menulist_normalize_stored_day_hours($hours[ $day ])
            : maca_menulist_normalize_stored_day_hours(array('closed' => false));
        $first_window = !empty($day_hours['windows']) ? $day_hours['windows'][0] : array('open' => '11:00', 'close' => '22:00');

        $draft[ $day ] = array(
            'is_open'    => empty($day_hours['closed']) ? '1' : '',
            'time_open'  => $first_window['open'],
            'time_close' => $first_window['close'],
        );
    }

    return $draft;
}

/**
 * Hours array for opening-hours field rendering from wizard draft rows.
 *
 * @param array<int, array<string, mixed>> $draft_hours Draft hours from wizard state.
 * @return array<int, array{closed: bool, open: string, close: string}>
 */
function maca_menulist_onboarding_get_hours_for_display($draft_hours) {
    if (empty($draft_hours) || !is_array($draft_hours)) {
        return maca_menulist_get_booking_hours();
    }

    $display = maca_menulist_get_default_booking_hours();

    for ($day = 0; $day <= 6; $day++) {
        if (!isset($draft_hours[ $day ]) || !is_array($draft_hours[ $day ])) {
            continue;
        }

        $row = $draft_hours[ $day ];
        $display[ $day ] = maca_menulist_normalize_stored_day_hours(
            array(
                'closed' => empty($row['is_open']),
                'open'   => isset($row['time_open']) ? (string) $row['time_open'] : '11:00',
                'close'  => isset($row['time_close']) ? (string) $row['time_close'] : '22:00',
            )
        );
    }

    return $display;
}

/**
 * Sync wizard draft menu type to the saved content mode option.
 *
 * Keeps DB in line with draft when the user changes menu type mid-wizard
 * (e.g. activates a license and picks online menu without re-submitting the step).
 *
 * @param array<string, mixed> $state Current wizard state.
 * @param array<string, mixed> $draft Wizard draft values.
 * @return array{state: array<string, mixed>, draft: array<string, mixed>, changed: bool}
 */
function maca_menulist_onboarding_sync_content_mode($state, $draft) {
    $changed = false;

    if (!isset($draft['content_mode'])) {
        return array(
            'state'   => $state,
            'draft'   => $draft,
            'changed' => false,
        );
    }

    $mode = sanitize_key((string) $draft['content_mode']);
    if (!in_array($mode, maca_menulist_get_content_modes(), true)) {
        return array(
            'state'   => $state,
            'draft'   => $draft,
            'changed' => false,
        );
    }

    if ($mode === 'dishes' && !maca_menulist_pro_has_feature('dishes')) {
        return array(
            'state'   => $state,
            'draft'   => $draft,
            'changed' => false,
        );
    }

    $previous = maca_menulist_get_content_mode();
    if ($previous === $mode) {
        return array(
            'state'   => $state,
            'draft'   => $draft,
            'changed' => false,
        );
    }

    update_option('maca_menulist_content_mode', $mode);
    $state = maca_menulist_onboarding_reset_after_menu_type($state, $previous, $mode, $draft);
    $changed = true;

    return array(
        'state'   => $state,
        'draft'   => $draft,
        'changed' => $changed,
    );
}

/**
 * Clear downstream wizard progress when menu type changes.
 *
 * @param array<string, mixed> $state         Wizard state.
 * @param string               $previous_mode Previous content mode.
 * @param string               $new_mode      New content mode.
 * @param array<string, mixed> $draft         Wizard draft values (updated in place).
 * @return array<string, mixed>
 */
function maca_menulist_onboarding_reset_after_menu_type($state, $previous_mode, $new_mode, &$draft) {
    if ($previous_mode === $new_mode) {
        return $state;
    }

    $steps_after = array(
        'restaurant',
        'opening_hours',
        'menu_category',
        'first_dish',
        'extras',
        'publish',
    );

    $completed = isset($state['completed_steps']) && is_array($state['completed_steps'])
        ? $state['completed_steps']
        : array();
    $state['completed_steps'] = array_values(array_diff($completed, $steps_after));

    if ($new_mode === 'files') {
        unset($draft['dish_category'], $draft['dish_title'], $draft['dish_price']);
    } else {
        unset($draft['category_name']);
    }

    $state['draft'] = $draft;

    return $state;
}

/**
 * Whether a wizard step can be opened directly from the step indicator.
 *
 * @param string               $target  Step slug.
 * @param string               $current Current step slug.
 * @param array<string, mixed> $state   Wizard state.
 * @param array<string, mixed> $values  Form values.
 * @return bool
 */
function maca_menulist_onboarding_can_jump_to_step($target, $current, $state, $values) {
    if ($target === 'complete') {
        return false;
    }

    $active = maca_menulist_onboarding_get_active_step_slugs($values);
    $target_index = array_search($target, $active, true);
    $current_index = array_search($current, $active, true);

    if ($target_index === false) {
        return false;
    }

    if ($current_index === false) {
        return true;
    }

    if ($target_index <= $current_index) {
        return true;
    }

    $completed = isset($state['completed_steps']) && is_array($state['completed_steps'])
        ? $state['completed_steps']
        : array();

    return in_array($target, $completed, true);
}

/**
 * Apply step data to plugin options / database.
 *
 * @param string               $step  Step slug.
 * @param array<string, mixed> $draft Form values.
 * @return true|WP_Error
 */
function maca_menulist_onboarding_apply_step($step, $draft) {
    switch ($step) {
        case 'menu_type':
            $mode = isset($draft['content_mode']) ? sanitize_key((string) $draft['content_mode']) : 'files';
            if (!in_array($mode, maca_menulist_get_content_modes(), true)) {
                $mode = 'files';
            }
            if ($mode === 'dishes' && !maca_menulist_pro_has_feature('dishes')) {
                return new WP_Error(
                    'maca_onboarding_no_license',
                    maca_menulist_ob(
                        'Onlinemeny kräver aktiv Pro-prenumeration. Välj PDF/bildmenyer eller aktivera licens under Prenumeration.',
                        'Online menu requires an active Pro subscription. Choose PDF/image menus or activate a license under Subscription.'
                    )
                );
            }
            update_option('maca_menulist_content_mode', $mode);
            break;

        case 'restaurant':
            $name = isset($draft['restaurant_name']) ? sanitize_text_field((string) $draft['restaurant_name']) : '';
            $email = isset($draft['restaurant_email']) ? sanitize_email((string) $draft['restaurant_email']) : '';
            $address = isset($draft['restaurant_address']) ? sanitize_textarea_field((string) $draft['restaurant_address']) : '';
            $phone = isset($draft['restaurant_phone']) ? sanitize_text_field((string) $draft['restaurant_phone']) : '';
            $website = isset($draft['restaurant_website']) ? esc_url_raw((string) $draft['restaurant_website']) : '';
            $vat_number = isset($draft['restaurant_vat_number']) ? sanitize_text_field((string) $draft['restaurant_vat_number']) : '';
            $currency = isset($draft['price_currency']) ? sanitize_text_field((string) $draft['price_currency']) : 'kr';

            if ($name === '') {
                return new WP_Error(
                    'maca_onboarding_restaurant_name',
                    maca_menulist_ob('Ange restaurangnamn.', 'Please enter a restaurant name.')
                );
            }

            if ($currency === '') {
                $currency = 'kr';
            }

            update_option('maca_menulist_menu_footer_restaurant_name', $name);
            update_option('maca_menulist_menu_footer_email', $email);
            update_option('maca_menulist_menu_footer_address', $address);
            update_option('maca_menulist_menu_footer_phone', $phone);
            update_option('maca_menulist_menu_footer_website', $website);
            update_option('maca_menulist_menu_footer_vat_number', $vat_number);
            update_option('maca_menulist_price_currency', $currency);
            break;

        case 'opening_hours':
            if (!empty($draft['hours']) && is_array($draft['hours'])) {
                maca_menulist_save_booking_hours($draft['hours']);
            }
            break;

        case 'menu_category':
            $mode = maca_menulist_onboarding_draft_content_mode($draft);

            if ($mode === 'files') {
                $category_name = isset($draft['category_name']) ? sanitize_text_field((string) $draft['category_name']) : '';
                if ($category_name === '') {
                    return new WP_Error(
                        'maca_onboarding_category',
                        maca_menulist_ob(
                            'Ange ett kategorinamn (t.ex. Lunch eller Middag).',
                            'Please enter a category name (e.g. Lunch or Dinner).'
                        )
                    );
                }

                $existing_id = maca_menulist_db_get_category_id_by_name($category_name);
                if ($existing_id <= 0) {
                    maca_menulist_db_insert_category(
                        array(
                            'name'       => $category_name,
                            'sort_order' => 0,
                        )
                    );
                }
            } else {
                if (!maca_menulist_pro_has_feature('dishes')) {
                    return new WP_Error(
                        'maca_onboarding_no_license',
                        maca_menulist_ob(
                            'Onlinemeny kräver aktiv Pro-prenumeration.',
                            'Online menu requires an active Pro subscription.'
                        )
                    );
                }

                $dish_category = isset($draft['dish_category']) ? sanitize_text_field((string) $draft['dish_category']) : '';
                if ($dish_category === '') {
                    return new WP_Error(
                        'maca_onboarding_dish_category',
                        maca_menulist_ob(
                            'Ange en menykategori (t.ex. Huvudrätt eller Drycker).',
                            'Please enter a menu category (e.g. Main course or Drinks).'
                        )
                    );
                }

                $categories = maca_menulist_db_get_dish_categories();
                $category_id = 0;

                foreach ($categories as $cat) {
                    if (strcasecmp((string) $cat->name, $dish_category) === 0) {
                        $category_id = (int) $cat->id;
                        break;
                    }
                }

                if ($category_id <= 0) {
                    maca_menulist_db_insert_dish_category(
                        array(
                            'name'       => $dish_category,
                            'sort_order' => count($categories),
                        )
                    );
                }
            }
            break;

        case 'first_dish':
            if (!maca_menulist_onboarding_should_show_step('first_dish', $draft)) {
                break;
            }

            if (!maca_menulist_pro_has_feature('dishes')) {
                return new WP_Error(
                    'maca_onboarding_no_license',
                    maca_menulist_ob(
                        'Du behöver registrera en Pro-licens för att lägga till rätter.',
                        'You need an active Pro license to add dishes.'
                    )
                );
            }

            $dish_category = isset($draft['dish_category']) ? sanitize_text_field((string) $draft['dish_category']) : '';
            if ($dish_category === '') {
                return new WP_Error(
                    'maca_onboarding_dish_category_missing',
                    maca_menulist_ob(
                        'Menykategori saknas. Gå tillbaka och skapa en kategori först.',
                        'Menu category is missing. Go back and create a category first.'
                    )
                );
            }

            $categories = maca_menulist_db_get_dish_categories();
            $category_id = 0;

            foreach ($categories as $cat) {
                if (strcasecmp((string) $cat->name, $dish_category) === 0) {
                    $category_id = (int) $cat->id;
                    break;
                }
            }

            if ($category_id <= 0) {
                return new WP_Error(
                    'maca_onboarding_dish_category_missing',
                    maca_menulist_ob(
                        'Menykategorin hittades inte. Gå tillbaka och skapa kategorin igen.',
                        'Menu category was not found. Go back and create the category again.'
                    )
                );
            }

            $dish_title = isset($draft['dish_title']) ? sanitize_text_field((string) $draft['dish_title']) : '';
            if ($dish_title === '') {
                return new WP_Error(
                    'maca_onboarding_dish_title',
                    maca_menulist_ob('Ange ett namn på rätten.', 'Please enter a dish name.')
                );
            }

            $dish_price_raw = isset($draft['dish_price']) ? wp_unslash((string) $draft['dish_price']) : '';
            $dish_price_raw = str_replace(',', '.', trim($dish_price_raw));
            $dish_price = ($dish_price_raw !== '') ? floatval($dish_price_raw) : null;

            maca_menulist_db_insert_dish(
                array(
                    'category_id' => $category_id,
                    'title'       => $dish_title,
                    'price'       => $dish_price,
                    'is_active'   => 1,
                    'sort_order'  => 0,
                )
            );
            break;

        case 'extras':
            if (!empty($draft['enable_table_order']) && maca_menulist_is_dishes_mode()) {
                update_option('maca_menulist_table_order', '1');
            }
            if (!empty($draft['enable_bookings'])) {
                update_option('maca_menulist_table_booking', '1');
            }
            if (!empty($draft['enable_info_hub']) && maca_menulist_pro_has_feature('info_hub')) {
                update_option('maca_menulist_info_hub_enabled', '1');
            }
            break;

        case 'welcome':
        case 'opening_hours':
        case 'publish':
        case 'complete':
            break;
    }

    return true;
}

/**
 * Progress percentage for the wizard header.
 *
 * @param array<string, mixed> $state State.
 * @return int
 */
function maca_menulist_onboarding_progress_percent($state) {
    $values = maca_menulist_onboarding_get_form_values($state);
    $slugs = maca_menulist_onboarding_get_active_step_slugs($values);
    $slugs = array_values(array_filter($slugs, static function ($slug) {
        return $slug !== 'complete';
    }));
    $completed = isset($state['completed_steps']) && is_array($state['completed_steps'])
        ? $state['completed_steps']
        : array();
    $completed_visible = array_filter(
        $completed,
        static function ($slug) use ($values) {
            return maca_menulist_onboarding_should_show_step($slug, $values);
        }
    );
    $total = max(1, count($slugs) - 1);

    return min(100, (int) round((count($completed_visible) / $total) * 100));
}
