<?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\Model\LogType;
use Easymailing\App\Core\Provider\Woocommerce\Model\SynchronizationStatus;
use Easymailing\App\Core\Provider\Woocommerce\Service\MetadataHelper;
use Easymailing\App\Core\Transient\FlashBag;
use Easymailing\App\Model\Product;
use Easymailing\App\Repository\ConfigRepository;
use Easymailing\App\Utils\Api;

class ProductHooks
{
	private $config;
	private $apiClient;
	private $categoryHooks;
	private $flashBag;
	private $logManager;

	public function __construct(ConfigRepository $configRepository, Client $apiClient, CategoryHooks $categoryHooks, FlashBag $flashBag, LogManager $logManager)
	{
		$this->config        = $configRepository->getSynchronizationConfig();
		$this->apiClient     = $apiClient;
		$this->categoryHooks = $categoryHooks;
		$this->flashBag      = $flashBag;
		$this->logManager    = $logManager;
	}

	public function synchronizeProductAdmin(int $postId)
	{
		try {
			$this->synchronizeProduct($postId);
			$this->flashBag->add('success', __("Producto sincronizado en Easymailing correctamente", 'easymailing'));
		}catch(\Exception $e){
			$this->logManager->createLog(LogType::ERROR, sprintf("Error in synchronize product: %s", $e->getMessage()));
			$this->flashBag->add('error', __("Ocurrió un error al sincronizar el producto", 'easymailing'));
		}
	}

	public function productSaved($postId)
	{
		$status = get_post_status($postId);
		if ($status === 'trash') {
			return;
		}

		try {
			$this->synchronizeProduct($postId);
		} catch (\Exception $e) {
			$this->logManager->createLog(LogType::ERROR, sprintf("Error in synchronize product: %s", $e->getMessage()));
		}
	}

	public function synchronizeProduct(int $postId)
	{
		if(get_post_type($postId) != 'product'){
			return;
		}

		if(get_post_type($postId) === "product_variation"){
			return;
		}

		try {
			$isCreated = get_post_meta($postId, MetadataHelper::isResourceCreated(), true);
			$post           = get_post($postId);

			if(!$isCreated){
				$this->handleNewProduct($postId, $post);
				return;
			}

			$this->handleUpdateProduct($postId, $post);

		}catch(\Exception $e){
			$this->logManager->createLog(LogType::ERROR, sprintf("Error in synchronize product: %s", $e->getMessage()));
		}
	}

	private function handleNewProduct($postId, $post): void
	{
		try{
			$product     = wc_get_product($postId);
			$emProductId = wp_generate_uuid4();
			update_post_meta($postId, MetadataHelper::getResourceId(), $emProductId);

			$productResource = $this->createProduct($postId, $post, $emProductId);
			if($product->is_type('variable')){
				$this->handleVariableProduct($product, $emProductId);
			}else{
				$this->createVariationSimple($postId, $post, $emProductId);
			}
			update_post_meta($postId, MetadataHelper::getSynchronizationStatus(), 'success');
			update_post_meta($postId, MetadataHelper::isResourceCreated(), true);
			update_post_meta($postId, MetadataHelper::getResourceId(), $productResource->getResourceId());
		}catch(\Exception $e){
			update_post_meta($postId, MetadataHelper::getSynchronizationStatus(), 'error');
			$this->logManager->createLog(LogType::ERROR, sprintf("Error in create product: %s", $e->getMessage()));
			throw new \Exception($e);
		}
	}

	private function handleUpdateProduct($postId, $post): void
	{
		try{
			$product     = wc_get_product($postId);
			$emProductId = get_post_meta($postId, MetadataHelper::getResourceId(), true);

			$this->updateProduct($postId, $post, $emProductId);

			if($product->is_type('variable')){
				$this->handleVariableProductUpdate($product, $emProductId);
			}else{
				$this->updateSimpleVariation($postId, $post, $emProductId);
			}
			update_post_meta($postId, MetadataHelper::getSynchronizationStatus(), 'success');

		}catch(\Exception $e){
			update_post_meta($postId, MetadataHelper::getSynchronizationStatus(), 'error');
			$this->logManager->createLog(LogType::ERROR, sprintf("Error in update product: %s", $e->getMessage()));
			throw new \Exception($e);
		}
	}

	private function handleVariableProduct($product, $emProductId): void
	{
		$variations = $product->get_available_variations();
		foreach($variations as $variation){
			$this->createVariation($variation, $emProductId);
		}
	}

	private function handleVariableProductUpdate($product, $emProductId): void
	{
		try{
			$productIri     = "/stores" . "/" . $this->config->getShopId() . "/products" . "/" . $emProductId;
			$client         = $this->apiClient;
			$emProduct      = $client->getProduct($productIri);
			$emVariationIds = $this->getEmProductVariants($emProduct->variants);
		}catch(\Exception $e){
			throw new \Exception($e);
		}

		$variations = $product->get_available_variations();

		foreach($variations as $variation){
			$variationId   = $variation['variation_id'];
			$emVariationId = get_post_meta($variationId, MetadataHelper::getResourceId(), true);

			if(in_array($emVariationId, $emVariationIds)){
				$this->updateVariation($variation, $emProductId, $emVariationId);
			}else{
				$this->createVariation($variation, $emProductId);
			}
		}
	}

	private function updateSimpleVariation($postId, $post, $emProductId): void
	{
		$emVariationId = get_post_meta($postId, MetadataHelper::getResourceId(), true);
		if( ! $emVariationId){
			$this->createVariationSimple($postId, $post, $emProductId);
		}else{
			$this->updateVariationSimple($postId, $post, $emProductId, $emVariationId);
		}
	}

	public function createProduct($postId, $post, $emProductId): Product
	{
		try{
			$client = $this->apiClient;
			$url    = "/stores" . "/" . $this->config->getShopId() . "/products";

			$product = wc_get_product($postId);
			$imageId = $product->get_image_id();

			$brands = wp_get_post_terms($product->get_id(), 'product_brand', ['fields' => 'names']);
			$vendor = !empty($brands) ? $brands[0] : null;

			$imageUrL = $imageId ? wp_get_attachment_image_url($imageId, 'full') : null;

			$body    = [
				'resource_id' => $emProductId,
				'title'       => $post->post_title,
				'description' => $post->post_content,
				'url'         => get_permalink($postId),
				'vendor'         => $vendor,
				'image_url'          => is_string($imageUrL) ? $imageUrL : null,
				'categories'  => $this->getProductCategoryIds($postId),
			];
			return $client->createProduct($url, $body);
		}catch(\Exception $e){
			$this->logManager->createLog(LogType::ERROR, sprintf("Error in create product: %s", $e->getMessage()));
			throw new \Exception($e);
		}
	}

	public function updateProduct($postId, $post, $emProductId): void
	{
		try{
			$client = $this->apiClient;
			$url    = "/stores" . "/" . $this->config->getShopId() . "/products" . "/" . $emProductId;

			$product = wc_get_product($postId);
			$imageId = $product->get_image_id();

			$brands = wp_get_post_terms($product->get_id(), 'product_brand', ['fields' => 'names']);
			$vendor = !empty($brands) ? $brands[0] : null;

			$imageUrL = $imageId ? wp_get_attachment_image_url($imageId, 'full') : null;

			$body = [
				'title'       => $post->post_title,
				'description' => $post->post_content,
				'url'         => get_permalink($postId),
				'vendor'         => $vendor,
				'image_url'          => is_string($imageUrL) ? $imageUrL : null,
				'categories'  => $this->getProductCategoryIds($postId),
			];

			$client->updateProduct($url, $body);
		}catch(NotFoundException $e){
			update_post_meta($postId, MetadataHelper::getSynchronizationStatus(), 'error');
			update_post_meta($postId, MetadataHelper::getResourceId(), null);
			update_post_meta($postId, MetadataHelper::isResourceCreated(), false);
			$this->logManager->createLog(LogType::ERROR, "Error in update product: Not found");
			throw new \Exception($e);
		}catch(\Exception $e){
			$this->logManager->createLog(LogType::ERROR, sprintf("Error in update product: %s", $e->getMessage()));
			throw new \Exception($e);
		}
	}

	public function createVariationSimple($postId, $post, $emProductId): void
	{
		try{
			$product       = wc_get_product($postId);
			$imageId       = $product->get_image_id();
			$client        = $this->apiClient;
			$emVariationId = wp_generate_uuid4();
			$variationIri  = "/stores" . "/" . $this->config->getShopId() . "/products" . "/" . $emProductId . "/variants";

			$imageUrL = $imageId ? wp_get_attachment_image_url($imageId, 'full') : null;

			$body          = [
				'resource_id'        => $emVariationId,
				'title'              => $post->post_title,
				'sku'                => $product->get_sku(),
				'price'              => Api::convertEurToCents($product->get_regular_price()),
				'inventory_quantity' => intval($product->get_stock_quantity()),
				'url'                => get_permalink($postId),
				'image_url'          => is_string($imageUrL) ? $imageUrL : null,
			];
			$client->createVariation($variationIri, $body);
			update_post_meta($postId, MetadataHelper::getVariantResourceId(), $emVariationId);
		}catch(\Exception $e){

			$this->logManager->createLog(LogType::ERROR, sprintf("Error in create variation simple: %s", $e->getMessage()));
			throw new \Exception($e);
		}
	}

	public function updateVariationSimple($postId, $post, $emProductId, $emVariationId): void
	{
		try{
			$product      = wc_get_product($postId);
			$imageId      = $product->get_image_id();
			$client       = $this->apiClient;
			$variationIri = "/stores" . "/" . $this->config->getShopId() . "/products" . "/" . $emProductId . "/variants" . "/" . $emVariationId;

			$imageUrL = $imageId ? wp_get_attachment_image_url($imageId, 'full') : null;

			$body = [
				'title'              => $post->post_title,
				'sku'                => $product->get_sku(),
				'price'              => Api::convertEurToCents($product->get_regular_price()),
				'inventory_quantity' => intval($product->get_stock_quantity()),
				'url'                => get_permalink($postId),
				'image_url'          => is_string($imageUrL) ? $imageUrL : null,
			];
			$client->updateVariation($variationIri, $body);
			update_post_meta($postId, MetadataHelper::getVariantResourceId(), $emVariationId);

		}catch(\Exception $e){
			if($e instanceof NotFoundException){
				$this->createVariationSimple($postId, $post, $emProductId);
			}else{
				$this->logManager->createLog(LogType::ERROR, sprintf("Error in update variation simple: %s", $e->getMessage()));
				throw new \Exception($e);
			}
		}
	}

	public function createVariation($variation, $emProductId): void
	{
		try{
			$client        = $this->apiClient;
			$emVariationId = wp_generate_uuid4();
			$variationIri  = "/stores" . "/" . $this->config->getShopId() . "/products" . "/" . $emProductId . "/variants";

			$body          = [
				'resource_id'        => $emVariationId,
				'title'              => get_the_title($variation['variation_id']),
				'sku'                => $variation['sku'],
				'price'              => Api::convertEurToCents($variation['display_price']),
				'inventory_quantity' => intval($variation["max_qty"]),
				'url'                => get_permalink($variation['variation_id']),
				'image_url'          => $variation['image']['url'] ? $variation['image']['url'] : null,
			];
			$client->createVariation($variationIri, $body);
			update_post_meta($variation['variation_id'], MetadataHelper::getResourceId(), $emVariationId);
			update_post_meta($variation['variation_id'], MetadataHelper::isResourceCreated(), true);
			update_post_meta($variation['variation_id'], MetadataHelper::getSynchronizationStatus(), SynchronizationStatus::SUCCESS);
		}catch(\Exception $e){
			$this->logManager->createLog(LogType::ERROR, sprintf("Error in create variation: %s", $e->getMessage()));
			throw new \Exception($e);
		}
	}

	public function updateVariation($variation, $emProductId, $emVariationId): void
	{
		try{
			$client       = $this->apiClient;
			$variationIri = "/stores" . "/" . $this->config->getShopId() . "/products" . "/" . $emProductId . "/variants" . "/" . $emVariationId;
			//$imageId = $variation->get_image_id();
			$body = [
				'title'              => get_the_title($variation['variation_id']),
				'sku'                => $variation['sku'],
				'price'              => Api::convertEurToCents($variation['display_price']),
				'inventory_quantity' => intval($variation["max_qty"]),
				'url'                => get_permalink($variation['variation_id']),
				'image_url'          => $variation['image']['url'] ? $variation['image']['url'] : null,
			];

			$client->updateVariation($variationIri, $body);
			update_post_meta($variation['variation_id'], MetadataHelper::getResourceId(), $emVariationId);
		}catch(\Exception $e){
			if($e instanceof NotFoundException){
				$this->createVariation($variation, $emProductId);
			}else{
				$this->logManager->createLog(LogType::ERROR, sprintf("Error in update variation: %s", $e->getMessage()));
				throw new \Exception($e);
			}
		}
	}

	public function getEmProductVariants($variants): array
	{
		$variantIds = [];
		foreach($variants as $variant){
			$variantIds[] = $variant->resourceId;
		}

		return $variantIds;
	}

	private function getProductCategoryIds($postId): array
	{
		$wpProductCategories     = get_the_terms($postId, 'product_cat');
		$productCategoriesToSave = [];

		if( ! is_wp_error($wpProductCategories) && ! empty($wpProductCategories)){
			$productCategoriesToSave = array_map(function ($category){
				return $this->categoryHooks->updateCategory($category->term_id);
			}, $wpProductCategories);
		}

		return array_map(function ($category){
			return $category->id;
		}, $productCategoriesToSave);

	}
}
