<?php
	
	namespace Main\Services;
	
	use PDO;
	
	class CartService
	{
		public static function getCart() {
			$shoppingCart = db()->prepare("SELECT * FROM ShoppingCarts WHERE accountID = ?");
			$shoppingCart->execute(array(auth()->user()->id()));
			$shoppingCart = $shoppingCart->fetch();
			
			$items = [];
			$total = 0;
			$subtotal = 0;
			$discount = 0;
			$cashback = 0;
			$tax = 0;
			$coupon = null;
			$creatorCode = null;
			$creatorCodeDiscount = 0;
			$creatorCodeRevenueShare = 0;
			
			$shoppingCartID = auth()->user()->id();
			if (!$shoppingCart) {
				$createShoppingCart = db()->prepare("INSERT INTO ShoppingCarts (accountID) VALUES (?)");
				$createShoppingCart->execute(array($shoppingCartID));
			} else {
				$items = self::getItems($shoppingCartID);
				
				if ($shoppingCart["couponID"] != null) {
					$coupon = db()->prepare("SELECT * FROM ProductCoupons WHERE id = ?");
					$coupon->execute([$shoppingCart["couponID"]]);
					$coupon = $coupon->fetch();
					if ($coupon) {
						$couponStatus = self::checkCoupon($coupon["name"], $items);
						if (!$couponStatus["status"]) {
							$updateShoppingCartCoupon = db()->prepare("UPDATE ShoppingCarts SET couponID = ? WHERE accountID = ?");
							$updateShoppingCartCoupon->execute([null, $shoppingCartID]);
							$coupon = null;
						}
					}
					else {
						$updateShoppingCartCoupon = db()->prepare("UPDATE ShoppingCarts SET couponID = ? WHERE accountID = ?");
						$updateShoppingCartCoupon->execute([null, $shoppingCartID]);
					}
				}
				
				if ($shoppingCart["creatorCodeID"] != null) {
					$creatorCode = db()->prepare("SELECT * FROM CreatorCodes WHERE id = ? AND creatorID != ?");
					$creatorCode->execute([$shoppingCart["creatorCodeID"], auth()->user()->id()]);
					$creatorCode = $creatorCode->fetch();
					if (!$creatorCode) {
						$updateShoppingCartCreatorCode = db()->prepare("UPDATE ShoppingCarts SET creatorCodeID = ? WHERE accountID = ?");
						$updateShoppingCartCreatorCode->execute([null, $shoppingCartID]);
						$creatorCode = null;
					}
				}
				
				// Calculate price values
				foreach ($items as $item) {
					$subtotal += $item["subtotal_raw"];
					$discount += $item["discount_raw"];
					
					// Get coupon discount
					if ($coupon && isset($couponStatus) && $couponStatus["status"] && in_array($item["product_id"], $couponStatus["products"])) {
						$discount += (($item["total_raw"])*(($couponStatus["data"]["discount"])/100));
					}
				}
				
				// Get creator code discount
				if ($creatorCode) {
					$creatorCodeDiscount = (($subtotal - $discount) * (($creatorCode["discount"]) / 100));
					$discount += $creatorCodeDiscount;
				}
				
				if (modules('store')->settings('tax') > 0 && modules('store')->settings('isCreditRequiredOnPurchase') == 0) {
					$tax = ($subtotal - $discount) * (modules('store')->settings('tax') / 100);
				}
				$total = $subtotal + $tax - $discount;
				$cashback = !modules('credit')->isActive() ? 0 : ($total * (modules('store')->settings('cashback') / 100));
				
				if ($creatorCode) {
					$creatorCodeRevenueShare = ($total - $cashback) * ($creatorCode["share"] / 100);
				}
			}
			
			return [
				'status' => true,
				'total' => money()->format($total),
				'total_raw' => money()->formatDecimal($total),
				'subtotal' => money()->format($subtotal),
				'subtotal_raw' => money()->formatDecimal($subtotal),
				'discount' => money()->format($discount),
				'discount_raw' => money()->formatDecimal($discount),
				'cashback' => money()->format($cashback),
				'cashback_raw' => money()->formatDecimal($cashback),
				'tax' => money()->format($tax),
				'tax_raw' => money()->formatDecimal($tax),
				'coupon' => $coupon ? [
					'id' => $coupon["id"],
					'code' => $coupon["name"],
				] : null,
				'creator_code' => $creatorCode ? [
					'id' => $creatorCode["id"],
					'creator_id' => $creatorCode["creatorID"],
					'code' => $creatorCode["code"],
					'discount' => money()->format($creatorCodeDiscount),
					'discount_raw' => money()->formatDecimal($creatorCodeDiscount),
					'revenue_share' => money()->format($creatorCodeRevenueShare),
					'revenue_share_raw' => money()->formatDecimal($creatorCodeRevenueShare),
				] : null,
				'items' => $items,
			];
		}
		
		public static function getItems($shoppingCartID)
		{
			$cartItems = db()->prepare("SELECT P.*, SCP.id as cartItemID, SCP.quantity, PC.name as categoryName, PC.isCumulative FROM ShoppingCartProducts SCP INNER JOIN Products P ON P.id = SCP.productID INNER JOIN ProductCategories PC ON PC.id = P.categoryID WHERE SCP.shoppingCartID = ? ORDER BY SCP.id DESC");
			$cartItems->execute([$shoppingCartID]);
			$cartItems = $cartItems->fetchAll(PDO::FETCH_ASSOC);
			
			$items = [];
			
			foreach ($cartItems as $shoppingCartProduct) {
				$productName = $shoppingCartProduct["name"];
				
				// Stock Check
				if ($shoppingCartProduct["stock"] == 0) {
					self::deleteItem($shoppingCartProduct["cartItemID"]);
					continue;
				}
				
				// If the stock is limited
				if ($shoppingCartProduct["stock"] != -1) {
					// Get the total quantity of the product in the cart
					$totalQuantityQuery = db()->prepare("SELECT SUM(quantity) FROM ShoppingCartProducts WHERE shoppingCartID = ? AND productID = ?");
					$totalQuantityQuery->execute(array($shoppingCartID, $shoppingCartProduct["id"]));
					$totalQuantity = (int) $totalQuantityQuery->fetchColumn();
					
					// If stock over the limit update the quantity
					if ($totalQuantity > $shoppingCartProduct["stock"]) {
						$needRemoveQuantity = $totalQuantity - $shoppingCartProduct["stock"];
						if ($needRemoveQuantity >= $shoppingCartProduct["quantity"]) {
							self::deleteItem($shoppingCartProduct["cartItemID"]);
							continue;
						}
						
						$updateShoppingCartItem = db()->prepare("UPDATE ShoppingCartProducts SET quantity = quantity - ? WHERE id = ?");
						$updateShoppingCartItem->execute(array($needRemoveQuantity, $shoppingCartProduct["cartItemID"]));
						$shoppingCartProduct["quantity"] -= $needRemoveQuantity;
					}
				}
				
				// Is disabled quantity
				$disableQuantity = $shoppingCartProduct["isCumulative"] == '1' || $shoppingCartProduct["disableQuantity"] == '1';
				
				if ($disableQuantity) {
					// If the quantity is more than 1, update the quantity
					if ($shoppingCartProduct["quantity"] > 1) {
						$updateShoppingCartItem = db()->prepare("UPDATE ShoppingCartProducts SET quantity = 1 WHERE id = ?");
						$updateShoppingCartItem->execute(array($shoppingCartProduct["cartItemID"]));
						$shoppingCartProduct["quantity"] = 1;
					}
					
					// Delete the product if there is another product in the cart with the same product (if quantity is disabled)
					$totalQuantityQuery = db()->prepare("SELECT SUM(quantity) FROM ShoppingCartProducts WHERE shoppingCartID = ? AND productID = ?");
					$totalQuantityQuery->execute(array($shoppingCartID, $shoppingCartProduct["id"]));
					$totalQuantity = (int) $totalQuantityQuery->fetchColumn();
					if ($totalQuantity > 1) {
						self::deleteItem($shoppingCartProduct["cartItemID"]);
						continue;
					}
				}
				
				// If the category of the product is cumulative
				if ($shoppingCartProduct["isCumulative"] == '1') {
					// Check if there is another product in the cart with the same cumulative category. If there is, delete the product
					$existsSameCumulativeCategoryProductsQuery = db()->prepare("SELECT 1 FROM ShoppingCartProducts SCP INNER JOIN Products P ON SCP.productID = P.id WHERE SCP.shoppingCartID = ? AND P.categoryID = ? AND P.id != ?");
					$existsSameCumulativeCategoryProductsQuery->execute(array($shoppingCartID, $shoppingCartProduct["categoryID"], $shoppingCartProduct["id"]));
					$existsSameCumulativeCategoryProducts = (bool) $existsSameCumulativeCategoryProductsQuery->fetchColumn();
					if ($existsSameCumulativeCategoryProducts) {
						self::deleteItem($shoppingCartProduct["cartItemID"]);
						continue;
					}
				}
				
				// Validate Variables
				$isVariablesValid = true;
				$variables = db()->prepare("SELECT V.*, CV.value FROM ShoppingCartProductVariableValues CV INNER JOIN StoreVariables V ON V.id = CV.variableID WHERE CV.cartProductID = ?");
				$variables->execute(array($shoppingCartProduct["cartItemID"]));
				$variables = $variables->fetchAll(PDO::FETCH_ASSOC);
				
				foreach ($variables as &$variable) {
					switch ($variable["type"]) {
						case "text":
							$isVariablesValid = strlen($variable["value"]) > 0;
							break;
						case "url":
							$isVariablesValid = filter_var($variable["value"], FILTER_VALIDATE_URL);
							break;
						case "email":
							$isVariablesValid = filter_var($variable["value"], FILTER_VALIDATE_EMAIL);
							break;
						case "number":
							$isVariablesValid = is_numeric($variable["value"]);
							break;
						case "dropdown":
							$dropdownOptions = db()->prepare("SELECT * FROM StoreVariableDropdownOptions WHERE variableID = ?");
							$dropdownOptions->execute(array($variable["id"]));
							$dropdownOptions = $dropdownOptions->fetchAll(PDO::FETCH_ASSOC);
							
							$selectedOption = array_filter($dropdownOptions, function ($option) use ($variable) {
								return $option['value'] === $variable['value'];
							});
							
							if (count($selectedOption) == 0) {
								$isVariablesValid = false;
								break;
							}
							
							$selectedOption = array_shift($selectedOption);
							$variable["price"] = $selectedOption["price"];
							break;
					}
					
					// If any variable is not valid, break the loop
					if (!$isVariablesValid) {
						break;
					}
				}
				
				// If any variable is not valid, delete the product
				if (!$isVariablesValid) {
					self::deleteItem($shoppingCartProduct["cartItemID"]);
					continue;
				}
				
				// Unset the variable to prevent memory leak
				unset($variable);
				
				// Calculate the variables price
				$extraFee = 0;
				foreach ($variables as $variable) {
					if (isset($variable["price"]) && $variable["price"] > 0) {
						$extraFee += $variable["price"];
					}
				}
				
				$price = $shoppingCartProduct["price"] + $extraFee;
				$discountedPrice = 0;
				$discount = 0;
				$discountProducts = explode(",", modules('store')->settings('discountProducts'));
				$discountedPriceStatus = ($shoppingCartProduct["discountedPrice"] != 0 && ($shoppingCartProduct["discountExpiryDate"] > datetime() || $shoppingCartProduct["discountExpiryDate"] == '1000-01-01 00:00:00'));
				$storeDiscountStatus = (modules('store')->settings('discount') != 0 && (in_array($shoppingCartProduct["id"], $discountProducts) || modules('store')->settings('discountProducts') == '0') && (modules('store')->settings('discountExpiryDate') > datetime() || modules('store')->settings('discountExpiryDate') == '1000-01-01 00:00:00'));
				if ($discountedPriceStatus || $storeDiscountStatus) {
					$discountedPrice = ($storeDiscountStatus ? (($price*(100- modules('store')->settings('discount')))/100) : ($shoppingCartProduct["discountedPrice"] + $extraFee));
				}
				
				$subs = false;
				$canGift = $shoppingCartProduct["disableGifting"] == '0';
				if ($shoppingCartProduct["isCumulative"] == '1') {
					$subs = db()->prepare("SELECT * FROM Subscriptions S INNER JOIN Products P ON P.id = S.productID WHERE P.categoryID = ? AND accountID = ? AND (endsAt > ? OR endsAt = '1000-01-01 00:00:00') ORDER BY P.priority ASC LIMIT 1");
					$subs->execute(array($shoppingCartProduct["categoryID"], auth()->user()->id(), datetime()));
					$subs = $subs->fetch(PDO::FETCH_ASSOC);
					if ($subs) {
						// Block downgrading
						if ($shoppingCartProduct["priority"] >= $subs["priority"]) {
							self::deleteItem($shoppingCartProduct["cartItemID"]);
							continue;
						}
						
						if ($subs["discountedPrice"] > 0 && ($subs["discountExpiryDate"] > datetime() || $subs["discountExpiryDate"] == '1000-01-01 00:00:00'))
							$subs["price"] = $subs["discountedPrice"];
						else if (modules('store')->settings('discount') != 0 && (in_array($subs["productID"], $discountProducts) || modules('store')->settings('discountProducts') == '0') && (modules('store')->settings('discountExpiryDate') > datetime() || modules('store')->settings('discountExpiryDate') == '1000-01-01 00:00:00'))
							$subs["price"] = ($subs["price"]*(100-modules('store')->settings('discount')))/100;
						
						if ($subs["endsAt"] == '1000-01-01 00:00:00') {
							$subsPrice = ($discountedPrice > 0 ? $discountedPrice : $price) - $subs["price"];
						} else {
							$subsPrice = ($discountedPrice > 0 ? $discountedPrice : $price) - (($subs["price"] * (strtotime($subs["endsAt"]) - time())) / (strtotime($subs["endsAt"]) - strtotime($subs["startsAt"])));
						}
						$discount += ($discountedPrice > 0 ? $discountedPrice : $price) - $subsPrice;
						$discountedPrice = $subsPrice;
						
						$productName .= " (".t__("Upgrade").")";
						$canGift = false;
					}
				}
				
				$items[] = [
					"id" => $shoppingCartProduct["cartItemID"],
					"product_id" => $shoppingCartProduct["id"],
					"category" => $shoppingCartProduct["categoryName"],
					"name" => $productName,
					"price" => money()->format($price),
					"discounted_price" => money()->format($discountedPrice),
					'total' => money()->format((int)$shoppingCartProduct["quantity"] * ($discountedPrice != 0 ? $discountedPrice : $price)),
					'price_raw' => money()->formatDecimal($price),
					'discounted_price_raw' => money()->formatDecimal($discountedPrice),
					'total_raw' => money()->formatDecimal((int)$shoppingCartProduct["quantity"] * ($discountedPrice != 0 ? $discountedPrice : $price)),
					'subtotal_raw' => money()->formatDecimal((int)$shoppingCartProduct["quantity"] * (($discountedPrice != 0 ? $discountedPrice : $price) + $discount)),
					'discount_raw' => money()->formatDecimal($discount),
					"duration" => (int)$shoppingCartProduct["duration"],
					"stock" => $shoppingCartProduct["stock"],
					"image" => "/assets/core/images/store/products/".$shoppingCartProduct["imageID"].".".$shoppingCartProduct["imageType"],
					"quantity" => (int)$shoppingCartProduct["quantity"],
					"variables" => $variables,
					"is_subscription" => $subs,
					"disable_quantity" => $disableQuantity,
					"can_gift" => $canGift,
				];
			}
			
			return $items;
		}
		
		public static function deleteItem($id)
		{
			$deleteShoppingCartItem = db()->prepare("DELETE FROM ShoppingCartProducts WHERE id = ?");
			$deleteShoppingCartItem->execute(array($id));
			
			$deleteShoppingCartItemVariables = db()->prepare("DELETE FROM ShoppingCartProductVariableValues WHERE cartProductID = ?");
			$deleteShoppingCartItemVariables->execute(array($id));
		}
		
		public static function checkCoupon($coupon, $cartItems)
		{
			$productIDs = [];
			$total = 0;
			
			foreach ($cartItems as $cartItem) {
				$productIDs[] = $cartItem["product_id"];
				$total += $cartItem["total_raw"];
			}
			$productIDs = array_unique($productIDs);
			
			$productCoupons = db()->prepare("SELECT * FROM ProductCoupons WHERE name = ? AND (expiryDate > ? OR expiryDate = ?)");
			$productCoupons->execute(array($coupon, datetime(), '1000-01-01 00:00:00'));
			$productCoupons = $productCoupons->fetch();
			if ($productCoupons) {
				$productCouponsHistory = db()->prepare("SELECT COUNT(id) FROM ProductCouponsHistory WHERE couponID = ?");
				$productCouponsHistory->execute(array($productCoupons["id"]));
				$productCouponsHistory = $productCouponsHistory->fetchColumn();
				if ($productCoupons["redeemLimit"] > $productCouponsHistory || $productCoupons["redeemLimit"] == -1) {
					$productCouponsHistory = db()->prepare("SELECT COUNT(id) FROM ProductCouponsHistory WHERE accountID = ? AND couponID = ?");
					$productCouponsHistory->execute(array(auth()->user()->id(), $productCoupons["id"]));
					$productCouponsHistory = $productCouponsHistory->fetchColumn();
					if ($productCoupons["redeemLimitPerCustomer"] > $productCouponsHistory || $productCoupons["redeemLimitPerCustomer"] == -1) {
						$products = explode(",", $productCoupons["products"]);
						if (!empty(array_intersect($productIDs, $products)) || $productCoupons["products"] == '0') {
							if ($total >= $productCoupons["minPayment"]) {
								return [
									'status' => true,
									'data' => $productCoupons,
									'products' => $productCoupons["products"] == '0' ? $productIDs : $products
								];
							} else {
								return [
									'status' => false,
									'error' => 'error_coupon_min_payment|' . $productCoupons["minPayment"],
									'message' => t__("You must have a minimum of %amount% credits in your account to use this coupon!", ['%amount%' => $productCoupons["minPayment"]]),
								];
							}
						} else {
							return [
								'status' => false,
								'error' => 'error_coupon_no_product',
								'message' => t__("This coupon is not valid for your shopping cart!"),
							];
						}
					} else {
						return [
							'status' => false,
							'error' => 'error_coupon_used',
							'message' => t__("You have used this coupon before!"),
						];
					}
				} else {
					return [
						'status' => false,
						'error' => 'error_coupon_limit',
						'message' => t__("This coupon has expired!"),
					];
				}
			} else {
				return [
					'status' => false,
					'error' => 'error_coupon_not_found',
					'message' => t__("Coupon is invalid!"),
				];
			}
		}
		
		public static function checkCreatorCode($code): array
		{
			$creatorCode = db()->prepare("SELECT * FROM CreatorCodes WHERE code = ? AND creatorID != ?");
			$creatorCode->execute(array($code, auth()->user()->id()));
			$creatorCode = $creatorCode->fetch();
			if ($creatorCode) {
				return [
					'status' => true,
					'data' => $creatorCode,
				];
			}
			
			return [
				'status' => false,
				'error' => 'error_creator_code_not_found',
				'message' => t__("Creator code is invalid!"),
			];
		}
	}