<?php

/**
 * Vvveb
 *
 * Copyright (C) 2022  Ziadin Givan
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

namespace Vvveb\Controller\Product;

use function Vvveb\__;
use Vvveb\Controller\Content\Edit;
use function Vvveb\dashesToCamelCase;
use Vvveb\Sql\Product_option_valueSQL;
use Vvveb\Sql\Product_optionSQL;
use Vvveb\Sql\Product_variantSQL;
use Vvveb\Sql\ProductSQL;

class Product extends Edit {
	protected string $type = 'product';

	protected string $object = 'product';

	protected string $module = 'product/product';

	protected string $list   = 'product/products';

	protected bool $revisions = true;

	/**
	 * Save product variants from the request post data.
	 *
	 * @return void
	 */
	function saveVariants(): void {
		$post                     = &$this->request->post;
		$post['product_variant']  = $post['product_variant'] ?? [];

		if ($post['product_variant']) {
			$deleteVariant            = $this->request->post['delete']['product_variant_id'] ?? [];
			$variantSQL               = new Product_variantSQL();

			unset($post['product_variant']['#'], $post['product_variant']['#product_variant#'], $post['product_variant']['0']);

			foreach ($post['product_variant'] as $i => $opt) {
				$opt['product_id'] = $this->product_id;

				if ($opt['product_variant_id']) {
					//existing variant
					$variantSQL->edit([
						'product_variant'    => $opt,
						'product_variant_id' => $opt['product_variant_id'],
					] + $this->global);
				} else {
					// new variant
					unset($opt['product_variant_id']);
					$result = $variantSQL->add(['product_variant' => $opt] + $this->global);

					if ($result && $result['product_variant']) {
						//set new id to use for variant values
						$opt['product_variant_id'] = $result['product_variant'];
					}
				}
			}

			if ($deleteVariant) {
				$variantSQL->delete(['product_variant_id' => $deleteVariant] + $this->global);
			}
		}
	}

	/**
	 * Save product options and option values from the request post data.
	 *
	 * @return void
	 */
	function saveOptions(): void {
		$post                    = &$this->request->post;
		$post['product_option']  = $post['product_option'] ?? [];

		if ($post['product_option']) {
			$deleteOption		    = $this->request->post['delete']['product_option_id'] ?? [];
			$deleteOptionValue	= $this->request->post['delete']['product_option_value_id'] ?? [];

			$optionSQL               = new Product_optionSQL();
			$productoptionvalueSQL   = new Product_option_valueSQL();

			unset($post['product_option']['#'], $post['product_option']['0']);

			foreach ($post['product_option'] as $i => $opt) {
				$opt['product_id'] = $this->product_id;

				if ($opt['product_option_id']) {
					//existing option
					$optionSQL->edit([
						'product_option'	   => $opt,
						'product_option_id'	=> $opt['product_option_id'],
					] + $this->global);
				} else {
					// new option
					unset($opt['product_option_id']);
					$result = $optionSQL->add(['product_option' => $opt] + $this->global);

					if ($result && $result['product_option']) {
						//set new id to use for option values
						$opt['product_option_id'] = $result['product_option'];
					}
				}

				if (isset($opt['product_option_value'])) {
					unset($opt['product_option_value']['#'], $opt['product_option_value']['#product_option_value#']);

					foreach ($opt['product_option_value'] as $j => $value) {
						//set parent option id
						$value['product_option_id'] = $opt['product_option_id'];
						$value['option_id']         = $opt['option_id'];
						$value['product_id']        = $this->product_id;

						if ($value['product_option_value_id']) {
							//existing option value
							$productoptionvalueSQL->edit([
								'product_option_value'		  => $value,
								'product_option_value_id'	=> $value['product_option_value_id'],
							] + $this->global);
						} else {
							unset($value['product_option_value_id']);
							$result = $productoptionvalueSQL->add(['product_option_value' => $value] + $this->global);
						}
					}
				}
			}

			if ($deleteOptionValue) {
				$productoptionvalueSQL->delete(['product_option_value_id' => $deleteOptionValue] + $this->global);
			}

			if ($deleteOption) {
				$optionSQL->delete(['product_option_id' => $deleteOption] + $this->global);
			}
		}
	}

	/**
	 * Save the product and related data (options, variants, features).
	 *
	 */
	function save(): void {
		$post                    = &$this->request->post;

		$post['requires_shipping']    = isset($post['requires_shipping']) ? 1 : 0;
		$post['subscription_onetime'] = isset($post['subscription_onetime']) ? 1 : 0;
		$post['manufacturer_id']      = isset($post['manufacturer_id']) ? ($post['manufacturer_id'] ?: 0) : null;
		$post['vendor_id']            = isset($post['vendor_id']) ? ($post['vendor_id'] ?: 0) : null;

		parent::save();

		$this->product_id = $this->request->get[$this->object . '_id'] ?? $post[$this->object . '_id'] ?? false;

		if ($this->product_id && isset($post['quantity'])) {
			try {
				\Vvveb\System\Notifications::checkLowStock([
					'product_id' => $this->product_id,
					'name'       => $post['name'] ?? '',
					'quantity'   => (int) $post['quantity'],
				]);
			} catch (\Throwable $e) {
			}
		}

		if ($this->product_id) {
			$this->saveOptions();
			$this->saveVariants();

			$product          = new ProductSQL();
			$features         = ['related', /*'variant',*/ 'subscription', 'discount', 'promotion', 'points', 'attribute', 'digital_asset'];

			foreach ($features as $feature) {
				$feature = $this->object . '_' . $feature; //eg: product_related
				$data    = $this->request->post[$feature] ?? [];

				if ($data) {
					$fn = lcfirst(dashesToCamelCase($feature, '_')); //eg: $product->productRelated()
					unset($data['#']);
					unset($data['#' . $feature . '#']);
					$product->$fn([$this->object . '_id' => $this->product_id, $feature => array_unique($data, SORT_REGULAR)]);
				}
			}
		}

		$this->index();
	}

	/**
	 * Generate all combinations of option values.
	 *
	 * @param  array<string, array<int, string>>       $arr Option values keyed by option ID.
	 * @return array{0: list<string>, 1: list<string>} Names and value keys for each combination.
	 */
	private function optionCombinations(array $arr): array {
		$names  = [];
		$values = [];

		foreach ($arr as $optionId => $set) {
			if (! $names) {
				foreach ($set as $i => $val) {
					$names[]  = $val;
					$values[] = $optionId . ':' . $i;
				}
			} else {
				$length    = count($names);
				$newNames  = [];
				$newValues = [];

				for ($l = 0; $l < $length; $l++) {
					$currName = $names[$l];

					foreach ($set as $j => $val) {
						$newNames[]  = $currName . ' / ' . $val;
						$newValues[] = $values[$l] . ',' . $optionId . ':' . $j;
					}
				}

				$names  = $newNames;
				$values = $newValues;
			}
		}

		return [$names, $values];
	}

	/**
	 * Display the product edit form with all related data.
	 *
	 * @return void
	 */
	function index(): void {
		parent::index();

		$view    = $this->view;
		$product = new ProductSQL();
		$options = (isset($view->product['product_id']) ? $view->product : $this->global);

		$data    = $product->getData($options);

		if (isset($data['option'])) {
			foreach ($data['option'] as $id => &$option) {
				unset($option['array_key']);
			}
		} else {
			$data['option'] = [];
		}

		$optionIds = [];

		if (isset($view->product['product_option'])) {
			$product_option_value = &$view->product['product_option_value'];
			$product_option       = &$view->product['product_option'];

			$option_value_content = [];
			$names                = [];

			$default = '';

			if ($view->product['language_id'] != $this->global['default_language_id']) {
				$default = '[' . __('No translation') . ']';
			}

			foreach ($view->product['option_value_content'] as &$value) {
				$option_value_content[$value['option_id']][$value['option_value_id']] = $value;
				$names[$value['option_value_id']]                                     = $value['name'] ?? $default;
				//$optionIds[$value['product_option_id']][$value['product_option_value_id']] = $value['name'];
			}

			foreach ($product_option_value as &$value) {
				if (isset($product_option[$value['product_option_id']])) {
					$product_option[$value['product_option_id']]['values'][$value['product_option_value_id']] = $value;
					$optionIds[$value['product_option_id']][$value['product_option_value_id']]                = $names[$value['option_value_id']] ?? $default;
				}
			}

			$view->product['option_value_content'] = $option_value_content;
		} else {
			$view->product['product_option'] = [];
		}

		$view->combinations = [];

		if ($optionIds && isset($view->product['product_variant'])) {
			foreach ($view->product['product_variant'] as $opts => &$variant) {
				$keys = explode(',', $opts);
				$name = '';
				foreach ($keys as $key) {
					$r = explode(':', $key);
					$o = $r[0];
					$v = $r[1];

					if ($name) {
						$name .= ' / ';
					}

					$name .= $optionIds[$o][$v];
				}

				$variant['name'] = $name;
			}
			//$combinations       = $this->optionCombinations($optionIds);
			//$view->combinations = array_combine($combinations[1], $combinations[0]);
		}

		$view->product['manufacturer_id'] = (($view->product['manufacturer_id'] ?? 0) ? $view->product['manufacturer_id'] : '');
		$view->product['vendor_id']       = (($view->product['vendor_id'] ?? 0) ? $view->product['vendor_id'] : '');
		$view->product['status']          = $view->product['status'] ?? 1;
		$data['subtract_stock']           = [1 => __('Yes'), 0 => __('No')]; //Subtract stock options
		$data['status']                   = [0 => __('Disabled'), 1 => __('Enabled')];
		$view->set($data);
	}
}
