<?php
/**
 * @package CleverOgre
 * @subpackage woocommerce-download-template
 * @version 0.1.0
 * @since 0.1.0
 */

namespace WooCommerce\DownloadTemplate\Values;

defined('ABSPATH') || exit;

use \WooCommerce\DownloadTemplate\Product;

class GForms extends Appender {

    public function __construct() {
        $this->priority = 40;
        parent::__construct();
        add_filter('wc_download_template_download_args', [$this, 'get_download_args'], 10, 5);
    }

    private function get_form_id(\WC_Product|\WP_Post|int|string|bool $product = false):int|bool {
        if (!class_exists('\WC_GFPA_Main')) return false;
        if (is_bool($product) && $product === false) $product = get_the_ID();
        if (is_string($product) && is_numeric($product)) $product = intval($product);
        if (is_int($product) && function_exists('wc_get_product')) $product = wc_get_product($product);
        if (!is_object($product)) return false;
        if (is_a($product, '\WP_Post') && $product->post_type === 'product') $product = wc_get_product($product->ID);
        if (!is_a($product, '\WC_Product')) return false;

        $gravity_form_data = \WC_GFPA_Main::instance()->get_gravity_form_data(get_post($product->get_id()));
        if (!is_array($gravity_form_data) || empty($gravity_form_data)) return false;

        $form_id = rgar($gravity_form_data, 'id');
        if (!is_numeric($form_id)) return false;
        return intval($form_id);
    }

    private function get_form(\WC_Product|\WP_Post|int|string|bool $product = false):array|bool {
        if (!class_exists('\GFAPI')) return false;
        $form_id = $this->get_form_id($product);
        if (!is_int($form_id)) return false;
        $form = \GFAPI::get_form($form_id);
        if (!is_array($form) || empty($form)) return false;
        return $form;
    }

    private function has_form(\WC_Product|\WP_Post|int|string|bool $product = false):bool {
        return is_int($this->get_form_id($product));
    }

    public function get_download_args(array $args, \WC_Order_Item $order_item, \WC_Order|bool $order, \WC_Product $product, \WC_Customer_Download $download):array {
        if (!$this->has_form($product)) return $args;

        $history = maybe_unserialize($order_item->get_meta('_gravity_forms_history', true));
        if (empty($history)) return $args;

        $args['gf_entry'] = isset($history['_gravity_form_lead']) ? $history['_gravity_form_lead'] : false;

        // NOTE: Form data doesn't include fields
        $args['gf_form'] = rgar(isset($history['_gravity_form_data']) ? $history['_gravity_form_data'] : false, 'id');

        return $args;
    }

    public function get_default_args(array $args = []):array {
        $args['gf_entry'] = false;
        $args['gf_form'] = false;
        return $args;
    }

    private function get_entry($entry_id = false) {
        if (is_array($entry_id) && array_key_exists('form_id', $entry_id)) return $entry_id; // NOTE: Not checking for 'id' key because it doesn't exist in WC order item meta data
        if (is_string($entry_id) && is_numeric($entry_id)) $entry_id = intval($entry_id);
        if (!is_int($entry_id)) return [];

        if (!class_exists('\GFAPI')) return [];
        $entry = \GFAPI::get_entry($entry_id);
        return is_array($entry) ? $entry : [];
    }

    private function get_field_data(\GF_Field $field, array $entry, string $prepend = 'field_', bool $use_labels = false):array {
        $label = $field->get_field_label(false, '');
        $values = [];
        switch ($field->get_input_type()) {
            case 'section':
            case 'html':
            case 'captcha':
            case 'password':
            case 'page':
                break;
            case 'form':
                $nested_form_id = intval($field->gpnfForm);
                $nested_entry_ids = explode(',', rgar($entry, $field->id));
                foreach ((array)$nested_entry_ids as $i => $nested_entry_id) {
                    $values = array_merge($values, $this->get_entry_data(
                        $nested_entry_id,
                        $nested_form_id,
                        sprintf('%s%s_%d_', $prepend, $field->id, ($i + 1)),
                        $use_labels
                    ));
                }

                // Fill empty rows
                $max = property_exists($field, 'gpnfEntryLimitMax') ? intval($field->gpnfEntryLimitMax) : false;
                if (!!$max && count($nested_entry_ids) < $max) {
                    for ($i = count($nested_entry_ids); $i < $max; $i++) {
                        $values = array_merge($values, $this->get_entry_data(
                            [],
                            $nested_form_id,
                            sprintf('%s%s_%d_', $prepend, $field->id, ($i + 1)),
                            $use_labels
                        ));
                    }
                }
                break;
            case 'list':
                $data = maybe_unserialize(rgar($entry, $field->id));
                $columns = property_exists($field, 'enableColumns') ? !!$field->enableColumns : false;
                foreach ($data as $i => $row) {
                    if ($columns) {
                        $j = 0;
                        foreach ($row as $column_label => $value) {
                            $key = sprintf('%s%s_%d_%d', $prepend, $field->id, ($i+1), ($j+1));
                            $values[$key] = !!$use_labels ? $column_label : $value;
                            $j++;
                        }
                    } else {
                        $key = sprintf('%s%s_%d', $prepend, $field->id, ($i+1));
                        $values[$key] = !!$use_labels ? $label : $row;
                    }
                }
                if (!is_array($data)) $data = [];

                $max = property_exists($field, 'maxRows') ? intval($field->maxRows) : false;
                if (!!$max && count($data) < $max) {
                    for ($i = count($data); $i < $max; $i++) {
                        if ($columns) {
                            for ($j = 0; $j < (property_exists($field, 'choices') && is_array($field->choices) ? count($field->choices) : 1); $j++) {
                                $key = sprintf('%s%s_%d_%d', $prepend, $field->id, ($i+1), ($j+1));
                                $values[$key] = !!$use_labels ? $label : '';
                            }
                        } else {
                            $key = sprintf('%s%s_%d', $prepend, $field->id, ($i+1));
                            $values[$key] = !!$use_labels ? $label : '';
                        }
                    }
                }

                break;
            case 'number':
                $value = rgar($entry, $field->id);
                $values[$prepend . strval($field->id)] = !!$use_labels ? $label : $value;
                $values[$prepend . strval($field->id) . '.readable'] = !!$use_labels ? sprintf(__('%s (Human Readable)', wp_get_theme(get_stylesheet())->get('TextDomain')), $label) : ucwords(\NumberFormatter::create('en', \NumberFormatter::SPELLOUT)->format($value));
                break;
            case 'date':
                $value = rgar($entry, $field->id);
                if (!empty($value)) {
                    $dt = \DateTime::createFromFormat('Y-m-d', $value);
                    if (!!$dt) $value = $dt->format(get_option('date_format'));
                }
                $values[$prepend . strval($field->id)] = !!$use_labels ? $label : $value;
                break;
            default:
                if (is_array($field->inputs) && !empty($field->inputs)) {
                    if (in_array($field->get_input_type(), ['name'])) {
                        $value = [];
                        foreach ($field->inputs as $input) {
                            $input_value = rgar($entry, $input['id']);
                            if (empty($input_value)) continue;
                            $value[$input['id']] = $input_value;
                        }
                        $values[$prepend . strval($field->id)] = !!$use_labels ? $label : implode(' ', $value);
                    }
                    foreach ($field->inputs as $input) {
                        $values[$prepend . strval($input['id'])] = !!$use_labels ? sprintf('%s (%s)', $label, $input['label']) : rgar($entry, $input['id']);
                    }
                } else {
                    $values[$prepend . strval($field->id)] = !!$use_labels ? $label : rgar($entry, $field->id);
                }
                break;
        }
        return $values;
    }

    private function get_entry_data($entry_id = false, $form_id = false, string $prepend = 'field_', bool $use_labels = false):array {
        $entry = $this->get_entry($entry_id);
        // NOTE: Don't bother checking validity of entry.

        if (!$form_id && !empty($entry)) $form_id = rgar($entry, 'form_id');
        $form = \GFAPI::get_form($form_id);
        if (!$form) return [];

        $values = [];
        foreach ((array)$form['fields'] as $field) {
            $values = array_merge($this->get_field_data($field, $entry, $prepend, $use_labels), $values);
        }

        return apply_filters('wc_download_template_gforms_entry_values', $values, $entry, $form, $prepend, $use_labels);
    }

    private function get_form_data($form_id = false, string $prepend = 'field_', bool $use_labels = false):array {
        return $this->get_entry_data(false, $form_id, $prepend, $use_labels);
    }

    public function get(array $values = [], array $args = []):array {
        if (!isset($args['gf_entry']) || !$args['gf_entry']) return $values;

        $_values = $this->get_entry_data($args['gf_entry'], isset($args['gf_form']) ? $args['gf_form'] : false);

        return array_merge($values, apply_filters('wc_download_template_gforms_values', $_values, $args['gf_entry'], isset($args['gf_form']) ? $args['gf_form'] : false));
    }

    public function get_example(array $values = []):array {
        if (!class_exists('\WC_GFPA_Main')) return $values;

        global $post;
        $form_id = $this->get_form_id($post);
        if (!is_int($form_id)) return $values;

        return array_merge($values, $this->get_form_data($form_id, 'field_', true));
    }

}

GForms::instance();
