<?php

declare(strict_types=1);

namespace MyCustomizer\WooCommerce\Connector\Controller;

use Exception;
use MyCustomizer\WooCommerce\Connector\Controller\MczrCartController;
use MyCustomizer\WooCommerce\Connector\Libs\MczrConnect;
use MyCustomizer\WooCommerce\Connector\Libs\MczrSettings;
use WC_Meta_Data;
use WC_Order;
use WC_Order_Item;
use WC_Order_Item_Product;
use WC_Product;

class MczrOrderController
{
    /** @var MczrConnect */
    private $mczr;
    /** @var MczrCartController */
    private $mczrCartController;
    /** @var MczrSettings */
    private $settings;

    public function __construct()
    {
        $this->mczr = new MczrConnect();
        $this->mczrCartController = new MczrCartController();
        $this->settings = new MczrSettings();
    }

    public function init(): void
    {
        \add_action('woocommerce_order_status_changed', [$this, 'onOrderStatusChanged'], 99, 3);
        \add_action('woocommerce_update_order_item', [$this, 'onUpdateOrderItem'], 99, 3);
        \add_action('woocommerce_checkout_order_processed', [$this, 'onCheckoutOrderProcessed'], 99, 3);
        \add_action('woocommerce_payment_complete', [$this, 'onPaymentComplete'], 99);
        \add_action('woocommerce_delete_order', [$this, 'onOrderDeleted'], 99);
        \add_action('woocommerce_order_item_meta_start', [$this, 'displayMczrOrderItems'], 10, 3);
    }

    /**
     * @param WC_Meta_Data[] $metas
     * @param string $key
     * @param string $keyName
     *
     * @return array|string|false
     */
    private function extractMeta($metas, string $key = 'mczr', string $keyName = 'designId')
    {
        if (empty($metas)) {
            return false;
        }

        foreach ($metas as $meta) {
            if ($meta->get_data()['key'] != $key) {
                continue;
            }

            // wildcard * returns all
            if ('*' == $keyName) {
                return $meta->get_data()['value'];
            }

            // ...or a specific value
            if (isset($meta->get_data()['value'][$keyName]) && !empty($meta->get_data()['value'][$keyName])) {
                return $meta->get_data()['value'][$keyName];
            }
        }

        return false;
    }

    /**
     * @param mixed $productId
     */
    private function isMczrProductById($productId): bool
    {
        $product = \wc_get_product($productId);

        return $product instanceof WC_Product && $product->is_type('mczr');
    }

    public function getKickflipOrders(string $fromDate): array
    {
        $ordersArgs = [
            'date_modified' => ">=$fromDate"
        ];
        $orders = \wc_get_orders($ordersArgs);
        $kickflipOrders = [];

        foreach ($orders as $order) {
            $kickflipItems = [];
            $items = $order->get_items();

            /** @var WC_Order_Item_Product $item */
            foreach ($items as $item) {
                $metas = $item->get_meta_data();
                $productId = $item->get_data()['product_id'];

                if (!$this->isMczrProductById($productId)) {
                    continue;
                }

                $metasMczr = $this->extractMeta($metas, 'mczr', '*');

                if (!isset($metasMczr['designId'])) {
                    continue;
                }

                $props = [
                    'design' => $metasMczr['designId'],
                    'quantity' => $item->get_data()['quantity'],
                    'eCommerceItemId' => $item->get_data()['id'],
                ];

                if ($item->get_subtotal() != $item->get_total() && $item->get_total() < $item->get_subtotal()) {
                    $props['discounts'][] = [
                        'amount' => $item->get_subtotal() - $item->get_total()
                    ];
                }
                $kickflipItems[] = $props;
            }

            if (!$kickflipItems) {
                continue;
            }

            $orderId = $order->get_id();
            $kickflipStatusData = $this->getMczrStatus($order->get_status());
            $kickflipOrders[] = [
                'eCommerceOrderId' => $orderId,
                'eCommerceOrderName' => "Order #$orderId",
                'store' => $this->settings->get('shopId'),
                'items' => $kickflipItems,
                'currency' => $order->get_currency(),
                'status' => $kickflipStatusData['status'],
                'paymentStatus' => $kickflipStatusData['paymentStatus'],
                'date' => $order->get_date_created()->date('Y-m-d H:i:s')
            ];
        }

        return $kickflipOrders;
    }

    public function onCheckoutOrderProcessed(int $order_id, array $_posted_data, WC_Order $_order): void
    {
        $this->onOrderCreated($order_id);
    }

    public function onPaymentComplete(int $order_id): void
    {
        $this->onOrderCreated($order_id);
    }

    public function onOrderCreated(int $orderId): void
    {
        $order = new WC_Order($orderId);
        $items = $order->get_items();
        $mczrItems = [];

        /**
         * Check if order includes at least one customizable item
         *
         * @var WC_Order_Item_Product $item
         */
        foreach ($items as $item) {
            $metas = $item->get_meta_data();
            $productId = $item->get_data()['product_id'];

            if (!$this->isMczrProductById($productId)) {
                continue;
            }

            $metasMczr = $this->extractMeta($metas, 'mczr', '*');

            if (!isset($metasMczr['designId'])) {
                continue;
            }

            $props = [
                'design' => $metasMczr['designId'],
                'quantity' => $item->get_data()['quantity'],
                'eCommerceItemId' => $item->get_data()['id'],
            ];

            if ($item->get_subtotal() != $item->get_total() && $item->get_total() < $item->get_subtotal()) {
                $props['discounts'][] = [
                    'amount' => $item->get_subtotal() - $item->get_total()
                ];
            }
            $mczrItems[] = $props;
        }

        // No mczr product, webhook will not fired
        if (count($mczrItems) <= 0) {
            return;
        }

        // Fire webhook
        $path = "/brands/{$this->settings->get('brand')}/orders";
        $content = [
            'eCommerceOrderId' => $orderId,
            'eCommerceOrderName' => "Order #$orderId",
            'store' => $this->settings->get('shopId'),
            'items' => $mczrItems,
            'currency' => $order->get_currency(),
            'date' => $order->get_date_created()->date('Y-m-d H:i:s'),
        ];

        $this->mczr->post($path, $content);
    }

    public function onOrderStatusChanged(int $orderId, string $_old_status, string $newStatus): void
    {
        $order = new WC_Order($orderId);
        $items = $order->get_items();
        $mczrItems = [];

        /**
         * Check if order includes at least one customizable item
         *
         * @var WC_Order_Item_Product $item
         */
        foreach ($items as $item) {
            $metas = $item->get_meta_data();
            $productId = $item->get_data()['product_id'];

            if (!$this->isMczrProductById($productId)) {
                continue;
            }

            $metasMczr = $this->extractMeta($metas, 'mczr', '*');

            if (!isset($metasMczr['designId'])) {
                continue;
            }

            $props = [
                'design' => $metasMczr['designId'],
                'quantity' => $item->get_data()['quantity'],
                'eCommerceItemId' => $item->get_data()['id'],
            ];
            if ($item->get_subtotal() != $item->get_total() && $item->get_total() < $item->get_subtotal()) {
                $props['discounts'][] = [
                    'amount' => $item->get_subtotal() - $item->get_total()
                ];
            }
            $mczrItems[] = $props;
        }

        // No mczr product, webhook will not fire
        if (count($mczrItems) <= 0) {
            return;
        }

        $path = "/brands/{$this->settings->get('brand')}/orders/update";
        $content = [
            'payload' => $this->getMczrStatus($newStatus),
            'query' => [
                'eCommerceOrderId' => $orderId,
                'store' => $this->settings->get('shopId'),
            ],
        ];
        $content['payload']['items'] = $mczrItems;

        $this->mczr->post($path, $content);
    }

    /**
     * @throws Exception
     */
    public function onOrderDeleted(int $orderId): void
    {
        $content['payload'] = [
            'status' => 'cancelled'
        ];

        $content['query'] = [
            'eCommerceOrderId' => $orderId,
            'store' => $this->settings->get('shopId'),
            'status' => 'pending'
        ];

        try {
            $path = "/brands/{$this->settings->get('brand')}/orders/update";
            $this->mczr->post($path, $content);
        } catch (Exception $e) {
            throw new Exception('Failed to update order status');
        }
    }

    public function onUpdateOrderItem(int $_item_id, WC_Order_Item $_order_item, int $order_id): void
    {
        $order = new WC_Order($order_id);
        $items = $order->get_items();
        $mczrItems = [];

        /**
         * Check if order includes at least one customizable item
         *
         * @var WC_Order_Item_Product $item
         */
        foreach ($items as $item) {
            $metas = $item->get_meta_data();
            $productId = $item->get_data()['product_id'];

            if (!$this->isMczrProductById($productId)) {
                continue;
            }

            $metasMczr = $this->extractMeta($metas, 'mczr', '*');

            if (!isset($metasMczr['designId'])) {
                continue;
            }

            $props = [
                'design' => $metasMczr['designId'],
                'quantity' => $item->get_data()['quantity'],
                'eCommerceItemId' => $item->get_data()['id'],
            ];
            if ($item->get_subtotal() != $item->get_total() && $item->get_total() < $item->get_subtotal()) {
                $props['discounts'][] = [
                    'amount' => $item->get_subtotal() - $item->get_total()
                ];
            }
            $mczrItems[] = $props;
        }

        // No mczr product, webhook will not fire
        if (count($mczrItems) <= 0) {
            return;
        }

        $path = "/brands/{$this->settings->get('brand')}/orders/update";
        $content = [
            'payload' => [
                'items' => $mczrItems,
            ],
            'query' => [
                'eCommerceOrderId' => $order_id,
                'store' => $this->settings->get('shopId'),
            ],
        ];

        $this->mczr->post($path, $content);
    }

    private function getMczrStatus(string $shopOrderStatus): array
    {
        switch ($shopOrderStatus) {
            case 'on-hold':
            case 'pending':
            case 'failed':
                return ['status' => 'pending', 'paymentStatus' => 'unpaid'];
            case 'processing':
                return ['status' => 'pending', 'paymentStatus' => 'paid'];
            case 'cancelled':
                return ['status' => 'cancelled'];
            case 'completed':
                return ['status' => 'fulfilled', 'paymentStatus' => 'paid'];
            case 'refunded':
                return ['status' => 'cancelled', 'paymentStatus' => 'refunded'];
            default:
                return ['status' => 'pending', 'paymentStatus' => 'unpaid'];
        }
    }

    public function displayMczrOrderItems(int $item_id, WC_Order_Item $item, WC_Order $_order): void
    {
        if ($item->is_type('line_item')) {
            $mczrMetas = \wc_get_order_item_meta($item_id, 'mczr');

            if (isset($mczrMetas) && !empty($mczrMetas)) {
                if (isset($mczrMetas['image'])) {
                    print '<div><img src="' . $mczrMetas['image'] . '" width="250px" height="auto"/></div>';
                }
                if (isset($mczrMetas['summary_v2'])) {
                    foreach ($mczrMetas['summary_v2'] as $mczrMetaId => $mczrMeta) {
                        $value = is_array($mczrMetas['summary_v2'][$mczrMetaId]['value']) ? implode(', ', $mczrMetas['summary_v2'][$mczrMetaId]['value']) : $mczrMetas['summary_v2'][$mczrMetaId]['value'];
                        print '<div>' . \__("{$mczrMetas['summary_v2'][$mczrMetaId]['key']}: {$value}", 'woocommerce') . '</div>';
                    }
                }
            }
        }
    }

    /**
     * @param mixed $data
     * 
     * @throws Exception
     */
    public function createOrder($data): array
    {
        // Fetch store products
        $storeProducts = get_posts([
            'post_type' => 'product',
            'posts_per_page' => -1,
        ]);

        if (empty($storeProducts)) {
            throw new Exception('No products found in the store.');
        }

        // Create a new order
        $createdOrder = \wc_create_order();
        if (is_wp_error($createdOrder)) {
            throw new Exception('Order creation failed: ' . $createdOrder->get_error_message());
        }

        $createdOrder->set_order_key($data['orderId']);
        $createdOrder->set_address([
            'first_name' => $data['customer']['name'],
            'email' => $data['customer']['email'],
        ], 'billing');

        $mczrItems = [];

        foreach ($data['items'] as $item) {
            // Find the product by name
            $product = array_filter($storeProducts, function ($product) use ($item) {
                return $product->post_title === $item['design']['productName'];
            });

            if (!empty($product)) {
                $product = reset($product);
                $productId = $product->ID;

                $variationDatas = [
                    'attributes' => [],
                    'sku' => 'mczr_' . uniqid(),
                    'regular_price' => $item['design']['price'],
                    'sale_price' => $item['design']['price'],
                    'stock_qty' => 99999,
                    'meta_data' => [
                        'key' => 'mczr',
                    ],
                ];

                // Create product variation
                $variationId = $this->mczrCartController->createVariation($productId, $variationDatas);
                if (!$variationId) {
                    throw new Exception('Failed to create product variation for ' . $item['design']['productName']);
                }

                // Add product variation to order
                $productVariation = \wc_get_product($variationId);
                $createdOrder->add_product($productVariation, 1);

                $summary = $item['design']['summary'];

                if ($item['design']['customExtraPrices']) {
                    foreach ($item['design']['customExtraPrices'] as $index => $extraPrice) {
                        $summary[] = [
                            'key' => 'Custom detail #' . ($index + 1),
                            'value' => $extraPrice['description']
                        ];
                    }
                }

                // Prepare meta data for order item, to display in admin order panel
                $mczrMetas = [
                    'shopProductId' => $productId,
                    'mczrStoreId' => $item['design']['store'],
                    'designId' => $item['design']['id'],
                    'image' => $item['design']['designImage']['url'],
                    'title' => $item['design']['productName'],
                    'customizationNumber' => $item['design']['customizerProductId'],
                    'summary_v2' => $summary,
                    'price' => $item['design']['price'],
                    'originalStartingPoint' => get_post_meta($productId, 'mczrStartingPoint', true),
                    'startingPoint' => $item['design']['id'],
                ];

                // Update order item meta data
                $orderItems = $createdOrder->get_items();
                if (!empty($orderItems)) {
                    $orderItem = reset($orderItems);
                    $orderItem->update_meta_data('mczr', $mczrMetas);
                }

                $createdOrder->save();

                // Store data for further processing
                $mczrItems[] = [
                    'design' => $item['design']['id'],
                    'quantity' => $item['quantity'],
                    'eCommerceItemId' => $createdOrder->get_id(),
                ];
            }
        }

        $createdOrder->calculate_totals();
        $createdOrder->save();

        $orderId = $createdOrder->get_id();

        if ($orderId <= 0) {
            throw new Exception('Order ID is invalid.');
        }

        $content = [
            'eCommerceOrderId' => $orderId,
            'eCommerceOrderName' => "Order #$orderId",
            'store' => $this->settings->get('shopId'),
            'items' => $mczrItems,
            'totalPrice' => $createdOrder->get_total(),
            'currency' => $createdOrder->get_currency(),
            'date' => $createdOrder->get_date_created()->date('Y-m-d H:i:s'),
        ];

        try {
            $path = "/brands/{$this->settings->get('brand')}/orders";
            $this->mczr->post($path, $content);

            return [
                'url' => \admin_url('post.php?post=' . $orderId . '&action=edit'),
                'orderId' => $orderId,
            ];
        } catch (Exception $e) {
            throw new Exception('Failed to create order for ' . $orderId);
        }
    }
}
