<?php

namespace RTMCustomMails\ObjectSupport;

use ReflectionMethod;
use RTMCustomMails\ConditionProcessor\OrderHasCouponCondition;
use RTMCustomMails\ConditionProcessor\OrderHasDownloadableItemCondition;
use RTMCustomMails\ConditionProcessor\OrderHasFreeItemCondition;
use RTMCustomMails\ConditionProcessor\OrderHasProductCondition;
use RTMCustomMails\ConditionProcessor\OrderHasProductWithAttributeCondition;
use RTMCustomMails\ConditionProcessor\OrderHasShippingMethodCondition;
use RTMCustomMails\ConditionProcessor\OrderHasStatusCondition;
use RTMCustomMails\ConditionProcessor\OrderIsGuestCondition;
use RTMCustomMails\ConditionProcessor\UserFirstTimeCustomerCondition;
use RTMCustomMails\Rest\Model\ObjectSearchModel;
use RTMCustomMails\Util;
use RTMCustomMails\WCPDFDocument;
use WC_Abstract_Order;
use WC_Order;
use WPO\WC\PDF_Invoices\Documents\Order_Document;
use WPO_WCPDF;

class EmailOrderSupport extends EmailObjectSupport
{

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

    public function getAllowedContextVariables($vars = []): array
    {
        $order = new WC_Order();
        $order_getters = Util::getClassMethods($order, 'get_order_', []);
        $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']);

        $names = array_map(fn(ReflectionMethod $method) => substr($method->getName(), 4), [
            ...$order_getters,
            ...$billing_getters,
            ...$formatted_getters,
            ...$customer_getters,
        ]);

        return array_unique([
            ...$vars,
            ...$names,
            'customer',
            'order',
        ]);
    }

    protected function getSelectorConfig(): array
    {
        return [
            'valueField' => 'orderId',
            'searchField' => [
                [
                    'field' => 'orderNumber',
                    'weight' => 2
                ],
                [
                    'field' => 'orderId',
                    'weight' => 2
                ]
            ],
            'render' => [
                'option' => '<div>{text}</div>',
                'item' => '<div>{text}</div>'
            ]
        ];
    }

    public function findObjects(array $objects, string $search): array
    {
        $orders = wc_get_orders([
            'status' => array_keys(wc_get_order_statuses()),
            'limit' => 20,
            '_order_number_like' => "$search"
        ]);
        return array_merge($objects, array_map([$this, 'orderAsSearchModel'], $orders));
    }

    private function orderAsSearchModel(WC_Abstract_Order $order)
    {
        $orderNumber = method_exists($order, 'get_order_number') ? $order->get_order_number() : ($order->get_meta('_order_number') ?: $order->get_id());
        return new ObjectSearchModel($order->get_id(), "Order #{$orderNumber} ({$order->get_status()})");
    }

    public function simplifyOrder(WC_Abstract_Order $order): array
    {
        if ($order instanceof WC_Order) {
            if ($order->get_order_number() !== (string)$order->get_id()) {
                $text = "Order #{$order->get_order_number()} ({$order->get_id()})";
            } else {
                $text = "Order #{$order->get_order_number()}";
            }
        } else {
            $text = "Order #{$order->get_id()} ({$order->get_status()})";
        }
        return [
            "orderId" => $order->get_id(),
            "orderNumber" => $order instanceof WC_Order ? $order->get_order_number() : null,
            "text" => $text
        ];
    }

    public function getLatestObjects(int $amount = 20): array
    {
        $orders = wc_get_orders([
            'status' => array_keys(wc_get_order_statuses()),
            'limit' => 20,
        ]);
        return array_map([$this, 'simplifyOrder'], $orders);
    }

    public function addTriggerHookOptions(array $options = []): array
    {
        // generate status transition hook and labels
        $statuses = wc_get_order_statuses();
        foreach ($statuses as $statusIdA => $statusNameA) {
            // generate base hook name
            $baseHook = 'woocommerce_order_status_' . substr($statusIdA, 3);

            // add {$basehook}_notification hook (deferred emails)
            $options['Order Status Notifications'][$baseHook . '_notification'] = "When an order changes to status '$statusNameA'";
            foreach ($statuses as $statusIdB => $statusNameB) {
                if ($statusIdA === $statusIdB) {
                    // skip same hooks, not possible
                    continue;
                }
                // generate transition hooks
                $transitionHook = $baseHook . '_to_' . substr($statusIdB, 3) . '_notification';
                $options['Order Status Transitions'][$transitionHook] = "When an order changes status from '$statusNameA' to '$statusNameB'";
            }
        }

        return $options + [
                'Refunds' => [
                    'woocommerce_order_fully_refunded' => 'When an order is fully refunded',
                    'woocommerce_order_partially_refunded' => 'When an order is partially refunded.',
                ],
                'woocommerce_new_customer_note_notification' => 'When a customer adds a new note to an order.'
            ];
    }

    public function addConditionFunctions(array $functionClasses = []): array
    {
        return [
            ...$functionClasses,
            OrderHasProductCondition::class,
            UserFirstTimeCustomerCondition::class,
            OrderHasProductWithAttributeCondition::class,
            OrderHasStatusCondition::class,
            OrderHasDownloadableItemCondition::class,
            OrderIsGuestCondition::class,
            OrderHasCouponCondition::class,
            OrderHasFreeItemCondition::class,
        ];
    }

    public function addDocumentSupport(array $documents = []): array
    {
        if (!class_exists('WPO_WCPDF')) {
            return $documents;
        }

        $docs = WPO_WCPDF::instance()->documents->get_documents();

        return $documents + array_values(array_map(fn(Order_Document $document) => WCPDFDocument::createFromDocument($document), $docs));
    }

}