<?php

namespace RTMCustomMails\ContextBuilder;

use RTMCustomMails\Util;
use RTMMailComposer\ContextBuilder\AbstractObjectContextBuilder;
use RTMMailComposer\Model\CustomMail;
use WC_Abstract_Order;
use WC_Meta_Data;
use WP_User;

/**
 * @template T of WC_Abstract_Order
 * @extends AbstractObjectContextBuilder<T>
 */
class OrderContextBuilder extends AbstractObjectContextBuilder
{

    public function __construct()
    {
        parent::__construct('order');
    }

    /**
     * @param CustomMail $mail
     * @param T $mailObject
     * @param array $context
     * @return array
     * @throws \ReflectionException
     */
    public function prepareContext(CustomMail $mail, $mailObject, array $context = []): array
    {
        $order = $mailObject ?? null;
        if ($order) {
            if ($user = new WP_User($order->get_customer_id())) {
                $context['customer'] = $user;
            }

            $context['recipient_name'] = $user->get_billing_first_name();

            $meta = $this->getMeta($order);

            list($billing_meta, $meta) = $this->getBilling($meta);

            $order_getters = Util::getClassMethods($order, 'get_order_', ['currency']);
            $billing_getters = Util::getClassMethods($order, 'get_billing_', []);
            $formatted_getters = Util::getClassMethods($order, 'get_formatted_', []);
            $customer_getters = Util::getClassMethods($order, 'get_customer_', ['id', 'ip_address', 'user_agent']);
            $ctx = Util::invokeClassMethods(
                [...$order_getters, ...$billing_getters, ...$formatted_getters, ...$customer_getters],
                'get_',
                $order
            );
            $context = array_merge($context, $ctx, $billing_meta, ['order_meta' => $meta]);
        }
        return apply_filters('wrce_inject_context/object_type=order', $context, $mail, $mailObject);
    }

    public function getWhitelist(): array
    {
        $order_getters = Util::getClassMethods(WC_Abstract_Order::class, 'get_order_', ['currency']);
        $billing_getters = Util::getClassMethods(WC_Abstract_Order::class, 'get_billing_', []);
        $formatted_getters = Util::getClassMethods(WC_Abstract_Order::class, 'get_formatted_', []);
        $customer_getters = Util::getClassMethods(WC_Abstract_Order::class, 'get_customer_', ['id', 'ip_address', 'user_agent']);

        $propNames = array_reduce([...$order_getters, ...$billing_getters, ...$formatted_getters, ...$customer_getters],
            function (array $c, \ReflectionMethod $m) {
                $c[] = str_replace('get_', '', $m->getName());
                return $c;
            }, []);

        return [
            'customer',
            'recipient_name',
            ...$propNames,
        ];
    }

    /**
     * @param WC_Abstract_Order $order
     * @return array
     */
    public function getMeta(WC_Abstract_Order $order)
    {
        return array_reduce($order->get_meta_data(), function (array $c, WC_Meta_Data $m) {
            $meta_key = $m->get_data()['key'];
            $key = str_starts_with($meta_key, '_') ? substr($meta_key, 1) : $meta_key;
            $c[$key] = $m->get_data()['value'];
            return $c;
        }, []);
    }

    /**
     * @param array $meta
     * @return array
     */
    public function getBilling(mixed $meta): array
    {
        $billing_meta = array_filter($meta, fn($key) => str_starts_with($key, 'billing_'), ARRAY_FILTER_USE_KEY);
        $meta = array_filter(array_diff_assoc($meta, $billing_meta), fn($k) => !str_starts_with($k, 'buckaroo_'), ARRAY_FILTER_USE_KEY);
        return array($billing_meta, $meta);
    }

}
