<?php

namespace ShopManagerPro\Products;

use ShopManagerPro\Jobs\DTO\Generated\JobProductsItemChanges;
use ShopManagerPro\Jobs\DTO\Generated\JobProductsItemChangesBasic;
use ShopManagerPro\Jobs\DTO\Generated\JobProductsItemChangesBasicPostStatus;
use ShopManagerPro\Jobs\DTO\Generated\JobProductsItemChangesBasicRegularPrice;
use ShopManagerPro\Jobs\DTO\Generated\JobProductsItemChangesBasicSalePrice;
use ShopManagerPro\Jobs\DTO\Generated\JobProductsItemChangesBasicStock;
use ShopManagerPro\Jobs\DTO\Generated\JobProductsItemChangesBasicStockStatus;
use ShopManagerPro\Jobs\DTO\Generated\JobProductsItemChangesBasicTaxClass;
use ShopManagerPro\Jobs\DTO\Generated\JobProductsItemChangesBasicTaxStatus;
use ShopManagerPro\Jobs\DTO\Generated\JobProductsItemErrorAlternative1;
use ShopManagerPro\Products\DTO\Generated\ProductBasic;
use ShopManagerPro\Products\DTO\Generated\ProductBulkUpdateActions;
use ShopManagerPro\Products\DTO\Generated\ProductBulkUpdateActionsSpecialRegularPriceMode;
use ShopManagerPro\Products\DTO\Generated\ProductBulkUpdateActionsSpecialSalePriceMode;
use ShopManagerPro\Products\DTO\ProductUpdateException;
use ShopManagerPro\Products\Fields\DTO\Basic\BasicFieldKeys;
use ShopManagerPro\Products\Fields\DTO\ProductFieldGroupType;
use ShopManagerPro\Shared\Utils\Strings;

class ProductChangeService {
	public static function applyRegularPriceChange(JobProductsItemChangesBasic $changes, ProductBulkUpdateActions $actions, ProductBasic $basic) {
		$basicActions = $actions->getBasic();
		$specialActions = $actions->getSpecial();
		if (!$specialActions || !$specialActions->getRegularPriceMode()) {
			return $changes;
		} $mode = $specialActions->getRegularPriceMode();
		$priceModification = match ($mode) {
			ProductBulkUpdateActionsSpecialRegularPriceMode::fixed => $basicActions ? $basicActions->getRegularPrice() : null, ProductBulkUpdateActionsSpecialRegularPriceMode::percentage => $specialActions->getRegularPricePercentage() ?: 0, ProductBulkUpdateActionsSpecialRegularPriceMode::absolute => $specialActions->getRegularPriceAbsolute() ?: 0, ProductBulkUpdateActionsSpecialRegularPriceMode::clear => null,
		};
		$referencePrice = match ($specialActions->getRegularPriceUseSaleAsReference()) {
			true => $basic->getSalePrice(), false,null => $basic->getRegularPrice(),
		};
		$newRegularPrice = ProductUpdateService::calculateNewPrice($referencePrice, $priceModification, $mode);

		return $changes->withRegularPrice(new JobProductsItemChangesBasicRegularPrice($basic->getRegularPrice(), $newRegularPrice));
	}

	public static function applySalePriceChange(JobProductsItemChangesBasic $changes, ProductBulkUpdateActions $actions, ProductBasic $basic) {
		$basicActions = $actions->getBasic();
		$specialActions = $actions->getSpecial();
		if (!$specialActions || !$specialActions->getSalePriceMode()) {
			return $changes;
		} $mode = $specialActions->getSalePriceMode();
		$priceModification = match ($mode) {
			ProductBulkUpdateActionsSpecialSalePriceMode::fixed => $basicActions ? $basicActions->getSalePrice() : null, ProductBulkUpdateActionsSpecialSalePriceMode::percentage => $specialActions->getSalePricePercentage() ?: 0, ProductBulkUpdateActionsSpecialSalePriceMode::absolute => $specialActions->getSalePriceAbsolute() ?: 0, ProductBulkUpdateActionsSpecialSalePriceMode::clear => null,
		};
		$referencePrice = match ($specialActions->getSalePriceUseRegularAsReference()) {
			true => $basic->getRegularPrice(), false,null => $basic->getSalePrice(),
		};
		$newSalePrice = ProductUpdateService::calculateNewPrice($referencePrice, $priceModification, $mode);

		return $changes->withSalePrice(new JobProductsItemChangesBasicSalePrice($basic->getSalePrice(), $newSalePrice));
	}

	public static function applyStockStatusChange(JobProductsItemChangesBasic $changes, ProductBulkUpdateActions $actions, ProductBasic $basic) {
		$basicActions = $actions->getBasic();
		if (!$basicActions || $basicActions->getStockStatus() === null) {
			return $changes;
		} $currentStockStatus = $basic->getStockStatus();
		$newStockStatus = $basicActions->getStockStatus();
		if ($newStockStatus === $currentStockStatus) {
			return $changes;
		} $change = new JobProductsItemChangesBasicStockStatus($currentStockStatus ? $currentStockStatus->value : null, $newStockStatus ? $newStockStatus->value : null);

		return $changes->withStockStatus($change);
	}

	public static function applyPostStatusChange(JobProductsItemChangesBasic $changes, ProductBulkUpdateActions $actions, ProductBasic $basic) {
		$basicActions = $actions->getBasic();
		if (!$basicActions || $basicActions->getPostStatus() === null) {
			return $changes;
		} $currentPostStatus = $basic->getPostStatus();
		$newPostStatus = $basicActions->getPostStatus();
		if ($newPostStatus->value === $currentPostStatus->value) {
			return $changes;
		} $change = JobProductsItemChangesBasicPostStatus::buildFromInput(['old' => $currentPostStatus->value, 'new' => $newPostStatus->value]);

		return $changes->withPostStatus($change);
	}

	public static function applyStockChange(JobProductsItemChangesBasic $changes, ProductBulkUpdateActions $actions, ProductBasic $basic) {
		$basicActions = $actions->getBasic();
		if (!$basicActions || $basicActions->getStock() === null) {
			return $changes;
		} $currentStock = $basic->getStock();
		$newStock = $basicActions->getStock();
		if ($newStock === $currentStock) {
			return $changes;
		} $change = new JobProductsItemChangesBasicStock($currentStock, $newStock);

		return $changes->withStock($change);
	}

	public static function applyTaxClassChange(JobProductsItemChangesBasic $changes, ProductBulkUpdateActions $actions, ProductBasic $basic) {
		$basicActions = $actions->getBasic();
		if (!$basicActions || $basicActions->getTaxClass() === null) {
			return $changes;
		} $currentTaxClass = $basic->getTaxClass();
		$newTaxClass = $basicActions->getTaxClass();
		if ($newTaxClass === $currentTaxClass) {
			return $changes;
		} $change = new JobProductsItemChangesBasicTaxClass($currentTaxClass, $newTaxClass);

		return $changes->withTaxClass($change);
	}

	public static function applyTaxStatusChange(JobProductsItemChangesBasic $changes, ProductBulkUpdateActions $actions, ProductBasic $basic) {
		$basicActions = $actions->getBasic();
		if (!$basicActions || $basicActions->getTaxStatus() === null) {
			return $changes;
		} $currentTaxStatus = $basic->getTaxStatus();
		$newTaxStatus = $basicActions->getTaxStatus();
		if ($newTaxStatus === $currentTaxStatus) {
			return $changes;
		} $change = new JobProductsItemChangesBasicTaxStatus($currentTaxStatus, $newTaxStatus);

		return $changes->withTaxStatus($change);
	}

	public static function validateRegularPrice(\WC_Product $product, JobProductsItemChanges $changes) {
		$basic = $changes->getBasic();
		if (!$basic || !$basic->getRegularPrice()) {
			return;
		} $expectedOld = $basic->getRegularPrice()->getOld();
		$actualValue = $product->get_regular_price();
		$actualValue = $actualValue === '' ? null : (float) $actualValue;
		if (!ProductUpdateService::valuesMatch($expectedOld, $actualValue)) {
			$expectedStr = $expectedOld === null ? 'null' : (string) $expectedOld;
			$actualStr = $actualValue === null ? 'null' : (string) $actualValue;
			throw new ProductUpdateException(new JobProductsItemErrorAlternative1(ProductFieldGroupType::BASIC->value.':'.BasicFieldKeys::REGULAR_PRICE->value, $expectedStr, $actualStr));
		}
	}

	public static function validateSalePrice(\WC_Product $product, JobProductsItemChanges $changes) {
		$basic = $changes->getBasic();
		if (!$basic || !$basic->getSalePrice()) {
			return;
		} $expectedOld = $basic->getSalePrice()->getOld();
		$actualValue = $product->get_sale_price();
		$actualValue = $actualValue === '' ? null : (float) $actualValue;
		if (!ProductUpdateService::valuesMatch($expectedOld, $actualValue)) {
			throw new ProductUpdateException(new JobProductsItemErrorAlternative1(ProductFieldGroupType::BASIC->value.':'.BasicFieldKeys::SALE_PRICE->value, $expectedOld, $actualValue));
		}
	}

	public static function validateStockStatus(\WC_Product $product, JobProductsItemChanges $changes) {
		$basic = $changes->getBasic();
		if (!$basic || !$basic->getStockStatus()) {
			return;
		} $expectedOld = Strings::backedEnumToString($basic->getStockStatus()->getOld());
		$actualValue = $product->get_stock_status();
		if (!ProductUpdateService::valuesMatch($expectedOld, $actualValue)) {
			$expectedStr = strval($expectedOld);
			$actualStr = strval($actualValue);
			throw new ProductUpdateException(new JobProductsItemErrorAlternative1(ProductFieldGroupType::BASIC->value.':'.BasicFieldKeys::STOCK_STATUS->value, $expectedStr, $actualStr));
		}
	}

	public static function validatePostStatus(\WC_Product $product, JobProductsItemChanges $changes) {
		$basic = $changes->getBasic();
		if (!$basic || !$basic->getPostStatus()) {
			return;
		} $expectedOld = Strings::backedEnumToString($basic->getPostStatus()->getOld());
		$actualValue = $product->get_status();
		if (!ProductUpdateService::valuesMatch($expectedOld, $actualValue)) {
			throw new ProductUpdateException(new JobProductsItemErrorAlternative1(ProductFieldGroupType::BASIC->value.':'.BasicFieldKeys::POST_STATUS->value, $expectedOld, $actualValue));
		}
	}

	public static function validateStock(\WC_Product $product, JobProductsItemChanges $changes) {
		$basic = $changes->getBasic();
		if (!$basic || !$basic->getStock()) {
			return;
		} $expectedOld = $basic->getStock()->getOld();
		$actualValue = $product->get_stock_quantity();
		if (!ProductUpdateService::valuesMatch($expectedOld, $actualValue)) {
			$expectedStr = $expectedOld === null ? 'null' : (string) $expectedOld;
			$actualStr = $actualValue === null ? 'null' : (string) $actualValue;
			throw new ProductUpdateException(new JobProductsItemErrorAlternative1(ProductFieldGroupType::BASIC->value.':'.BasicFieldKeys::STOCK_COUNT->value, $expectedStr, $actualStr));
		}
	}

	public static function validateTaxClass(\WC_Product $product, JobProductsItemChanges $changes) {
		$basic = $changes->getBasic();
		if (!$basic || !$basic->getTaxClass()) {
			return;
		} $expectedOld = $basic->getTaxClass()->getOld();
		$actualValue = $product->get_tax_class();
		if (!ProductUpdateService::valuesMatch($expectedOld, $actualValue)) {
			$expectedStr = (string) $expectedOld;
			$actualStr = (string) $actualValue;
			throw new ProductUpdateException(new JobProductsItemErrorAlternative1(ProductFieldGroupType::BASIC->value.':'.BasicFieldKeys::TAX_CLASS->value, $expectedStr, $actualStr));
		}
	}

	public static function validateTaxStatus(\WC_Product $product, JobProductsItemChanges $changes) {
		$basic = $changes->getBasic();
		if (!$basic || !$basic->getTaxStatus()) {
			return;
		} $expectedOld = $basic->getTaxStatus()->getOld();
		$actualValue = $product->get_tax_status();
		if (!ProductUpdateService::valuesMatch($expectedOld, $actualValue)) {
			$expectedStr = (string) $expectedOld;
			$actualStr = (string) $actualValue;
			throw new ProductUpdateException(new JobProductsItemErrorAlternative1(ProductFieldGroupType::BASIC->value.':'.BasicFieldKeys::TAX_STATUS->value, $expectedStr, $actualStr));
		}
	}

	public static function validateDefaultTaxonomies(\WC_Product $product, JobProductsItemChanges $changes) {
		if (!$changes->getDefaultTaxonomy()) {
			return;
		} foreach ($changes->getDefaultTaxonomy()->getOld() as $taxonomy => $expectedTermIds) {
			$actualTermIds = wp_get_object_terms($product->get_id(), $taxonomy, ['fields' => 'ids']);
			if ($actualTermIds instanceof \WP_Error) {
				continue;
			} $actualTermIds = (array) $actualTermIds;
			sort($expectedTermIds);
			sort($actualTermIds);
			if ($expectedTermIds !== $actualTermIds) {
				$expectedStr = implode(',', $expectedTermIds);
				$actualStr = implode(',', $actualTermIds);
				throw new ProductUpdateException(new JobProductsItemErrorAlternative1(ProductFieldGroupType::DEFAULT_TAXONOMY->value.':'.$taxonomy, $expectedStr, $actualStr));
			}
		}
	}

	public static function validateAcfFields(\WC_Product $product, JobProductsItemChanges $changes) {
		if (!$changes->getAcf() || !function_exists('get_field')) {
			return;
		} foreach ($changes->getAcf()->getOld() as $fieldId => $expectedValues) {
			$actualValues = get_field($fieldId, $product->get_id());
			if (!is_array($actualValues)) {
				$actualValues = [];
			} $actualValues = array_map('strval', $actualValues);
			$expectedValues = array_map('strval', $expectedValues);
			sort($expectedValues);
			sort($actualValues);
			if ($expectedValues !== $actualValues) {
				$expectedStr = implode(',', $expectedValues);
				$actualStr = implode(',', $actualValues);
				throw new ProductUpdateException(new JobProductsItemErrorAlternative1(ProductFieldGroupType::ACF->value.':'.$fieldId, $expectedStr, $actualStr));
			}
		}
	}

	public static function validateProductAttributes(\WC_Product $product, JobProductsItemChanges $changes) {
		if (!$changes->getProductAttribute()) {
			return;
		} foreach ($changes->getProductAttribute()->getOld() as $attribute => $expectedTermIds) {
			$actualTermIds = wp_get_object_terms($product->get_id(), $attribute, ['fields' => 'ids']);
			if ($actualTermIds instanceof \WP_Error) {
				continue;
			} $actualTermIds = (array) $actualTermIds;
			sort($expectedTermIds);
			sort($actualTermIds);
			if ($expectedTermIds !== $actualTermIds) {
				$expectedStr = implode(',', $expectedTermIds);
				$actualStr = implode(',', $actualTermIds);
				throw new ProductUpdateException(new JobProductsItemErrorAlternative1(ProductFieldGroupType::PRODUCT_ATTRIBUTE->value.':'.$attribute, $expectedStr, $actualStr));
			}
		}
	}
}
