<?php

namespace RTMCustomMails\WooCommerce;

use ArrayAccess;
use WC_Email;
use WordpressModels\Traits\SingletonTrait;

/**
 * Class to convert WooCommerce email form fields to JSON Schema.
 */
final class WooCommerceSchemaConverter
{

    use SingletonTrait;

    protected function __construct()
    {

        add_filter('wrce_woocommerce_json_schema_mapping/field_type=checkbox', [$this, 'checkboxFieldMapping']);
        add_filter('wrce_woocommerce_json_schema_mapping/field_type=text', [$this, 'stringFieldMapping']);
        add_filter('wrce_woocommerce_json_schema_mapping/field_type=textarea', [$this, 'stringFieldMapping']);

        add_filter('wrce_woocommerce_json_schema_mapping/field_type=radio', [$this, 'selectFieldMapping'], 10, 2);
        add_filter('wrce_woocommerce_json_schema_mapping/field_type=select', [$this, 'selectFieldMapping'], 10, 2);

        add_filter('wrce_woocommerce_json_schema_mapping/field_type=multiselect', [$this, 'multiselectFieldMapping'], 10, 2);


        // the following fields work well with default config (formatted string type)
//        add_filter('wrce_woocommerce_json_schema_mapping/field_type=email', [$this, 'emailFieldMapping']);
//        add_filter('wrce_woocommerce_json_schema_mapping/field_type=color', [$this, 'colorFieldMapping']);
    }

    /**
     * Get the JSON Schema for a WooCommerce email.
     *
     * Sub-schemas are generated for each field in a similar way to the WooCommerce HTML renderer, but here
     * we use WordPress hooks to generate the sub-schema associative array.
     *
     * Conditional schemas are not supported.
     *
     * @param WC_Email $email
     * @return array an associative array representing the JSON Schema
     */
    public static function getJsonSchema(WC_Email $email): array
    {
        $jsonSchema = [
            '$schema' => 'http://json-schema.org/draft-07/schema#',
            'type' => 'object',
            'properties' => [],
            'required' => [],
        ];

        foreach ($email->get_form_fields() as $fieldKey => $fieldData) {
            // Skip fields with UI properties
            if (isset($fieldData['css'])) {
                continue;
            }

            $field_type = self::determineFieldType($fieldData);

            if ($field_type) {
                // defaults
                $property = [
                        'title' => $fieldData['title'],
                        'default' => $fieldData['default']
                    ] + $field_type;

                // add description if available
                if (isset($fieldData['description'])) {
                    $property['description'] = html_entity_decode($fieldData['description']);
                }

                // add options if available
                if (isset($fieldData['options'])) {
                    // eliminate option groups
                    $flatOptions = self::flatOptions($fieldData['options']);
                    if ($field_type['type'] === 'array') {
                        $property['items']['enum'] = array_keys($flatOptions);
                        $property['items']['enumNames'] = array_values($flatOptions);
                    } else {
                        $property['enum'] = array_keys($flatOptions);
                        $property['enumNames'] = array_values($flatOptions);
                    }
                }

                // add to properties
                $jsonSchema['properties'][$fieldKey] = $property;

                // add to required if required
                if ($fieldData['required'] ?? false) {
                    $jsonSchema['required'][] = $fieldKey;
                }
            }
        }

        return $jsonSchema;
    }

    /**
     * Determine the JSON Schema type for a WooCommerce email field.
     *
     * @param array|ArrayAccess $data
     * @return array|null
     */
    private static function determineFieldType(array|ArrayAccess $data): ?array
    {
        return apply_filters('wrce_woocommerce_json_schema_mapping/field_type=' . $data['type'], [
            'type' => 'string', // default (email, color, etc.)
            'format' => $data['type']
        ], $data);
    }

    /**
     * Flatten an array of options.
     *
     * @param array $options
     * @return array
     */
    private static function flatOptions(array $options)
    {
        $flattened = [];

        foreach ($options as $key => $value) {
            if (is_array($value)) {
                $flattened = array_merge($flattened, self::flatOptions($value));
            } else {
                $flattened[$key] = $value;
            }
        }

        return $flattened;
    }


    /* Field Mappings */

    public function checkboxFieldMapping()
    {
        return [
            'type' => 'boolean'
        ];
    }

    /**
     * Unformatted string
     * @return string[]
     */
    public function stringFieldMapping()
    {
        return [
            'type' => 'string',
        ];
    }

    /**
     * Mapping for select and radio fields for mutex values.
     *
     * @param array $fieldData
     * @return array
     */
    public function selectFieldMapping(array $_, array|ArrayAccess $fieldData)
    {
        $options = $fieldData['options'] ?? [];
        $itemType = gettype(current(array_keys($options))) !== 'string' ? 'number' : 'string';

        return [
            'type' => $itemType,
            'enum' => array_keys($options),
            'enumNames' => array_values($options),
        ];
    }

    /**
     * Multi-select field mapping.
     * @param array $fieldData
     * @return array
     */
    public function multiselectFieldMapping(array $_, array|ArrayAccess $fieldData)
    {
        $options = $fieldData['options'] ?? [];
        $itemType = gettype(current(array_keys($options))) !== 'string' ? 'number' : 'string';

        return [
            'type' => 'array',
            'items' => [
                'type' => $itemType,
                'enum' => array_keys($options),
                'enumNames' => array_values($options),
            ],
            "uniqueItems" => true
        ];
    }

}
