<?php

namespace Easymailing\App\Core\Provider\Woocommerce\Hooks;

use Easymailing\App\Api\Client;
use Easymailing\App\Api\Exception\NotFoundException;
use Easymailing\App\Core\Log\LogManager;
use Easymailing\App\Core\Provider\Woocommerce\Exception\CustomerNotFoundException;
use Easymailing\App\Core\Provider\Woocommerce\Exception\ProductOrVariantNotFoundException;
use Easymailing\App\Core\Provider\Woocommerce\Model\LogType;
use Easymailing\App\Core\Provider\Woocommerce\Service\CustomerService;
use Easymailing\App\Core\Provider\Woocommerce\Service\MetadataHelper;
use Easymailing\App\Core\Provider\Woocommerce\Service\OrderStatusHandler;
use Easymailing\App\Core\Provider\Woocommerce\Service\OrderValidator;
use Easymailing\App\Core\Provider\Woocommerce\Service\PhoneCountryResolver;
use Easymailing\App\Core\Transient\FlashBag;
use Easymailing\App\Model\Customer;
use Easymailing\App\Model\Order;
use Easymailing\App\Repository\ConfigRepository;
use Easymailing\App\Utils\Api;
use Easymailing\App\Utils\CustomerPayload;

class OrderHooks
{
    private $config;
    private $apiClient;
    private $productHooks;
    private $customerService;
    private $orderStatusHandler;
    private $orderValidator;
    private $flashBag;
    private $logManager;

    public function __construct(
		ConfigRepository $configRepository,
		Client $apiClient,
		ProductHooks $productHooks,
		CustomerService $customerService,
		OrderStatusHandler $orderStatusHandler,
		OrderValidator $orderValidator,
		FlashBag $flashBag,
		LogManager $logManager
    )
    {
        $this->config = $configRepository->getSynchronizationConfig();
        $this->apiClient = $apiClient;
        $this->productHooks = $productHooks;
        $this->customerService = $customerService;
        $this->orderStatusHandler = $orderStatusHandler;
        $this->orderValidator = $orderValidator;
        $this->flashBag = $flashBag;
        $this->logManager = $logManager;
    }



	public function synchronizeOrderAdmin(int $orderId): void
    {
        try {
			$orderResource = $this->handleOrder($orderId);
	        $this->orderStatusHandler->synchronizeOrderStatus($orderResource, wc_get_order($orderId));
	        $this->flashBag->add('success', __("Pedido sincronizado en Easymailing correctamente", 'easymailing'));
        }catch (ProductOrVariantNotFoundException $e) {
	        // Manejo específico de errores de productos o variantes no encontrados
	        $this->logManager->createLog(
		        LogType::WARNING,
		        sprintf(
			        "Synchronization failed for Order ID: %s. %s",
			        $e->getOrderId(),
			        $e->getMessage()
		        )
	        );
	        $this->flashBag->add(
		        'error',
		        __("No se pudo sincronizar el pedido porque algunos productos o variantes no existen en WooCommerce.", 'easymailing')
	        );
        } catch (\Exception $e) {
	        $this->logManager->createLog(LogType::ERROR, sprintf("Error in synchronize order: %s", $e->getMessage()));
			$message = __("Ocurrió un error al sincronizar el pedido", 'easymailing');
			if ($e->getMessage() !== '') {
				$message .= ': ' . $e->getMessage();
			}
	        $this->flashBag->add('error', $message);
        }
    }


	public function orderCreated(\WC_Order $order)
	{
		try {
			$this->handleOrder($order->get_id());
		} catch (\Exception $e) {

			$this->logManager->createLog(LogType::ERROR, sprintf("Error in synchronize order: %s", $e->getMessage()));
		}
	}

	public function synchronizeStatus(int $order_id, ?string $old_status, ?string $new_status, \WC_Order $order)
	{
		$resourceId = get_post_meta($order_id, MetadataHelper::getResourceId(), true);
		$this->logManager->createLog(LogType::INFO, sprintf("Synchronize Status. Old status: %s, new status: %s, resourceId: %s", $old_status, $new_status, $resourceId));

		if(!$resourceId){
			$this->logManager->createLog(LogType::INFO, sprintf("Can not synchronize status, resourceId not found: %s", $order_id));
			return;
		}

		try {
			$iri = sprintf("/stores/%s/orders/%s", $this->config->getShopId(), $resourceId);
			$orderResource = $this->apiClient->getOrder($iri);
		}catch(NotFoundException $e){
			$this->logManager->createLog(LogType::ERROR, sprintf("Can not synchronize status, order not found: %s", $order_id));
			return;
		}catch(\Exception $e){
			$this->logManager->createLog(LogType::ERROR, sprintf("Can not synchronize status, Error: %s", $e->getMessage()));
			return;
		}

		try {
			$this->orderStatusHandler->synchronizeOrderStatus($orderResource, $order);
		}catch(\Exception $e){
			$this->logManager->createLog(LogType::ERROR, sprintf("Can not synchronize status, Error: %s", $e->getMessage()));
		}
	}


    public function handleOrder(int $orderId): Order
    {
		$this->logManager->createLog(LogType::INFO, sprintf("Handle order creation: %s", $orderId));

        $order = wc_get_order($orderId);

	    try {
		    $this->orderValidator->validateOrder($order);
	    } catch (ProductOrVariantNotFoundException $e) {
		    $this->logManager->createLog(
			    LogType::ERROR,
			    sprintf(
				    "Order ID: %s not synced. %s (Product ID: %s, Variant ID: %s).",
				    $e->getOrderId(),
				    $e->getMessage(),
				    $e->getProductId() ?? 'N/A',
				    $e->getVariantId() ?? 'N/A'
			    )
		    );
		    throw $e;
	    }catch (CustomerNotFoundException $e) {
		    $this->logManager->createLog(
			    LogType::WARNING,
			    sprintf(
				    "Order ID: %s not synced. The associated customer (ID: %s) has been deleted from WordPress.",
				    $e->getOrderId(),
				    $e->getCustomerId()
			    )
		    );
		    throw $e;
	    }



	    $userId = $order->get_user_id();
        $customerResourceId = null;

		if($userId !== 0 && $userId){
			$customerResourceId = get_user_meta($userId, MetadataHelper::getResourceId(), true);
		}

        if ($customerResourceId) {
			$this->logManager->createLog(LogType::INFO, sprintf("updateCustomerInEasyMailing -  Customer resource ID: %s", $customerResourceId));
			$this->updateCustomerInEasyMailing($order, $customerResourceId);
			update_post_meta($orderId, MetadataHelper::getCustomerResourceId(), $customerResourceId);
			update_post_meta($userId, MetadataHelper::getResourceId(), $customerResourceId);
	        $this->logManager->createLog(LogType::INFO, sprintf("update_post_meta user -  Customer resource ID: %s", $customerResourceId));
		}else{
			$customer = $this->apiClient->findCustomerByIdentity(
				sprintf("/stores/%s/customers", $this->config->getShopId()),
				$order->get_billing_email(),
				CustomerPayload::normalizePhone($order->get_billing_phone(), PhoneCountryResolver::fromOrder($order))
			);
			if(!$customer) {
				$customerResource = $this->createCustomerInEasyMailing($order);
				$customerResourceId = $customerResource->getResourceId();
			}else{
				$customerResourceId = $customer->getResourceId();
				$this->updateCustomerInEasyMailing($order, $customerResourceId);
			}

	        update_post_meta($orderId, MetadataHelper::getCustomerResourceId(), $customerResourceId);
			$this->logManager->createLog(LogType::INFO, sprintf("update_post_meta guest -  Customer resource ID: %s", $customerResourceId));

	        if($userId !== 0 && $userId){
		        update_post_meta($userId, MetadataHelper::getCustomerResourceId(), $customerResourceId);
	        }
        }

        $emSessionOrderId = null;
	    $hash = null;
        if ( isset(WC()->session) ) {
	        $emSessionOrderId = WC()->session->get(CartHooks::SESSION_ORDER_RESOURCE_ID_NAME);
	        $hash = WC()->session->get(CartHooks::SESSION_HASH_NAME);
        }

		if(!$hash){
			$hash = sanitize_text_field($_COOKIE[CampaignHashHook::CAMPAIGN_HASH_COOKIE]);
		}

		$this->logManager->createLog(LogType::INFO, sprintf("emSessionOrderId: %s", $emSessionOrderId));
		$this->logManager->createLog(LogType::INFO, sprintf("hash: %s", $hash));

        $orderResourceId = $emSessionOrderId ?? get_post_meta($orderId, MetadataHelper::getResourceId(), true);

	    $this->logManager->createLog(LogType::INFO, sprintf("orderResourceId: %s", get_post_meta($orderId, MetadataHelper::getResourceId(), true)));

        $orderCurrency = $order->get_currency();
        $orderTotal = $order->get_total();
        $checkoutUrl = $order->get_view_order_url();
		$customer = sprintf("/stores/%s/customers/%s", $this->config->getShopId(), $customerResourceId);

		if($orderResourceId){
			try {
				$iri = sprintf("/stores/%s/orders/%s", $this->config->getShopId(), $orderResourceId);
				$this->apiClient->getOrder($iri);
				$body = $this->buildOrderBody(null, $orderCurrency, $orderTotal, $checkoutUrl, $order, $customer, $hash);
				$orderResource = $this->apiClient->updateOrder($iri, $body);

			}catch(\Exception $e){
				$url = sprintf("/stores/%s/orders", $this->config->getShopId());
				$body = $this->buildOrderBody($orderResourceId, $orderCurrency, $orderTotal, $checkoutUrl, $order, $customer, $hash);
				$orderResource = $this->apiClient->createOrder($url, $body);
				update_post_meta($orderId, MetadataHelper::isResourceCreated(), true);
			}

		}else{
			$orderResourceId = wp_generate_uuid4();
			$url = sprintf("/stores/%s/orders", $this->config->getShopId());
			$body = $this->buildOrderBody($orderResourceId, $orderCurrency, $orderTotal, $checkoutUrl, $order, $customer, $hash);
			$orderResource = $this->apiClient->createOrder($url, $body);
			update_post_meta($orderId, MetadataHelper::isResourceCreated(), true);
		}

        update_post_meta($orderId, MetadataHelper::getResourceId(), $orderResource->getResourceId());
        update_post_meta($orderId, MetadataHelper::getCampaignHash(), $hash);

        WC()->session->__unset(CartHooks::SESSION_ORDER_RESOURCE_ID_NAME);
        WC()->session->__unset(CartHooks::SESSION_HASH_NAME);

        update_post_meta($orderId, MetadataHelper::getSynchronizationStatus(), "success");

		return $orderResource;

    }


	private function getOrderItems(\WC_Order $order): array
	{
		$wcOrderItems = $order->get_items();
		$items = [];

		foreach ($wcOrderItems as $item) {

			$productId = $item->get_product_id();
			$variationId = $item->get_variation_id();

			$emProductId = get_post_meta($productId, MetadataHelper::getResourceId(), true);

			try{
				$productIri = "/stores" . "/" . $this->config->getShopId() . "/products" . "/" . $emProductId;
				$this->apiClient->getProduct($productIri);
			}catch(\Exception $e){
				if ($e instanceof NotFoundException) {
					$this->logManager->createLog(LogType::INFO, sprintf("Error Product not found: %s", $e->getMessage()));
					$this->productHooks->synchronizeProduct($productId);
				} else{
					$this->logManager->createLog(LogType::INFO, sprintf("Error: %s", $e->getMessage()));
					throw new \Exception($e);
				}
			}

			$emVariantId = $variationId !== 0 ?
				get_post_meta($variationId, MetadataHelper::getResourceId(), true) :
				get_post_meta($productId, MetadataHelper::getVariantResourceId(), true);

			$items[] = [
				'quantity' => $item->get_quantity(),
				'price' => Api::convertEurToCents($item->get_total()),
				'variant' => sprintf("/stores/%s/products/%s/variants/%s", $this->config->getShopId(), $emProductId, $emVariantId),
			];
		}

		return $items;
	}

    private function createCustomerInEasyMailing($order): Customer
    {
		$body = $this->customerService->createCustomerFromOrderBody($order);
		$url = sprintf("/stores/%s/customers", $this->config->getShopId());
        return $this->apiClient->createCustomer($url, $body, CustomerPayload::optInQuery());
    }

    private function updateCustomerInEasyMailing($order, $customerResourceId): Customer
    {
		$body = $this->customerService->updateCustomerFromOrderBody($order);
		$url = sprintf("/stores/%s/customers/%s", $this->config->getShopId(), $customerResourceId);
        return $this->apiClient->updateCustomer($url, $body, CustomerPayload::optInQuery());
    }


    private function buildOrderBody(
        ?string $resourceId,
        string $currency,
        float $orderTotal,
        string $checkoutUrl,
        $order,
        string $customer,
	    ?string $campaignHash = null
    ): array {

        $body = [
            'currency' => $currency,
            'order_total' => Api::convertEurToCents($orderTotal),
            'checkout_url' => $checkoutUrl,
            'order_items' => $this->getOrderItems($order),
            'order_number' => $order->get_order_number(),
            'customer' => $customer,
        ];

	    if($campaignHash){
		    $url = sprintf("/campaigns?hash=%s", $campaignHash);
		    $campaign = $this->apiClient->getCampaignByHash($url);
		    if($campaign){
			    $body['campaign_hash'] = $campaignHash;
		    }
	    }

        if ($resourceId) {
            $body['resource_id'] = $resourceId;
        }

        return $body;
    }
}
