<?php
defined('ABSPATH') or die('Direct access not allowed');

/**
 * VollstartRD_Options — Plugin options framework.
 *
 * SRP: Defines option schema, reads/writes option values.
 * Options are stored in the custom rd_options table via DB class.
 * No admin UI logic here — that belongs to VollstartRD_Admin.
 */
class VollstartRD_Options {

    private $MAIN;
    private string $prefix;
    private ?array $options = null;

    public function __construct($MAIN) {
        $this->MAIN = $MAIN;
        $this->prefix = $MAIN->getPrefix();
    }

    /**
     * Initialize option definitions. Called lazily on first access.
     */
    private function initOptions(): void {
        if ($this->options !== null) {
            return;
        }

        $this->options = [];

        // --- General ---
        $this->options['hGeneral']        = $this->makeOption('hGeneral', __('General Settings', 'vollstart-repair-desk'), '', 'heading');
        $this->options['businessName']      = $this->makeOption('businessName', __('Business Name', 'vollstart-repair-desk'), __('The name of your workshop or business. This is shown in customer emails, on the public status page, and on PDF receipts. Example: "Mike\'s Bike Repair" or "TechFix Electronics".', 'vollstart-repair-desk'), 'text', '');
        $this->options['businessEmail']     = $this->makeOption('businessEmail', __('Business Email', 'vollstart-repair-desk'), __('The email address where YOU receive notifications — for example when a customer sends a message on the status page, or when a new repair is created (if enabled below). This is also shown to customers in emails so they can reply. If left empty, the WordPress admin email is used.', 'vollstart-repair-desk'), 'text', '');
        $this->options['businessPhone']     = $this->makeOption('businessPhone', __('Business Phone', 'vollstart-repair-desk'), __('Your workshop phone number. Shown to customers in status update emails and on the public status page, so they can reach you if needed. Leave empty to hide.', 'vollstart-repair-desk'), 'text', '');
        $this->options['businessAddress']   = $this->makeOption('businessAddress', __('Business Address', 'vollstart-repair-desk'), __('Your workshop address. Shown on PDF receipts and on the public status page so customers know where to pick up their device.', 'vollstart-repair-desk'), 'textarea', '');
        $this->options['businessTaxId']     = $this->makeOption('businessTaxId', __('Tax ID / VAT Number', 'vollstart-repair-desk'), __('Your business tax identification number (e.g. VAT ID, EIN, Steuernummer). Shown in the header of PDF receipts and customer reports when set. Leave empty to hide.', 'vollstart-repair-desk'), 'text', '');
        $this->options['businessTaxLabel']  = $this->makeOption('businessTaxLabel', __('Tax ID Label', 'vollstart-repair-desk'), __('Label used for the tax ID on PDF documents. Choose the term common in your country. Examples: "VAT", "Tax ID", "Steuernummer", "EIN".', 'vollstart-repair-desk'), 'text', 'VAT');
        $this->options['currency']        = $this->makeOption('currency', __('Currency symbol', 'vollstart-repair-desk'), __('Currency shown on invoices, billing summaries, and cost displays. If WooCommerce is active, this is ignored and the WooCommerce currency is used instead. Examples: €, $, CHF, £.', 'vollstart-repair-desk'), 'text', '€');
        $this->options['deviceTypes']     = $this->makeOption('deviceTypes', __('Device Types', 'vollstart-repair-desk'), __('Comma-separated list of device types for your workshop. Shown as a dropdown when creating devices. If empty, a free text field is shown instead. Example: "Bike, E-Bike, Shoes, Bag" or "Laptop, Phone, Tablet, Monitor". The first entry is pre-selected for new devices.', 'vollstart-repair-desk'), 'text', '');

        // --- Repair Numbers ---
        $this->options['hRepairNumbers']      = $this->makeOption('hRepairNumbers', __('Repair Numbers', 'vollstart-repair-desk'), '', 'heading');
        $this->options['repairNumberPrefix']  = $this->makeOption('repairNumberPrefix', __('Repair Number Prefix', 'vollstart-repair-desk'), __('Short prefix for repair numbers. Examples: "REP-", "WS-", "FIX-". Keep it short — customers may need to type or read it. You can change this anytime — existing repairs keep their number.', 'vollstart-repair-desk'), 'text', 'REP-');
        $this->options['repairNumberFormat']  = $this->makeOption('repairNumberFormat', __('Repair Number Format', 'vollstart-repair-desk'), __('Controls how the repair number looks after the prefix. "Number only" is simplest (REP-0001). "Year + Number" resets the counter each year and shows when the repair was created (REP-26-0001). "Year+Month + Number" resets monthly — best for busy workshops with many daily repairs (REP-2603-0001).', 'vollstart-repair-desk'), 'dropdown', 'number_only', [
            'values' => [
                ['label' => __('Number only (REP-0001)', 'vollstart-repair-desk'), 'value' => 'number_only'],
                ['label' => __('Year + Number (REP-26-0001)', 'vollstart-repair-desk'), 'value' => 'year_number'],
                ['label' => __('Year+Month + Number (REP-2603-0001)', 'vollstart-repair-desk'), 'value' => 'yearmonth_number'],
            ],
        ]);
        $this->options['repairNumberDigits']  = $this->makeOption('repairNumberDigits', __('Number of digits', 'vollstart-repair-desk'), __('How many digits for the sequential part. 4 digits supports up to 9999 per period. 5 digits for high-volume workshops. Default: 4.', 'vollstart-repair-desk'), 'number', 4, ['min' => 3, 'max' => 6, 'step' => 1]);
        $this->options['customerNumberPrefix'] = $this->makeOption('customerNumberPrefix', __('Customer Number Prefix', 'vollstart-repair-desk'), __('Every customer gets an auto-generated number with this prefix. Useful for quickly identifying customers in the system. Examples: "KD-" creates KD-0001, KD-0002. "C-" creates C-0001. You can change this anytime — existing customers keep their number.', 'vollstart-repair-desk'), 'text', 'KD-');

        // --- Status Flow (internal data option, hidden from UI by empty label) ---
        $this->options['statusFlow']      = $this->makeOption('statusFlow', '', '', 'textarea', json_encode([
            ['slug' => 'received', 'active' => true],
            ['slug' => 'diagnosis', 'active' => true],
            ['slug' => 'quote', 'active' => true],
            ['slug' => 'waiting_for_parts', 'active' => true],
            ['slug' => 'in_progress', 'active' => true],
            ['slug' => 'quality_check', 'active' => true],
            ['slug' => 'done', 'active' => true],
            ['slug' => 'delivered', 'active' => true],
            ['slug' => 'cancelled', 'active' => true],
        ]));

        // --- Communication ---
        $this->options['hCommunication']  = $this->makeOption('hCommunication', __('Customer Communication', 'vollstart-repair-desk'), '', 'heading');
        $this->options['enableMessages']  = $this->makeOption('enableMessages', __('Enable message system', 'vollstart-repair-desk'), __('When enabled, the Messages section is shown in each repair view. You can send messages to the customer and — if configured below — the customer can reply on the public status page. When disabled, the message box is hidden everywhere (admin + public). Default: enabled.', 'vollstart-repair-desk'), 'checkbox', 1);

        // --- Email ---
        $this->options['hEmail']                       = $this->makeOption('hEmail', __('Email Notifications', 'vollstart-repair-desk'), '', 'heading');
        $this->options['emailStatusChangeEnabled']     = $this->makeOption('emailStatusChangeEnabled', __('Email customer on status change', 'vollstart-repair-desk'), __('When enabled, the customer receives an email every time you change the status of their repair (e.g. from "In Arbeit" to "Fertig"). The email contains the new status, device info, and a link to the status page.', 'vollstart-repair-desk'), 'checkbox', 1);
        $this->options['emailStatusChangeStatuses']    = $this->makeOption('emailStatusChangeStatuses', __('Send email only on these statuses', 'vollstart-repair-desk'), __('Select which status changes trigger an email to the customer. Leave empty to send on every status change. Tip: select only "Done" if you only want to notify when the repair is ready.', 'vollstart-repair-desk'), 'text', '');
        $this->options['emailStatusChangeSubject']     = $this->makeOption('emailStatusChangeSubject', __('Status Change — Subject Line', 'vollstart-repair-desk'), __('Subject line of the status change email. Placeholders: {repair_number}, {status}, {business_name}, {device}.', 'vollstart-repair-desk'), 'text', __('Repair Update: {repair_number} — {status}', 'vollstart-repair-desk'));
        $this->options['emailStatusChangeBody']        = $this->makeOption('emailStatusChangeBody', __('Status Change — Email Body', 'vollstart-repair-desk'), __('Body of the status change email. HTML allowed. Placeholders: {repair_number}, {status}, {device}, {customer_name}, {business_name}, {status_url}, {estimated_date}.', 'vollstart-repair-desk'), 'textarea', '');
        $this->options['emailNotifyAdmin']             = $this->makeOption('emailNotifyAdmin', __('Notify me on new repair', 'vollstart-repair-desk'), __('When enabled, you receive an email at the Business Email address (see above) every time a new repair is created.', 'vollstart-repair-desk'), 'checkbox', 0);
        $this->options['emailNewRepairSubject']        = $this->makeOption('emailNewRepairSubject', __('New Repair — Subject Line', 'vollstart-repair-desk'), __('Subject for the admin notification email. Placeholders: {repair_number}, {device}, {business_name}.', 'vollstart-repair-desk'), 'text', __('New repair created: {repair_number}', 'vollstart-repair-desk'));
        $this->options['emailNewRepairBody']           = $this->makeOption('emailNewRepairBody', __('New Repair — Email Body', 'vollstart-repair-desk'), __('Body of the admin new-repair notification. HTML allowed. Placeholders: {repair_number}, {device}, {customer_name}, {business_name}, {admin_url}.', 'vollstart-repair-desk'), 'textarea', '');
        $this->options['emailCustomerMessageEnabled']  = $this->makeOption('emailCustomerMessageEnabled', __('Email customer on new message', 'vollstart-repair-desk'), __('When enabled, the customer receives an email when you send a message in the repair view. The email includes the message text and a link to reply on the status page (if enabled). The customer can also reply directly to this email — it goes to your Business Email.', 'vollstart-repair-desk'), 'checkbox', 1);
        $this->options['emailCustomerMessageSubject']  = $this->makeOption('emailCustomerMessageSubject', __('Message to Customer — Subject', 'vollstart-repair-desk'), __('Subject line when emailing your message to the customer. Placeholders: {repair_number}, {business_name}.', 'vollstart-repair-desk'), 'text', __('Message from {business_name} — {repair_number}', 'vollstart-repair-desk'));
        $this->options['emailCustomerMessageBody']    = $this->makeOption('emailCustomerMessageBody', __('Message to Customer — Email Body', 'vollstart-repair-desk'), __('Body of the customer-message email. HTML allowed. Placeholders: {repair_number}, {customer_name}, {message}, {business_name}, {status_url}.', 'vollstart-repair-desk'), 'textarea', '');
        $this->options['emailNewMessageEnabled']       = $this->makeOption('emailNewMessageEnabled', __('Notify me on customer message', 'vollstart-repair-desk'), __('When enabled, you receive an email when a customer sends a message on the public status page.', 'vollstart-repair-desk'), 'checkbox', 1);
        $this->options['emailNewMessageSubject']       = $this->makeOption('emailNewMessageSubject', __('Customer Message — Subject Line', 'vollstart-repair-desk'), __('Subject for the customer message notification. Placeholders: {repair_number}, {business_name}.', 'vollstart-repair-desk'), 'text', __('New customer message on repair {repair_number}', 'vollstart-repair-desk'));
        $this->options['emailNewMessageBody']          = $this->makeOption('emailNewMessageBody', __('Customer Message — Email Body', 'vollstart-repair-desk'), __('Body of the customer-message admin notification. HTML allowed. Placeholders: {repair_number}, {customer_name}, {message}, {business_name}, {admin_url}.', 'vollstart-repair-desk'), 'textarea', '');

        // --- Pickup Reminder ---
        $this->options['hReminder']               = $this->makeOption('hReminder', __('Pickup Reminder', 'vollstart-repair-desk'), '', 'heading');
        $this->options['pickupReminderEnabled']   = $this->makeOption('pickupReminderEnabled', __('Send pickup reminder email', 'vollstart-repair-desk'), __('When a repair has been in "Done" status for a certain number of days without being picked up, automatically send a reminder email to the customer. The reminder is sent once — not repeatedly.', 'vollstart-repair-desk'), 'checkbox', 1);
        $this->options['pickupReminderDays']      = $this->makeOption('pickupReminderDays', __('Days before pickup reminder', 'vollstart-repair-desk'), __('How many days after a repair reaches "Done" status should the pickup reminder be sent? Default: 3 days.', 'vollstart-repair-desk'), 'number', 3, ['min' => 1, 'max' => 30, 'step' => 1]);
        $this->options['pickupReminderSubject']   = $this->makeOption('pickupReminderSubject', __('Pickup Reminder — Subject', 'vollstart-repair-desk'), __('Subject line for pickup reminder. Placeholders: {repair_number}, {business_name}, {device}.', 'vollstart-repair-desk'), 'text', __('Reminder: Your repair {repair_number} is ready for pickup', 'vollstart-repair-desk'));
        $this->options['pickupReminderBody']      = $this->makeOption('pickupReminderBody', __('Pickup Reminder — Email Body', 'vollstart-repair-desk'), __('Body of the pickup reminder. HTML allowed. Placeholders: {repair_number}, {device}, {customer_name}, {business_name}, {status_url}.', 'vollstart-repair-desk'), 'textarea', '');

        // --- Work Steps ---
        $this->options['hWorkSteps']         = $this->makeOption('hWorkSteps', __('Work Steps', 'vollstart-repair-desk'), '', 'heading');
        $this->options['enableWorkSteps']    = $this->makeOption('enableWorkSteps', __('Enable work steps system', 'vollstart-repair-desk'), __('Master switch for the entire work steps feature. When disabled: the Work Steps tab is hidden, no checklists are shown in repairs or on the public status page, and repair type templates cannot assign work steps. Existing data is preserved. Default: enabled.', 'vollstart-repair-desk'), 'checkbox', 1);
        $this->options['allowManualSteps']   = $this->makeOption('allowManualSteps', __('Allow adding manual steps to repairs', 'vollstart-repair-desk'), __('When enabled, workers can add custom steps directly in a repair. Disable if you want repairs to only use predefined work steps from templates. Default: enabled.', 'vollstart-repair-desk'), 'checkbox', 1);
        $this->options['enableTimeTracking'] = $this->makeOption('enableTimeTracking', __('Enable time tracking on work steps', 'vollstart-repair-desk'), __('When enabled, each work step shows a play/pause timer button. Workers can track how long each step takes. The tracked time is shown on the step and in the audit trail. Disable if your workshop does not need time tracking. Default: enabled.', 'vollstart-repair-desk'), 'checkbox', 1);
        $this->options['allowRemoveSteps']   = $this->makeOption('allowRemoveSteps', __('Allow removing work steps from repairs', 'vollstart-repair-desk'), __('When enabled, workers can remove individual work steps or entire step groups from a repair. When disabled, the assigned work steps are locked — only checking off is possible. Default: disabled.', 'vollstart-repair-desk'), 'checkbox', 0);
        $this->options['allowManualTimeEdit'] = $this->makeOption('allowManualTimeEdit', __('Allow manual time entry', 'vollstart-repair-desk'), __('When enabled, workers can manually edit the tracked time on a work step. Useful when someone forgot to start the timer. An edit button appears next to the timer. Default: enabled.', 'vollstart-repair-desk'), 'checkbox', 1);

        // --- Public Status Page ---
        $this->options['hStatusPage']                = $this->makeOption('hStatusPage', __('Public Status Page', 'vollstart-repair-desk'), '', 'heading');
        $this->options['statusPageEnabled']          = $this->makeOption('statusPageEnabled', __('Enable public status page', 'vollstart-repair-desk'), __('When enabled, customers can check their repair status online using a shortcode [repair_status]. They enter their repair number and access code (printed on the receipt or sent via email). Disable to keep all repair data private. Default: disabled.', 'vollstart-repair-desk'), 'checkbox', 0);
        $this->options['statusPageSlug']             = $this->makeOption('statusPageSlug', __('Status page slug', 'vollstart-repair-desk'), __('The URL slug used for the public status page. Example: "repair-desk" creates URLs like yoursite.com/repair-desk/REP-0001/CODE. Only letters, numbers, and hyphens.', 'vollstart-repair-desk'), 'text', 'vollstart-repair-desk');
        $this->options['statusPageIntroText']        = $this->makeOption('statusPageIntroText', __('Intro text', 'vollstart-repair-desk'), __('Text shown above the lookup form on the public status page. Keep it short and helpful. Example: "Enter your repair number and access code to check the current status."', 'vollstart-repair-desk'), 'text', __('Enter your repair number and access code to check the status of your repair.', 'vollstart-repair-desk'));
        $this->options['statusPageShowTimeline']     = $this->makeOption('statusPageShowTimeline', __('Show status timeline', 'vollstart-repair-desk'), __('When enabled, customers see the full status history (date + status name). When disabled, only the current status is shown. Default: enabled.', 'vollstart-repair-desk'), 'checkbox', 1);
        $this->options['statusPageShowFiles']        = $this->makeOption('statusPageShowFiles', __('Show repair photos/files', 'vollstart-repair-desk'), __('When enabled, customers can view and download photos and documents attached to their repair (e.g. intake photos, damage documentation). Default: disabled.', 'vollstart-repair-desk'), 'checkbox', 0);
        $this->options['statusPageShowWorkSteps']    = $this->makeOption('statusPageShowWorkSteps', __('Show work steps', 'vollstart-repair-desk'), __('When enabled, customers see the checklist of work steps and their progress (checked/unchecked, tracked time). Gives transparency about what has been done. Default: disabled.', 'vollstart-repair-desk'), 'checkbox', 0);
        $this->options['statusPageShowStepTime']     = $this->makeOption('statusPageShowStepTime', __('Show tracked time on work steps', 'vollstart-repair-desk'), __('When enabled and work steps are visible, customers also see how many minutes were spent on each step. Default: disabled.', 'vollstart-repair-desk'), 'checkbox', 0);
        $this->options['statusPageShowStepFiles']    = $this->makeOption('statusPageShowStepFiles', __('Show work step instruction files', 'vollstart-repair-desk'), __('When enabled and work steps are visible, customers can also see instruction images/documents attached to each step. Default: disabled.', 'vollstart-repair-desk'), 'checkbox', 0);
        $this->options['statusPageShowMessages']     = $this->makeOption('statusPageShowMessages', __('Show messages', 'vollstart-repair-desk'), __('When enabled, customers can see messages from the workshop on the status page. Default: enabled.', 'vollstart-repair-desk'), 'checkbox', 1);
        $this->options['statusPageAllowReply']       = $this->makeOption('statusPageAllowReply', __('Allow customer to reply', 'vollstart-repair-desk'), __('When enabled together with "Show messages", customers can write a reply on the status page. The message is stored and you get notified. Default: disabled.', 'vollstart-repair-desk'), 'checkbox', 0);

        // --- Ratings (internal) ---
        $this->options['hRatings']           = $this->makeOption('hRatings', __('Ratings', 'vollstart-repair-desk'), '', 'heading');
        $this->options['enableRatings']           = $this->makeOption('enableRatings', __('Enable internal rating system', 'vollstart-repair-desk'), __('When enabled, staff can rate customers internally. The average rating is shown on the customer card.', 'vollstart-repair-desk'), 'checkbox', 1);
        $this->options['ratingShowOnStatusPage']  = $this->makeOption('ratingShowOnStatusPage', __('Show rating on customer status page', 'vollstart-repair-desk'), __('When enabled, customers see a star-rating widget on the public status page after their repair is complete. Requires "Enable internal rating system" to be active.', 'vollstart-repair-desk'), 'checkbox', 0);
        $this->options['ratingAllowUpdate']       = $this->makeOption('ratingAllowUpdate', __('Allow customers to update their rating', 'vollstart-repair-desk'), __('When enabled, customers can change their previously submitted rating and comment. When disabled, only the first submission is accepted.', 'vollstart-repair-desk'), 'checkbox', 0);

        // --- Warranty & Maintenance ---
        $this->options['hWarranty']              = $this->makeOption('hWarranty', __('Warranty & Maintenance', 'vollstart-repair-desk'), '', 'heading');
        $this->options['warrantyDefaultDays']    = $this->makeOption('warrantyDefaultDays', __('Default warranty duration (days)', 'vollstart-repair-desk'), __('When set, a quick-fill button appears in the repair form to pre-fill the warranty date to today plus this many days. Set to 0 to hide the button. Example: 30 = one month warranty.', 'vollstart-repair-desk'), 'number', 0, ['min' => 0, 'max' => 3650, 'step' => 1]);

        // --- Repair Receipt (Free — used by generateReceipt PDF) ---
        $this->options['hReceipt']               = $this->makeOption('hReceipt', __('Repair Receipt', 'vollstart-repair-desk'), '', 'heading');
        $this->options['receiptShowCustomer']    = $this->makeOption('receiptShowCustomer', __('Show customer info', 'vollstart-repair-desk'), __('Display customer name, phone, and email on the repair receipt.', 'vollstart-repair-desk'), 'checkbox', 1);
        $this->options['receiptShowDevice']      = $this->makeOption('receiptShowDevice', __('Show device info', 'vollstart-repair-desk'), __('Display device brand, model, and serial number on the receipt.', 'vollstart-repair-desk'), 'checkbox', 1);
        $this->options['receiptShowDefect']      = $this->makeOption('receiptShowDefect', __('Show defect description', 'vollstart-repair-desk'), __('Display the defect description as reported by the customer.', 'vollstart-repair-desk'), 'checkbox', 1);
        $this->options['receiptShowSteps']       = $this->makeOption('receiptShowSteps', __('Show repair steps', 'vollstart-repair-desk'), __('Display the checklist steps assigned to this repair on the receipt.', 'vollstart-repair-desk'), 'checkbox', 1);
        $this->options['receiptShowUndoneSteps'] = $this->makeOption('receiptShowUndoneSteps', __('Show uncompleted steps', 'vollstart-repair-desk'), __('When enabled, all steps are listed on the receipt. When disabled, only completed steps are shown.', 'vollstart-repair-desk'), 'checkbox', 1);
        $this->options['receiptShowQr']          = $this->makeOption('receiptShowQr', __('Show QR code (status tracking)', 'vollstart-repair-desk'), __('Display QR code for online status tracking. Only works when Public Status Page is enabled.', 'vollstart-repair-desk'), 'checkbox', 1);
        $this->options['receiptShowEstimate']    = $this->makeOption('receiptShowEstimate', __('Show estimated date and price', 'vollstart-repair-desk'), __('Display estimated completion date and price on the receipt.', 'vollstart-repair-desk'), 'checkbox', 1);
        $this->options['receiptFooterText']      = $this->makeOption('receiptFooterText', __('Receipt footer text', 'vollstart-repair-desk'), __('Custom text printed at the bottom of every receipt. Example: "Thank you for your trust!"', 'vollstart-repair-desk'), 'text', '');

        // --- PDF Format (applies to repair receipts and device/customer reports) ---
        $this->options['hPdf']           = $this->makeOption('hPdf', __('PDF Format', 'vollstart-repair-desk'), '', 'heading');
        $this->options['pdfPaperSize']   = $this->makeOption('pdfPaperSize', __('Paper size', 'vollstart-repair-desk'), __('Page format for generated PDFs.', 'vollstart-repair-desk'), 'dropdown', 'A4', ['values' => [['value' => 'A4', 'label' => 'A4 (210 × 297 mm)'], ['value' => 'LETTER', 'label' => 'Letter (8.5 × 11 in)'], ['value' => 'LEGAL', 'label' => 'Legal (8.5 × 14 in)'], ['value' => 'A5', 'label' => 'A5 (148 × 210 mm)']]]);
        $this->options['pdfFont']        = $this->makeOption('pdfFont', __('Font', 'vollstart-repair-desk'), __('Font used in generated PDFs.', 'vollstart-repair-desk'), 'dropdown', 'helvetica', ['values' => [['value' => 'helvetica', 'label' => 'Helvetica'], ['value' => 'times', 'label' => 'Times'], ['value' => 'courier', 'label' => 'Courier']]]);
        $this->options['pdfFontSize']    = $this->makeOption('pdfFontSize', __('Font size (pt)', 'vollstart-repair-desk'), __('Base font size for PDF body text. Headers and totals scale automatically. Default: 10.', 'vollstart-repair-desk'), 'number', 10, ['min' => 7, 'max' => 14, 'step' => 1]);
        $this->options['pdfMargin']      = $this->makeOption('pdfMargin', __('Page margin (mm)', 'vollstart-repair-desk'), __('Margin on all sides of the PDF page. Default: 15.', 'vollstart-repair-desk'), 'number', 15, ['min' => 5, 'max' => 30, 'step' => 1]);
        $this->options['pdfDirection']   = $this->makeOption('pdfDirection', __('Text direction', 'vollstart-repair-desk'), __('Text direction for the PDF. LTR for most languages, RTL for Arabic, Hebrew.', 'vollstart-repair-desk'), 'dropdown', 'L', ['values' => [['value' => 'L', 'label' => __('Left to Right (LTR)', 'vollstart-repair-desk')], ['value' => 'R', 'label' => __('Right to Left (RTL)', 'vollstart-repair-desk')]]]);

        // ---- Section-based extension point ----
        // Group the flat options into a sections-by-heading array, let
        // registered extensions modify it (add new sections, append options
        // to existing sections, even reorder) via filter, then flatten
        // back to the storage shape ($this->options as an associative array
        // keyed by option key). Order in $sections becomes visual order.
        $sections = [];
        $currentSection = null;
        foreach ($this->options as $key => $opt) {
            if ($opt['type'] === 'heading') {
                $currentSection = $key;
                $sections[$currentSection] = ['label' => $opt['label'], 'options' => []];
            } elseif ($currentSection !== null) {
                $sections[$currentSection]['options'][$key] = $opt;
            }
        }
        $sections = apply_filters('vollstart_rd_option_sections', $sections, $this);

        $this->options = [];
        foreach ($sections as $sectionKey => $section) {
            $this->options[$sectionKey] = $this->makeOption(
                $sectionKey,
                $section['label'] ?? '',
                '',
                'heading'
            );
            foreach ($section['options'] as $optKey => $opt) {
                $this->options[$optKey] = $opt;
            }
        }

        // Advanced section is always last (sections added by extensions that
        // should appear before Advanced are registered without reaching for it).
        $this->options['hAdvanced']             = $this->makeOption('hAdvanced', __('Advanced', 'vollstart-repair-desk'), '', 'heading');
        $this->options['deleteDataOnUninstall'] = $this->makeOption('deleteDataOnUninstall', __('Delete all data when plugin is deleted', 'vollstart-repair-desk'), __('WARNING: When this is enabled and you delete the plugin via WordPress admin, ALL data will be permanently removed — all repairs, customers, messages, uploaded files, and settings. This cannot be undone. Leave this unchecked if you plan to reinstall or update the plugin later. Your data is safe during normal plugin updates — this only applies when you DELETE the plugin.', 'vollstart-repair-desk'), 'checkbox', 0);
    }

    /**
     * Create an option definition array.
     */
    public function makeOption(string $key, string $label, string $desc, string $type, $default = null, $additional = []): array {
        if ($default === null) {
            $default = ($type === 'checkbox' || $type === 'number') ? 0 : '';
        }
        $option = [
            'key'      => $key,
            'id'       => $this->prefix . $key,
            'label'    => $label,
            'desc'     => $desc,
            'type'     => $type,
            'default'  => $default,
            'value'    => $default,
            '_isLoaded' => false,
        ];
        foreach ($additional as $addKey => $addValue) {
            $option[$addKey] = $addValue;
        }
        return $option;
    }

    /**
     * Get all options with values loaded from DB.
     */
    public function getOptions(): array {
        $this->initOptions();
        $this->loadAll();
        return $this->options;
    }

    /**
     * Key → value map for wp_localize_script. Checkboxes as bool, rest as string.
     */
    public function getValuesMap(): array {
        $this->initOptions();
        $this->loadAll();
        $map = [];
        foreach ($this->options as $opt) {
            if ($opt['type'] === 'heading') continue;
            $map[$opt['key']] = $opt['type'] === 'checkbox'
                ? (!empty($opt['value']) && $opt['value'] !== '0')
                : $opt['value'];
        }
        return $map;
    }

    /**
     * Get options stripped down for JSON response (no internal fields).
     * Only sends what JS needs: key, label, desc, type, value, values (dropdown).
     * Registered extensions can decorate items via the `vollstart_rd_options_for_js` filter.
     */
    public function getOptionsForJS(): array {
        $this->initOptions();
        $this->loadAll();

        $result = [];
        foreach ($this->options as $opt) {
            // Skip hidden options (no label)
            if (empty($opt['label'])) continue;
            $item = [
                'key'   => $opt['key'],
                'label' => $opt['label'],
                'type'  => $opt['type'],
            ];
            if ($opt['type'] !== 'heading') {
                $item['value'] = $opt['value'];
                $item['desc'] = $opt['desc'];
                if (!empty($opt['values'])) $item['values'] = $opt['values'];
                if (isset($opt['min'])) $item['min'] = $opt['min'];
                if (isset($opt['max'])) $item['max'] = $opt['max'];
                if (isset($opt['step'])) $item['step'] = $opt['step'];
            }
            $result[] = $item;
        }
        /**
         * Filter the serialized options array sent to JS.
         * A registered extension can hook in to mark its own options (e.g. with
         * an `addon: true` flag) for visual styling. Second argument is the source
         * option array, useful for looking up flags stored on the source definition.
         */
        $result = apply_filters('vollstart_rd_options_for_js', $result, $this->options);
        return $result;
    }

    /**
     * Get all option keys.
     */
    public function getOptionsKeys(): array {
        $this->initOptions();
        return array_map(fn($o) => $o['key'], $this->options);
    }

    /**
     * Get a single option definition by key. Lazy-loads its value.
     */
    public function getOption(string $key): ?array {
        $this->initOptions();
        $key = trim($key);
        if (empty($key)) {
            return null;
        }
        foreach ($this->options as $idx => $option) {
            if ($option['key'] === $key) {
                if (!$option['_isLoaded']) {
                    $this->loadSingle($idx);
                }
                return $this->options[$idx];
            }
        }
        return null;
    }

    /**
     * Get option value by key. Returns default if not set.
     */
    public function getOptionValue(string $key) {
        // Before init: read directly from DB to avoid triggering __() too early
        if (!did_action('init') && $this->options === null) {
            return $this->getOptionValueDirect($key);
        }
        $option = $this->getOption($key);
        if ($option === null) {
            return null;
        }
        return $option['value'];
    }

    /**
     * Read a single option value directly from DB, bypassing initOptions().
     * Used before 'init' to avoid premature translation loading.
     */
    private function getOptionValueDirect(string $key) {
        $table = $this->MAIN->getDB()->getTabelle('options');
        $row = $this->MAIN->getDB()->getRow(
            "SELECT `option_value` FROM `{$table}` WHERE `option_key` = %s LIMIT 1",
            trim($key)
        );
        return $row ? $row['option_value'] : null;
    }

    /**
     * Check if a checkbox option is active.
     */
    public function isCheckboxActive(string $key): bool {
        $value = $this->getOptionValue($key);
        return !empty($value) && $value !== '0';
    }

    /**
     * Change an option value. Persists to DB.
     */
    public function changeOption(array $data): bool {
        $this->initOptions();
        $key = trim($data['key'] ?? '');
        $value = $data['value'] ?? '';

        foreach ($this->options as $idx => $option) {
            if ($option['key'] === $key) {
                $oldValue = $option['_isLoaded'] ? $option['value'] : $this->readFromDb($key, $option['default']);
                $this->writeToDb($key, $value);
                $this->options[$idx]['value'] = $value;
                $this->options[$idx]['_isLoaded'] = true;
                $this->logOptionChange($key, $oldValue, $value);
                return true;
            }
        }
        return false;
    }

    /**
     * Log an option change to the history table.
     * Pattern from Serial Codes plugin.
     */
    private function logOptionChange(string $key, $oldValue, $newValue): void {
        $oldStr = is_array($oldValue) ? wp_json_encode($oldValue) : (string) $oldValue;
        $newStr = is_array($newValue) ? wp_json_encode($newValue) : (string) $newValue;
        if ($oldStr === $newStr) return;
        try {
            $this->MAIN->getDB()->insert('options_history', [
                'option_key' => $key,
                'old_value'  => $oldStr,
                'new_value'  => $newStr,
                'changed_by' => get_current_user_id(),
                'changed_at' => wp_date('Y-m-d H:i:s'),
            ]);
        } catch (Exception $e) {
            // silently ignore if table does not exist yet
        }
    }

    /**
     * Read a single option value from DB (without caching).
     */
    private function readFromDb(string $key, $default = '') {
        global $wpdb;
        $tbl = $this->MAIN->getDB()->getTabelle('options');
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $val = $wpdb->get_var($wpdb->prepare(
            "SELECT option_value FROM %i WHERE option_key = %s",
            $tbl,
            $key
        ));
        return $val !== null ? $val : $default;
    }

    /**
     * Reset all options to their defaults.
     */
    public function resetAllToDefault(): void {
        $this->initOptions();
        foreach ($this->options as $idx => $option) {
            if ($option['type'] === 'heading') {
                continue;
            }
            $this->writeToDb($option['key'], $option['default']);
            $this->options[$idx]['value'] = $option['default'];
            $this->options[$idx]['_isLoaded'] = true;
        }
    }

    // --- Export / Import ---

    /**
     * Export all options as JSON-ready array.
     */
    public function exportOptions(): array {
        $this->initOptions();
        $this->loadAll();
        $export = [];
        foreach ($this->options as $opt) {
            if ($opt['type'] === 'heading') continue;
            $export[$opt['key']] = $opt['value'];
        }
        return [
            'plugin'      => 'vollstart-repair-desk',
            'version'     => $this->MAIN->getPluginVersion(),
            'exported_at' => gmdate('Y-m-d\TH:i:s\Z'),
            'options'     => $export,
        ];
    }

    /**
     * Import options from an array. Only imports known keys.
     */
    public function importOptions(array $optionsData): array {
        $validKeys = $this->getOptionsKeys();
        $imported = 0;
        $skipped = 0;

        foreach ($optionsData as $key => $value) {
            if (!in_array($key, $validKeys, true)) {
                $skipped++;
                continue;
            }
            $option = $this->getOption($key);
            if ($option === null || $option['type'] === 'heading') {
                $skipped++;
                continue;
            }
            $this->changeOption(['key' => $key, 'value' => $value]);
            $imported++;
        }

        return ['imported' => $imported, 'skipped' => $skipped];
    }

    // --- Private DB helpers ---

    /**
     * Load a single option's value from DB. $idx is the array key in
     * $this->options — since the May 2026 sections refactor that is the
     * option-key string (e.g. 'businessName'); kept untyped to remain
     * tolerant of the older numeric-index code path.
     */
    private function loadSingle($idx): void {
        $key = $this->options[$idx]['key'];
        $rows = $this->MAIN->getDB()->fetch('options', ['option_key' => $key]);
        if (!empty($rows)) {
            $this->options[$idx]['value'] = $rows[0]['option_value'];
        }
        $this->options[$idx]['_isLoaded'] = true;
    }

    private function loadAll(): void {
        $allRows = $this->MAIN->getDB()->fetch('options');
        $map = [];
        foreach ($allRows as $row) {
            $map[$row['option_key']] = $row['option_value'];
        }
        foreach ($this->options as $idx => $option) {
            if (isset($map[$option['key']])) {
                $this->options[$idx]['value'] = $map[$option['key']];
            }
            $this->options[$idx]['_isLoaded'] = true;
        }
    }

    private function writeToDb(string $key, $value): void {
        global $wpdb;
        $tbl = $this->MAIN->getDB()->getTabelle('options');
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $existing = $wpdb->get_var($wpdb->prepare(
            "SELECT id FROM %i WHERE option_key = %s",
            $tbl,
            $key
        ));
        if ($existing) {
            $wpdb->update($tbl, ['option_value' => $value], ['option_key' => $key]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        } else {
            $wpdb->insert($tbl, ['option_key' => $key, 'option_value' => $value]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        }
    }
}
