<?php

namespace Storeplugin\PDFInvoice\App\Settings;

defined('ABSPATH') || exit;

/**
 * Centralized settings schema used by render, templates, activation, and ajax sanitization.
 */
class SettingsSchema
{
    public const GROUP_INVOICE_EDITOR = 'nxtpdf_invoice_editor';
    public const GROUP_PACKING_SLIP_EDITOR = 'nxtpdf_packing_slip_editor';
    public const GROUP_SETTINGS = 'nxtpdf_settings';
    public const WIZARD_COMPLETED_OPTION = 'nxtpdf_wizard_completed';
    public const WIZARD_REDIRECT_TRANSIENT = 'nxtpdf_activation_redirect';

    /**
     * Editor option defaults backed by field placeholders/defaults.
     *
     * @return array<string, mixed>
     */
    public static function editorDefaults(): array
    {
        return [
            'invoice_title' => 'INVOICE',
            'packing_title' => 'Packing Slip',
            'template_selector' => 'default',
            'header_font_size' => '10',
            'logo_height' => '150',
            'footer_font_size' => '10',
            'terms_title' => 'Terms & Conditions',
            'terms_text' => '',
            'return_policy_title' => 'Return Policy',
            'return_policy_text' => '',
            'bill_to_label' => 'Bill To',
            'payment_method_label' => 'Payment Method',
            'ship_to_label' => 'Ship To',
            'shipping_label' => 'Shipping',
            'item_label' => 'Item',
            'qty_label' => 'Qty',
            'price_label' => 'Price',
            'total_label' => 'Total',
            'subtotal_label' => 'Subtotal',
            'decimal_places' => '2',
            'currency_format' => 'symbol',
            'currency_position' => 'left',
            'decimal_separator' => '.',
            'thousand_separator' => ',',
            'invoice_number_padding' => '4',
            'invoice_number_next' => '1',
            'paper_size' => 'A4',
            'paper_orientation' => 'P',
            'page_margin_top' => '15',
            'page_margin_right' => '15',
            'page_margin_bottom' => '15',
            'page_margin_left' => '15',
        ];
    }

    /**
     * General option defaults backed by field placeholders/defaults.
     *
     * @return array<string, mixed>
     */
    public static function settingsDefaults(): array
    {
        return [
            'company_name' => 'Enter company name',
            'logo_url' => 'No image selected',
            'company_address_1' => 'Main Street, Anytown, NY 12345, etc.',
            'company_address_2' => '',
            'company_city' => '',
            'company_state' => '',
            'company_postcode' => '',
            'company_country' => '',
            'company_phone' => '',
            'company_email' => 'billing@company.com',
            'company_vat' => 'VAT/Tax ID',
            'company_coc' => '',
            'company_website' => 'https://example.com',
            'company_registration' => '',
            'company_gstin' => '',
            'company_abn' => '',
            'bank_details' => '',
            'additional_header_text' => '',
            'pdf_filename_format' => 'invoice-{order_number}',
            'invoice_number_prefix' => 'INV-',
            'invoice_date_format' => 'Y-m-d',
            'invoice_date_label' => 'Invoice Date',
            'order_date_label' => 'Order Date',
        ];
    }

    /**
     * Editor keys used by rendering/templates.
     *
     * @return array<int, string>
     */
    public static function editorKeys(): array
    {
        static $keys = null;

        if ($keys !== null) {
            return $keys;
        }

        $keys = array_values(array_unique(array_merge(
            array_keys(self::editorDefaults()),
            [
                'header_text',
                'header_align',
                'header_color',
                'show_logo',
                'show_company_name',
                'show_company_address',
                'show_company_phone',
                'show_company_email',
                'show_company_website',
                'show_company_vat',
                'show_company_coc',
                'show_company_registration',
                'show_company_gstin',
                'show_company_abn',
                'show_bank_details',
                'template_selector',
                'footer_text',
                'footer_align',
                'footer_color',
                'show_page_numbers',
                'show_order_notes',
                'show_billing_address',
                'show_shipping_address',
                'show_billing_email',
                'show_billing_phone',
                'show_discount_total',
                'show_coupon_details',
                'show_fees',
                'show_shipping_tax',
                'show_tax_by_rate',
                'show_sku',
                'show_variations',
                'show_item_meta',
                'show_product_image',
                'show_tax_column',
                'show_discount_column',
                'unit_price_source',
                'currency_format',
                'currency_position',
                'invoice_number_mode',
                'invoice_number_reset',
                'paper_size',
                'paper_orientation',
                'tax_label',
                'discount_label',
            ]
        )));

        return $keys;
    }

    /**
     * General keys used by rendering/templates.
     *
     * @return array<int, string>
     */
    public static function settingsKeys(): array
    {
        static $keys = null;

        if ($keys !== null) {
            return $keys;
        }

        $keys = array_values(array_unique(array_merge(
            array_keys(self::settingsDefaults()),
            [
                'invoice_number_suffix',
                'invoice_date_source',
            ]
        )));

        return $keys;
    }

    /**
     * Unified map of every key with its group and optional default.
     *
     * @return array<string, array{group: string, default?: mixed}>
     */
    public static function allKeys(): array
    {
        static $keys = null;

        if ($keys !== null) {
            return $keys;
        }

        $keys = [];

        foreach (self::editorDefaults() as $optionKey => $defaultValue) {
            $keys[$optionKey] = ['group' => self::GROUP_INVOICE_EDITOR, 'default' => $defaultValue];
        }
        foreach (self::editorKeys() as $optionKey) {
            $keys[$optionKey] ??= ['group' => self::GROUP_INVOICE_EDITOR];
        }
        foreach (self::settingsDefaults() as $optionKey => $defaultValue) {
            $keys[$optionKey] = ['group' => self::GROUP_SETTINGS, 'default' => $defaultValue];
        }
        foreach (self::settingsKeys() as $optionKey) {
            $keys[$optionKey] ??= ['group' => self::GROUP_SETTINGS];
        }

        return $keys;
    }

    /**
     * Boolean keys handled in ajax sanitize.
     *
     * @return array<int, string>
     */
    public static function booleanKeys(): array
    {
        return [
            'show_logo',
            'show_company_name',
            'show_company_address',
            'show_company_phone',
            'show_company_email',
            'show_company_website',
            'show_company_vat',
            'show_company_coc',
            'show_company_registration',
            'show_company_gstin',
            'show_company_abn',
            'show_bank_details',
            'show_order_notes',
            'show_page_numbers',
            'show_billing_address',
            'show_shipping_address',
            'show_billing_email',
            'show_billing_phone',
            'show_discount_total',
            'show_coupon_details',
            'show_fees',
            'show_shipping_tax',
            'show_tax_by_rate',
            'show_sku',
            'show_variations',
            'show_item_meta',
            'show_product_image',
            'show_tax_column',
            'show_discount_column',
        ];
    }

    /**
     * Numeric keys handled in ajax sanitize.
     *
     * @return array<int, string>
     */
    public static function numericKeys(): array
    {
        return [
            'logo_height',
            'header_font_size',
            'footer_font_size',
            'page_margin_top',
            'page_margin_right',
            'page_margin_bottom',
            'page_margin_left',
            'decimal_places',
            'invoice_number_padding',
            'invoice_number_next',
        ];
    }

    /**
     * Color keys handled in ajax sanitize.
     *
     * @return array<int, string>
     */
    public static function colorKeys(): array
    {
        return ['header_color', 'footer_color'];
    }

    /**
     * URL keys handled in ajax sanitize.
     *
     * @return array<int, string>
     */
    public static function urlKeys(): array
    {
        return ['logo_url', 'company_website'];
    }

    /**
     * Textarea keys handled in ajax sanitize.
     *
     * @return array<int, string>
     */
    public static function textareaKeys(): array
    {
        return ['footer_text', 'terms_text', 'return_policy_text'];
    }

    /**
     * Enum validation rules for ajax sanitize.
     *
     * @return array<string, array{values: array<int, string>, default: string}>
     */
    public static function enumRules(): array
    {
        return [
            'paper_size' => [
                'values' => ['A4'],
                'default' => 'A4',
            ],
            'paper_orientation' => [
                'values' => ['P'],
                'default' => 'P',
            ],
            'header_align' => [
                'values' => ['left', 'center', 'right'],
                'default' => 'center',
            ],
            'footer_align' => [
                'values' => ['left', 'center', 'right'],
                'default' => 'center',
            ],
            'currency_format' => [
                'values' => ['symbol'],
                'default' => 'symbol',
            ],
            'currency_position' => [
                'values' => ['left'],
                'default' => 'left',
            ],
            'template_selector' => [
                'values' => ['default'],
                'default' => 'default',
            ],
            'unit_price_source' => [
                'values' => ['subtotal', 'total'],
                'default' => 'subtotal',
            ],
            'invoice_number_mode' => [
                'values' => ['order_number', 'sequential'],
                'default' => 'order_number',
            ],
            'invoice_number_reset' => [
                'values' => ['never', 'yearly', 'monthly'],
                'default' => 'never',
            ],
        ];
    }

    /**
     * Sanitize a single setting value based on its key type.
     *
     * @param string $key
     * @param mixed  $value
     * @return mixed
     */
    public static function sanitizeValue(string $key, $value)
    {
        if (is_array($value)) {
            return array_map(function ($item) {
                return is_scalar($item) ? sanitize_text_field((string) $item) : '';
            }, $value);
        }

        $value = is_scalar($value) ? (string) $value : '';

        if (in_array($key, self::booleanKeys(), true)) {
            return !empty($value);
        }

        if (in_array($key, self::numericKeys(), true)) {
            return trim($value) === '' ? '' : absint($value);
        }

        if (in_array($key, self::colorKeys(), true)) {
            return sanitize_hex_color($value) ?: '';
        }

        if (in_array($key, self::urlKeys(), true)) {
            return esc_url_raw($value);
        }

        if ($key === 'company_email') {
            return sanitize_email($value);
        }

        if (in_array($key, self::textareaKeys(), true)) {
            return sanitize_textarea_field($value);
        }

        $enumRules = self::enumRules();
        if (isset($enumRules[$key])) {
            $rule = $enumRules[$key];
            return in_array($value, $rule['values'], true) ? $value : $rule['default'];
        }

        return sanitize_text_field($value);
    }

    /**
     * Free-version locked values for a specific option group.
     *
     * @return array<string, mixed>
     */
    public static function lockedValuesForGroup(string $group): array
    {
        $group = sanitize_key($group);

        if ($group === self::GROUP_SETTINGS) {
            return [
                'company_coc' => '',
                'company_registration' => '',
                'company_gstin' => '',
                'company_abn' => '',
                'bank_details' => '',
            ];
        }

        if (in_array($group, [self::GROUP_INVOICE_EDITOR, self::GROUP_PACKING_SLIP_EDITOR], true)) {
            return [
                'show_company_coc' => false,
                'show_company_registration' => false,
                'show_company_gstin' => false,
                'show_company_abn' => false,
                'show_bank_details' => false,
                'template_selector' => 'default',
                'invoice_number_padding' => 4,
                'invoice_number_next' => 1,
                'decimal_places' => 2,
                'currency_format' => 'symbol',
                'currency_position' => 'left',
                'paper_size' => 'A4',
                'paper_orientation' => 'P',
            ];
        }

        return [];
    }

    /**
     * Enforce free-version restrictions for an option-group payload.
     *
     * @param array<string, mixed> $options
     * @return array<string, mixed>
     */
    public static function normalizeOptionsForGroup(array $options, string $group): array
    {
        $lockedValues = self::lockedValuesForGroup($group);
        if ($lockedValues === []) {
            return $options;
        }

        foreach ($lockedValues as $key => $value) {
            $options[$key] = $value;
        }

        return $options;
    }

    /**
     * Enforce free-version restrictions for merged runtime settings.
     *
     * @param array<string, mixed> $settings
     * @return array<string, mixed>
     */
    public static function normalizeRuntimeSettings(array $settings, string $editorGroup): array
    {
        $settings = self::normalizeOptionsForGroup($settings, self::GROUP_SETTINGS);
        $settings = self::normalizeOptionsForGroup($settings, $editorGroup);

        return $settings;
    }
}
