<?php
namespace App\Models;
use CodeIgniter\Model;

class Product extends Model
{
	protected $table = 'epos_product';
	protected $primaryKey = 'prod_id';

	function exists($item_id)
	{
		$this->db->from('product');
		$this->db->where('prod_id', $item_id);
		$query = $this->db->get();
		return ($query->num_rows() == 1);
	}

	function fetch_category($cat)
	{
		$db = \Config\Database::connect();

		$conditions = array('parent_id' => $cat, 'display' => '1');
		$query = $db->table("epos_categories")->orderBy("category_name", "ASC")
			->where($conditions)
			->get();

		return $query->getResult();
	}

	function fetch_allsubcategories()
	{
		$conditions = array('parent_id !=' => '0');
		$this->db->order_by("category_name", "ASC");
		$this->db->where($conditions);
		$query = $this->db->get("epos_categories");
		return $query->getResult();
	}

	function fetch_parent($cat)
	{
		$conditions = array('category_id' => $cat, 'parent_id !=' => '0', 'display' => '1');
		$this->db->order_by("category_name", "ASC");
		$this->db->where($conditions);
		$q = $this->db->get("epos_categories");
		if ($q->num_rows() > 0) {
			$r = $q->row();
			return $r->parent_id;
		}
	}

	function get_all_categories($category_id = 0)
	{
		$is_mobile = session()->get('is_mobile');

		$Admin = new Admin();

		$db = \Config\Database::connect();

		// Fetch Image Host
		$img_host = $Admin->get_plink('img_host');

		$url = 'products/index/';
		$table_rows = "<div class='c_container' id='cat_main' style='display:none'>";

		$table_rows .= "<div class='c_item 0' id='cat_1' onclick='refresh_page(1, 0);'>"
			. "<div class='category-image'><div class='d-flex justify-content-center align-items-center'><img src='$img_host/images/categories/all/main.jpg'></div></div>"
			. "<div class='category-name'>All Products</div>"
			. "</div>";

		$m_values = array();
		$s_values = array();
		$m_values[0] = "All Products";

		$results = $db->query("SELECT * FROM epos_categories WHERE parent_id=0 AND display=1 ORDER BY category_name ASC");
		$c = 0;
		foreach ($results->getResult() as $res) {
			$c = $c + 1;
			$j = $c + 1;
			$catname = strtolower($res->category_name);
			$catname = preg_replace("/[.,&\s\/_]+/", "-", $catname);
			$m_values[$res->category_id] = $res->category_name;
			//echo $m_values[$res->category_id] . "<br />";

			$img_src = "$img_host/images/categories/$catname/main.jpg";
			$category = Category::findOne($res->category_id);
			if (!empty($category)) {
				if ($is_mobile == '1') {
					if (!empty($category['logo_mobile']['url']))
						$img_src = $category['logo_mobile']['url'];
					else if (!empty($category['logo_web']['url']))
						$img_src = $category['logo_web']['url'];
				} else {
					if (!empty($category['logo_web']['url']))
						$img_src = $category['logo_web']['url'];
					else if (!empty($category['logo_mobile']['url']))
						$img_src = $category['logo_mobile']['url'];
				}
			}

			$table_rows .= "<div class='c_item $res->category_id' id='cat_$j' onclick='refresh_page($j, $res->category_id);'>"
				. "<div class='category-image'><div class='d-flex justify-content-center align-items-center'><img src='$img_src'/></div></div>"
				. "<div class='category-name'>$res->category_name</div>"
				. "</div>";

			$catnum[$c] = $res->category_id;
			$cname[$c] = $catname;
		}
		//for($e=$c; $e<=15; $e++){  $table_rows .= "<div class='c_item'>&nbsp;</div>";	}
		for ($e = $c; $e <= 33; $e++) {
			$table_rows .= "<div class='c_item'>&nbsp;</div>";
		}
		$table_rows .= "</div>";
		$table_rows .= "<div id='subcategories'>";
		for ($i = 1; $i <= $c; $i++) {
			$j = $i + 1;
			$table_rows .= "<div class='c_container' id='subcat_$j' style='display:none;'>";
			$query = "select * from epos_categories where parent_id='" . $catnum[$i] . "' AND display=1 ORDER BY category_name ASC";
			$results_sub = $db->query($query);
			if (count($results_sub->getResult()) == 1) {
				$solo = 'c_item solo';
			} else {
				$solo = 'c_item';
			}

			$sc = 0;
			foreach ($results_sub->getResult() as $res_sub) {
				$sc = $sc + 1;
				$scname = strtolower($res_sub->category_name);

				$scname = preg_replace("/[.,&\s\/_]+/", "-", $scname);
				if (count($results_sub->getResult()) == 1) {
					$scname = "main";
				}
				$s_values[$res_sub->category_id] = $res_sub->category_name;

				// image
				$img_src = "$img_host/images/categories/$cname[$i]/$scname.jpg";
				$category = Category::findOne($res_sub->category_id);
				if (!empty($category)) {
					if ($is_mobile == '1') {
						if (!empty($category['media_mobile']['url']))
							$img_src = $category['media_mobile']['url'];
						else if (!empty($category['media_web']['url']))
							$img_src = $category['media_web']['url'];
					} else {
						if (!empty($category['media_web']['url']))
							$img_src = $category['media_web']['url'];
						else if (!empty($category['media_mobile']['url']))
							$img_src = $category['media_mobile']['url'];
					}
				}

				$table_rows .= "<div class='$solo $res_sub->category_id $catnum[$i]' onclick='refresh_page(0, $res_sub->category_id);'>"
					. "<div class='category-image'><div class='d-flex justify-content-center align-items-center'><img src='$img_src'></div></div>"
					. "<div class='category-name'>$res_sub->category_name</div>"
					. "</div>";
			}
			if ($sc < 6) {
				for ($e = $sc; $e <= 7; $e++) {
					$table_rows .= "<div class='c_item'>&nbsp;</div>";
				}
			} else {
				for ($e = $sc; $e <= 20; $e++) {
					$table_rows .= "<div class='c_item'>&nbsp;</div>";
				}
			}
			$table_rows .= "</div>";
		}
		$table_rows .= "</div>";

		return $table_rows;
	}

	function get_all_favorites($user_info, $limit = 15000, $offset = 0, $sort_key = 3, $category_id = 0)
	{
		return $this->load_favorites('', '', '', $user_info, $limit, $offset, $sort_key, $category_id);
	}

	function count_all_category($user_info, $filter)
	{
		return $this->total_search_num_rows_category($user_info, $filter);
	}

	public function get_info($prod_id)
	{
		$db = \Config\Database::connect();
		$builder = $db->table('epos_product p');
		$builder->select("p.*, 
			pi.url as image_url, 
			pi.version as image_version,
			(CASE WHEN (prod_rrp * prod_uos > 0) 
				THEN ROUND((1-((prod_sell * (1.00 + (CASE WHEN vat_code='A' THEN 0.2 WHEN vat_code='C' THEN 0.05 ELSE 0 END))) / (prod_rrp * prod_uos)))*100, 1)
				ELSE 0 
			END) AS por
		");
		$builder->join('epos_product_images pi', 'CAST(SUBSTRING(p.prod_code, 2, 6) AS UNSIGNED)=pi.prod_code', 'left');
		$builder->where('prod_id', $prod_id);

		$result = $builder->get()->getRow();
		return $result;
	}

	function get_item_id($item_number)
	{
		return;
	}

	function get_multiple_info($item_ids)
	{
		// $this->db->from('items');
		// $this->db->where_in('item_id',$item_ids);
		// $this->db->order_by("item", "asc");
		// return $this->db->get();

		$db = \Config\Database::connect();
		$builder = $db->table('items');
		$builder->whereIn('item_id', $item_ids);
		$builder->orderBy('item', 'asc');
		return $builder->get();
	}
	public function save($row): bool
	{

		return true;
	}

	function delete_all()
	{
		return;
	}

	function save_excel(&$prod_data, $prod_id = false)
	{
		return;
	}
	function update_multiple($item_data, $item_ids)
	{
		return;
	}
	function delete($item_id = null, bool $purge = false)
	{
		$this->db->where('item_id', $item_id);
		return $this->db->update('items', array('deleted' => 1));
	}
	function delete_list($item_ids)
	{
		$this->db->where_in('item_id', $item_ids);
		return $this->db->update('items', array('deleted' => 1));
	}
	function get_search_suggestions($search, $limit = 25)
	{
		$suggestions = array();
		return $suggestions;
	}

	function get_search_suggestions0($user_info, $search, $limit = 30)
	{
		$suggestions = array();
		return $suggestions;
	}

	function get_search_suggestions1($user_info, $search, $limit = 30)
	{
		$suggestions = array();
		return $suggestions;
	}

	function get_search_suggestions2($user_info, $search, $limit = 30, $category_id = 0)
	{
		$db = \Config\Database::connect();
		$branch = session()->get('branch');
		$organization_id = session()->get('organization_id');

		$Admin = new Admin();

		$category_id = intval($category_id);
		$suggestions = array();

		$cond = "";
		$search0 = $search;
		try {
			$cond .= "AND (";
			$arr0 = preg_split("/\,/", $search0);
			foreach ($arr0 as $index0 => $a0) {
				if ($index0 > 0)
					$cond .= " OR ";
				$cond .= "( ";
				//------------
				$a0 = trim($a0);
				$arr = array_values(array_filter(preg_split("/\ /", $a0), fn($v) => $v !== ''));
				foreach ($arr as $index => $a) {
					$keyword = $a;
					if (strpos($keyword, '%') !== false) {
						$keyword = str_replace(['%', '_'], ['\%', '\_'], $keyword);
					} else {
						$keyword = $db->escapeLikeString($a);
					}
					if ($index > 0)
						$cond .= " AND ";
					$cond .= "( ";
					// original search engine...
						// $cond .= "p.prod_code LIKE '%" . $keyword . "' ";
						// $cond .= "OR retail LIKE '" . 	$keyword . "%' ";
						// $cond .= "OR wholesale LIKE '" . $keyword . "%' ";
						// $cond .= "OR prod_desc LIKE '%". $keyword . "%' ";
						// $cond .= "OR brand LIKE '%" . 	$keyword . "%' ";
					// new search engine, holla.ardy
					$cond .= "p.prod_code LIKE '%" . 		$keyword . "' ";
					//----------
					$cond .= "OR retail LIKE '" . 			$keyword . "%' ";
					//----------
					$cond .= "OR wholesale LIKE '" . 		$keyword . "%' ";
					//----------
					$cond .= "OR prod_desc LIKE '" . 		$keyword . "%' ";
					$cond .= "OR prod_desc LIKE '% " . 		$keyword . "%' ";
					$cond .= "OR prod_desc = '" . 			$keyword . "' ";
					//----------
					$cond .= "OR prod_pack_desc LIKE '%" . 	$keyword . "%' ";
					//----------
					$cond .= "OR brand LIKE '%" . 			$keyword . "%' ";
					$cond .= ") ";
				}
				//------------
				$cond .= ") ";
			}
			$cond .= ") ";
		} catch (\Exception $e) {

		}
		//-------------------
		$search_cond = $cond;

		// Fetch Image Host
		$img_host = $Admin->get_plink('img_host');
		$query =  "SELECT p.prod_code, p.prod_desc, pi.url as image_url, pi.version as image_version 
                   FROM epos_product as p 
                   LEFT JOIN epos_product_images as pi on CAST(SUBSTRING(p.prod_code, 2, 6) AS UNSIGNED)=pi.prod_code 
                   LEFT JOIN epos_categories as ct on p.group_desc = ct.filter_desc 
                   WHERE p.is_disabled='N' AND ct.display=1 AND p.branch={$branch} ";
		if (!empty($organization_id)) {
			$query .= "AND p.organization_id={$organization_id} ";
		}
    	$query .= $search_cond . " AND ";
		// limit results to current selected category only. (original, till 2025.10.23)
		// but from 2025.10.24, because of feedback, not limit, instead of apply on all products
		/*
		if ($category_id != 0) {
			$results_category = $db->query("SELECT * FROM epos_categories WHERE category_id = '" . $category_id . "'");
			$res_category = $results_category->getRow();
			if ($res_category->parent_id == 0) {
				$results_subcategory = $db->query("SELECT * FROM epos_categories WHERE parent_id = '" . $res_category->category_id . "'");
				$nCount = 0;
				foreach ($results_subcategory->getResult() as $res_subcategory) {
					if ($nCount == 0) {
						$query .= "(group_desc = '" . $res_subcategory->filter_desc . "' ";
					} else {
						$query .= "OR group_desc = '" . $res_subcategory->filter_desc . "' ";
					}
					$nCount++;
				}
				$query .= ") AND ";
			} else {
				$query = "group_desc = '" . $res_category->filter_desc . "' AND ";
			}
		}*/
		$query .= "(price_list = '000' ";
		if (!empty($user_info)) {
			$codes = $user_info->price_list_codes ?? [];
			foreach ($codes as $code) {
				$query .= "OR price_list = '{$code}' ";
			}
		} else {
			$query .= "OR price_list='999'";
		}
		$query .= ") GROUP BY prod_desc ";
		$first_keyword = $db->escapeLikeString(trim(explode(' ', trim(explode(',', $search)[0]))[0]));
		$matchRankExpr = self::buildMatchRankExpr(['search0' => $search], $db);
		$query .= " ORDER BY ({$matchRankExpr}) ASC, CASE "
			. "WHEN prod_desc LIKE '" . $first_keyword . "%' THEN 0 "
			. "WHEN prod_desc LIKE '% " . $first_keyword . "%' THEN 1 "
			. "ELSE 2 END ASC, prod_desc ASC ";
		// here, we paginate the result as many as limit because speed up
		$query .= " LIMIT " . $limit; 
		$suggestions = array();
		$result = $db->query($query);
		foreach ($result->getResult() as $row) {
			$prod_desc = $row->prod_desc;
			$prod_desc_alt = str_replace("&", "%26", $prod_desc);
			$prod_desc_alt = str_replace("%", "%25", $prod_desc);

			$url = base_url() . 'products/index?';
			$url .= '&search_mode=search';
			// $url .= '&search0=' . $row->prod_code;
			$url .= '&search0=' . $prod_desc_alt;
			$url .= '&category_id=0';

			$imgSrc = !empty($row->image_url)
				? $img_host . '/product_images/' . $row->image_url . '?=v' . $row->image_version
				: '/images/icons/ribbon/no-product.png';
			$suggestions[] = [
				'label' => '<a href="' . $url . '" style="padding:0px;">
						<div class="autosearch" style="display:flex; align-items:center; background-color:aliceblue;" >
							<div style="margin:5px; padding:10px;">
								<img width="50" height="50" src="' . $imgSrc . '" />
							</div>
							<div style="margin:5px; padding:10px; font-size:14px; align-self: center;">' . $prod_desc . '</div>
						</div>
					</a>',
				'value' => $prod_desc,
			];
		}
		return $suggestions;
	}
	
	function get_item_search_suggestions($search, $limit = 25)
	{
		return;
	}
	function get_category_suggestions($search)
	{
		return;
	}

	function total_search_num_rows($user_info, $filter)
	{
		return $this->total_search_num_rows_category($user_info, $filter);
	}

	function total_search_num_rows_category($user_info, $filter)
	{
		$db = \Config\Database::connect();
		$branch = session()->get('branch');
		$organization_id = session()->get('organization_id');

		$cond = $this->buildCondition($user_info, $filter);

		$query = "SELECT p.prod_code FROM epos_product as p ";
		if (!empty($user_info) && !empty($filter['favorite'])) {
			$query .= " INNER JOIN epos_favorites as ef on (ef.prod_code = p.prod_code and ef.person_id=" . $user_info->person_id . " and ef.organization_id=".$organization_id." and ef.branch=".$branch." ) ";
   		}
    	$query .= " LEFT JOIN epos_categories as ct on p.group_desc = ct.filter_desc ";
		$query .= " WHERE ct.display=1 AND p.branch={$branch} ";
		if (!empty($organization_id)) {
			$query .= " AND p.organization_id={$organization_id} ";
		}
		$query .= " AND " . $cond . " GROUP BY p.prod_code ";

		$results = $db->query($query);
		return $results->getNumRows();
	}
	function search($user_info, $filter)
	{
		return $this->search_category($user_info, $filter);
	}

	// function search_category($user_info, $limit = 30 ,  $offset = 0 , $sort_key = 6 , 
	// 					$category_id=0, $im_new = 0, $plan_profit = 0, $own_label='N', $brand='', $favorite=0)
	/**
	 * Build a CASE expression that ranks rows by exact identifier match for
	 * a numeric search0 input. Returns "1" (no priority) when input doesn't qualify.
	 *   - length <= 7 digits  -> exact prod_code match wins
	 *   - length in {8,11,12,13,14} -> exact retail OR wholesale match wins
	 * Other numeric lengths and non-numeric inputs fall through.
	 */
	public static function buildMatchRankExpr(array $filter, $db): string
	{
		$search = trim($filter['search0'] ?? '');
		if ($search === '' || !ctype_digit($search)) {
			return '1';
		}
		$len = strlen($search);
		$val = $db->escape($search);
		if ($len <= 7) {
			return "CASE WHEN p.prod_code = {$val} THEN 0 ELSE 1 END";
		}
		if (in_array($len, [8, 11, 12, 13, 14], true)) {
			return "CASE WHEN p.retail = {$val} OR p.wholesale = {$val} THEN 0 ELSE 1 END";
		}
		return '1';
	}

	function search_category($user_info, $filter)
	{
		$db = \Config\Database::connect();
		$branch = session()->get('branch');
		$organization_id = session()->get('organization_id');

		$cond = $this->buildCondition($user_info, $filter);
		$matchRankExpr = self::buildMatchRankExpr($filter, $db);
		$porformula = ", (CASE WHEN (prod_rrp * prod_uos > 0) 
							   THEN ROUND((1-((prod_sell * (1.00 + (CASE WHEN vat_code='A' THEN 0.2 WHEN vat_code='C' THEN 0.05 ELSE 0 END))) / (prod_rrp * prod_uos)))*100, 1)
							   ELSE 0 END)
						 AS por ";
		if (request()->getUri()->getSegment(1) == 'presells') {
			$tablename = "presell";
			$addcols = "period_ref, g_qty, g_min, g_max, s_qty, s_min, s_max, m_qty, m_min, m_max, l_qty, l_min, l_max, e_qty, e_min, e_max, ordered, ";
		} else {
			$tablename = "product";
			$addcols = "";
		}
		$query = "SELECT
						{$addcols}
						p.prod_id, p.prod_code, prod_uos, prod_desc, prod_pack_desc, price_start, price_end, brand, epoints,
						vat_code, prod_price, group_desc, prod_code1,
						price_list, prod_level1, prod_level2, prod_level3,
						non_promo_sell_price,
						pi.url as image_url,
						pi.version as image_version,
						IF(brand='', 'zzzz', brand) as v_brand,
						prod_sell,
						prod_rrp, wholesale, retail, p_size, is_disabled, promo, van, shelf_life,
						availability, pfp, p.layer, p.pallet,
						({$matchRankExpr}) AS match_rank
						{$porformula}
        		FROM epos_{$tablename} as p 
				LEFT JOIN epos_product_images as pi on CAST(SUBSTRING(p.prod_code, 2, 6) AS UNSIGNED)=pi.prod_code 
        		LEFT JOIN epos_categories as ct on p.group_desc = ct.filter_desc ";
		if (!empty($filter['favorite'])) {
			$query .= " INNER JOIN epos_favorites as ef on (ef.prod_code = p.prod_code and ef.person_id={$user_info->person_id} and ef.organization_id=".$organization_id." and ef.branch=".$branch." ) ";
		}
		$query .= " WHERE ct.display=1 AND p.branch={$branch} ";
		if (!empty($organization_id)) {
			$query .= "AND p.organization_id={$organization_id} ";
		}
    	$query .= "AND " . $cond;

		$sort_key = isset($filter['sort_key']) ? $filter['sort_key'] : 3;
		switch ($sort_key) {
			case 1:
				$group = 'p.prod_code';
				$order_by = 'prod_code ASC, ';
				break;
			case 2:
				$group = 'p.prod_code';
				$order_by = 'prod_code DESC, ';
				break;
			case 3:
				;
				$group = 'p.prod_code';
				$order_by = 'prod_desc ASC, ';
				break;
			case 4:
				$group = 'p.prod_code';
				$order_by = 'prod_desc DESC, ';
				break;
			case 5:
				$group = 'p.prod_code';
				$order_by = 'por ASC, ';
				break;
			case 6:
				$group = 'p.prod_code';
				$order_by = 'por DESC, ';
				break;
			case 7:
				$group = 'p.prod_code';
				$order_by = 'prod_sell ASC, ';
				break;
			case 8:
				$group = 'p.prod_code';
				$order_by = 'prod_sell DESC, ';
				break;
			case 9:
				$group = 'p.prod_code';
				$order_by = 'ranking DESC, ';
				break;
			case 10:
				$group = 'p.prod_code';
				$order_by = 'v_brand ASC, ';
				break;
			default:
				$group = 'p.prod_code';
				$order_by = '';
				break;
		}
		$order_by = "match_rank ASC, " . $order_by . "p.brand ASC, p.prod_desc ASC, p.prod_pack_desc ASC, p.meta ASC ";
		$query .= " GROUP BY " . $group . " ";
    	$query .= " ORDER BY " . $order_by . " ";

		$offset = isset($filter['offset']) ? $filter['offset'] : 0;
		$query .= ' LIMIT ' . $offset . ', ' . $filter['limit'];

		$results = $db->query($query);
		return $results;
	}

	function get_categories()
	{
		$this->db->select('category');
		$this->db->from('items');
		$this->db->where('deleted', 0);
		$this->db->distinct();
		$this->db->order_by("category", "asc");

		return $this->db->get();
	}

	function to_cart($prod_code, $mode, $person_id, $quantity = 1, $spresell = 0, $type=null, $incentive=0)
	{
		$Employee = new Employee();
		$person_info = $Employee->get_info($person_id);
		$branch = session()->get('branch');
		$organization_id = session()->get('organization_id');
		$db = \Config\Database::connect();

		if ($mode == 4) {
			$query =  " DELETE FROM epos_cart WHERE person_id={$person_id}" .
                " AND branch={$branch}";
			if (!empty($organization_id)) {
				$query .= " AND organization_id={$organization_id} ";
			}
			$query .= " AND prod_code='{$prod_code}' ";
			if ($incentive) {
				$query .= " AND mb_deal_id!=0 AND mb_deal_id IS NOT NULL ";
			} else {
				$query .= " AND (mb_deal_id=0 OR mb_deal_id IS NULL) ";
			}
			// $query .= " AND presell=0 AND group_type='{$type}'";
			$db->transStart();
			$db->query($query);
			$db->transComplete();

			if ($db->transStatus() === FALSE)
				return -1;
			else
				return 0;
		} else {
			$cond = " prod_code='{$prod_code}' and person_id={$person_id}" . 
            		" AND branch={$branch}";
			if (!empty($organization_id)) {
				$cond.= " AND organization_id={$organization_id} ";
			}
			if ($incentive) {
				$cond .= " AND mb_deal_id!=0 AND mb_deal_id IS NOT NULL ";
			} else {
				$cond .= " AND (mb_deal_id=0 OR mb_deal_id IS NULL) ";
			}
			// $cond .= " AND presell=0 ";
			// $cond .= ($spresell == 1) ? " AND group_type='spresell'" : " AND group_type!='spresell'";
			$query = "SELECT * FROM epos_cart WHERE " . $cond;
			$res = $db->query($query);
	
			if ($res->getNumRows() == 0) {
				if ($mode == 1 || $mode == 3) {
					$res_prod = $this->getLowestPriceProductByCode($person_info, $prod_code, true, $spresell);
					if ($spresell) {
						$type = "spresell";
					} else {
						// Get Category type
						$category = $db->table('epos_categories')->where('filter_desc', $res_prod->group_desc)->get()->getRow();
						$type = !empty($category) ? $category->type : 'general';
					}
	
          			$line_position = $this->genCartLinePosition($person_id);

					$cart_data = array(
						'prod_code' => $prod_code,
						'quantity' => $quantity,
						'person_id' => $person_id,
						'group_type' => $type,
						'line_position' => $line_position,
						'branch' => $branch,
						'organization_id' => $organization_id,
					);
					$db->table('epos_cart')->insert($cart_data);
					return $quantity;
				} else if ($mode == 2)
					return 0;
				else
					return -1;
			} else if ($res->getNumRows() == 1) {
				$res_row = $res->getRow();
				$quantity1 = $res_row->quantity;
				if ($mode == 1)
					$quantity1 = $quantity1 + 1;
				else if ($mode == 2) {
					if ($quantity1 > 0)
						$quantity1 = $quantity1 - 1;
				} else if ($mode == 3) {
					$quantity1 = $quantity;
				}
	
				if ($quantity1 == 0) {
					$db = \Config\Database::connect();
					$db->query("DELETE FROM epos_cart WHERE " . $cond);
					return 0;
				} else {
					// $line_position = $this->genCartLinePosition($person_id);
					$cart_data = ['quantity' => $quantity1/*, 'line_position' => $line_position*/];
					$builder = $db->table('epos_cart');
					$builder->where($cond);
					$builder->update($cart_data);
					return $quantity1;
				}
			} else {
				return -1;
			}
		}
	}

	function to_cart_by_api($prod_code, $mode, $person_id, $quantity = 1, $spresell = 0, $type=null, $branch = null, $organization_id = null)
	{
		$Employee = new Employee();
		$person_info = $Employee->get_info($person_id);
		$db = \Config\Database::connect();

		if ($mode == 4) {
			$query =  " DELETE FROM epos_cart WHERE person_id={$person_id}" .
                " AND branch={$branch}";
			if (!empty($organization_id)) {
				$query .= " AND organization_id={$organization_id} ";
			}
			$query .= " AND prod_code='{$prod_code}' AND presell=0 AND group_type='{$type}'";
			$db->transStart();
			$db->query($query);
			$db->transComplete();

			if ($db->transStatus() === FALSE)
				return -1;
			else
				return 0;
		} else {
			$cond = " prod_code='{$prod_code}' and person_id={$person_id}" . 
            		" AND branch={$branch}";
			if (!empty($organization_id)) {
				$cond.= " AND organization_id={$organization_id} ";
			}
			$cond .= " AND presell=0 ";
			$cond .= ($spresell == 1) ? " AND group_type='spresell'" : " AND group_type!='spresell'";
			$query = "SELECT * FROM epos_cart WHERE " . $cond;
			$res = $db->query($query);
	
			if ($res->getNumRows() == 0) {
				if ($mode == 1 || $mode == 3) {
					$res_prod = $this->getLowestPriceProductByCode($person_info, $prod_code, true, $spresell, $branch, $organization_id);
					if ($spresell) {
						$type = "spresell";
					} else {
						// Get Category type
						$category = $db->table('epos_categories')->where('filter_desc', $res_prod->group_desc)->get()->getRow();
						$type = !empty($category) ? $category->type : 'general';
					}
	
          			$line_position = $this->genCartLinePosition($person_id);

					$cart_data = array(
						'prod_code' => $prod_code,
						'quantity' => $quantity,
						'person_id' => $person_id,
						'group_type' => $type,
						'line_position' => $line_position,
						'branch' => $branch,
						'organization_id' => $organization_id,
					);
					$db->table('epos_cart')->insert($cart_data);
					return $quantity;
				} else if ($mode == 2)
					return 0;
				else
					return -1;
			} else if ($res->getNumRows() == 1) {
				$res_row = $res->getRow();
				$quantity1 = $res_row->quantity;
				if ($mode == 1)
					$quantity1 = $quantity1 + 1;
				else if ($mode == 2) {
					if ($quantity1 > 0)
						$quantity1 = $quantity1 - 1;
				} else if ($mode == 3) {
					$quantity1 = $quantity;
				}
	
				if ($quantity1 == 0) {
					$db = \Config\Database::connect();
					$db->query("DELETE FROM epos_cart WHERE " . $cond);
					return 0;
				} else {
					// $line_position = $this->genCartLinePosition($person_id);
					$cart_data = ['quantity' => $quantity1/*, 'line_position' => $line_position*/];
					$builder = $db->table('epos_cart');
					$builder->where($cond);
					$builder->update($cart_data);
					return $quantity1;
				}
			} else {
				return -1;
			}
		}
	}

	function presell_to_cart($prod_code, $mode, $person_id, $quantity = 1)
	{
		$db = \Config\Database::connect();

		$query = "SELECT * FROM epos_cart WHERE prod_code='" . $prod_code . "' and person_id='" . $person_id . "' and presell=1";
		$res = $db->query($query);

		if ($res->num_rows() == 1) {
			$res_row = $res->row();
			$quantity1 = $res_row->quantity;

			if ($mode == 1) {
				$quantity1 = $quantity1 + 1;
			} else if ($mode == 2) {
				if ($quantity1 > 0)
					$quantity1 = $quantity1 - 1;
			} else if ($mode == 3) {
				$quantity1 = $quantity;
			}

			$cart_data = array('quantity' => $quantity1);
			$this->db->where('prod_code', $prod_code);
			$this->db->where('person_id', $person_id);
			$this->db->where('presell', '1');
			$this->db->update('cart', $cart_data);
			return $quantity1;
		} else
			return -1;
	}

	public static function get_cart_quantity($prod_code, $person_id, $spresell = 0)
	{
		$db = \Config\Database::connect();
		$query = "SELECT quantity FROM epos_cart
				  WHERE prod_code='" . $prod_code . "' and person_id='" . $person_id . "'";
		// 		  . "' and presell=0";
		// $query .= ($spresell == 1) ? " AND group_type='spresell'" : " AND group_type!='spresell'";
		$results = $db->query($query)->getRow();
		if ($results)
			return $results->quantity;
		return 0;
	}

	public static function get_cart_quantities_batch($prod_codes, $person_id)
	{
		if (empty($prod_codes)) return [];
		$db = \Config\Database::connect();
		$escaped = implode(',', array_map(function($c) use ($db) { return $db->escape(trim($c)); }, $prod_codes));
		$results = $db->query("SELECT prod_code, quantity FROM epos_cart WHERE prod_code IN ({$escaped}) AND person_id='{$person_id}'")->getResult();
		$map = [];
		foreach ($results as $row) {
			$map[$row->prod_code] = $row->quantity;
		}
		return $map;
	}

	public static function get_favorite_states_batch($person_id, $prod_codes)
	{
		if (empty($prod_codes)) return [];
		$db = \Config\Database::connect();
		$branch = session()->get('branch');
		$organization_id = session()->get('organization_id');
		$escaped = implode(',', array_map(function($c) use ($db) { return $db->escape(trim($c)); }, $prod_codes));
		$results = $db->query("SELECT prod_code FROM epos_favorites WHERE person_id='{$person_id}' AND prod_code IN ({$escaped}) AND organization_id='{$organization_id}' AND branch='{$branch}'")->getResult();
		$map = [];
		foreach ($results as $row) {
			$map[$row->prod_code] = 'active';
		}
		return $map;
	}

	function get_presell_quantity($prod_code, $person_id)
	{
		$db = \Config\Database::connect();

		$query = "SELECT quantity FROM epos_cart 
				  WHERE prod_code='" . $prod_code . "' and person_id='" . $person_id . "' and presell=1";
		$results = $db->query($query)->getRow();
		if ($results)
			return $results->quantity;
		return 0;
	}

	function get_duplicate($prod_code)
	{
		$db = \Config\Database::connect();
		$Employee = new Employee();
		$user_info = $Employee->get_logged_in_employee_info();
		$codes = Employee::parsePriceLists($user_info->price_lists ?? '');
		$checkCodes = array_merge(Employee::PRICE_LIST_GROUPS['du'], Employee::PRICE_LIST_GROUPS['us']);
		$userDuUsCodes = array_intersect($codes, $checkCodes);
		if (!empty($userDuUsCodes)) {
			$escaped = implode(',', array_map(fn($c) => $db->escape($c), $userDuUsCodes));
			$q = "SELECT * FROM epos_product WHERE prod_code=" . $db->escape($prod_code) . " AND price_list IN ({$escaped})";
			$r = $db->query($q);
			return $r->getNumRows() > 0;
		}
		return false;
	}

	function check_daytoday_usave($pid)
	{
		$db = \Config\Database::connect();
		$q = $db->table('epos_employees')->select('price_lists')->where('person_id', $pid)->get();
		if ($q->getNumRows() > 0) {
			$r = $q->getRow();
			$codes = Employee::parsePriceLists($r->price_lists ?? '');
			$checkCodes = array_merge(Employee::PRICE_LIST_GROUPS['du'], Employee::PRICE_LIST_GROUPS['us']);
			$data = '';
			$hasAny = false;
			foreach ($checkCodes as $c) {
				if (in_array($c, $codes)) {
					$data .= "1";
					$hasAny = true;
				} else {
					$data .= "0";
				}
			}
			return $hasAny ? $data : '';
		} else {
			$data = "No Rows Found";
		}
	}

	function presell($prod_id)
	{
		//$this->db->select('*');
		$this->db->where('prod_id', $prod_id);
		$q = $this->db->get('epos_presell');
		//$query = "SELECT * FROM epos_presell WHERE prod_id='".$prod_id."'";
		if ($q->num_rows() > 0) {
			$r = $q->row();
			return $r;
		}
		return 0;
	}

	public static function get_favorite_state($pid, $prod_code)
	{
		$db = \Config\Database::connect();
		$branch = session()->get('branch');
		$organization_id = session()->get('organization_id');	

		$query = $db->table('epos_favorites')
			->select('*')
			->where('person_id', $pid)
			->where('prod_code', $prod_code)
			->where('organization_id', $organization_id)
			->where('branch', $branch)
			->get();

		if ($query->getNumRows() > 0) {
			return "active";
		}
		return "";
	}

	function favorite($pid, $prod_code, $state)
	{
		$db = \Config\Database::connect();
		$branch = session()->get('branch');
		$organization_id = session()->get('organization_id');	
		
		if ($state == "active") {
			$db->query("DELETE FROM epos_favorites WHERE person_id='" . $pid . "' and prod_code='" . $prod_code . "' and organization_id=".$organization_id." and branch=".$branch);
		} else {
			$data = array('person_id' => $pid, 
			              'prod_code' => $prod_code,
			              'organization_id' => $organization_id,
			              'branch' => $branch
			              );
			$res = $db->table('epos_favorites')->insert($data);
		}
		return true;
	}

	function load_favorites($search0, $search1, $search2, $user_info, $limit = 30, $offset = 0, $sort_key = 6, $category_id = 0)
	{
		$db = \Config\Database::connect();
		
		$branch = session()->get('branch');
		$organization_id = session()->get('organization_id');		
		
		
		$favorites_list = '';
		$query0 = "SELECT * FROM epos_favorites WHERE person_id='" . $user_info->person_id . "' and organization_id=".$organization_id." and branch=".$branch;
		//$query0 = "SELECT * FROM epos_favorites WHERE person_id='" . $user_info->person_id . "'".$branch;
		$result_favorites = $db->query($query0);
		$nCount = 0;

		foreach ($result_favorites->getResult() as $res_favorites) {
			if ($nCount == 0) {
				$favorites_list .= "p.prod_code='" . $res_favorites->prod_code . "'";
			} else {
				$favorites_list .= " OR p.prod_code='" . $res_favorites->prod_code . "'";
			}
			$nCount++;
		}
		if ($favorites_list == '') {
			$favorites_list .= " p.prod_code!='' ";
		}

		$porformula = ", (CASE WHEN (prod_rrp * prod_uos > 0) 
							   THEN ROUND((1-((prod_sell * (1.00 + (CASE WHEN vat_code='A' THEN 0.2 WHEN vat_code='C' THEN 0.05 ELSE 0 END))) / (prod_rrp * prod_uos)))*100, 1)
							   ELSE 0 
						 END)
						 AS por";
		$query = "SELECT prod_id, p.prod_code, prod_uos, prod_desc, prod_pack_desc, price_start, price_end, brand, epoints,vat_code, prod_price, group_desc,
					prod_code1, price_list, prod_level1, prod_level2, prod_level3,
					pi.url as image_url, pi.version as image_version,
                    prod_sell, prod_rrp, wholesale, retail, p_size, is_disabled, promo, van, shelf_life" . $porformula . "
                FROM epos_product as p
				LEFT JOIN epos_product_images as pi on CAST(SUBSTRING(p.prod_code, 2, 6) AS UNSIGNED)=pi.prod_code 
                WHERE (" . $favorites_list . ") ";

		switch ($sort_key) {
			case 1:
				$query .= "GROUP BY p.prod_code ORDER BY p.prod_code ASC ";
				break;
			case 2:
				$query .= "GROUP BY p.prod_code ORDER BY p.prod_code DESC ";
				break;
			case 3:
				$query .= "GROUP BY p.prod_code ORDER BY p.prod_desc ASC ";
				break;
			case 4:
				$query .= "GROUP BY p.prod_code ORDER BY p.prod_desc DESC ";
				break;
			case 5:
				$query .= "GROUP BY p.prod_code ORDER BY p.por ASC ";
				break;
			case 6:
				$query .= "GROUP BY p.prod_code ORDER BY p.por DESC ";
				break;
			case 7:
				$query .= "GROUP BY p.prod_code ORDER BY p.prod_sell ASC ";
				break;
			case 8:
				$query .= "GROUP BY p.prod_code ORDER BY p.prod_sell DESC ";
				break;
			case 9:
				$query .= "GROUP BY p.prod_code ORDER BY p.prod_pack_desc ASC ";
				break;
			case 10:
				$query .= "GROUP BY p.prod_code ORDER BY p.prod_pack_desc DESC ";
				break;
			case 11:
				$query .= "GROUP BY p.prod_code ORDER BY p.prod_uos ASC ";
				break;
			case 12:
				$query .= "GROUP BY p.prod_code ORDER BY p.prod_uos DESC ";
				break;
			default:
				$query .= "GROUP BY p.prod_code ORDER BY p.prod_code DESC ";
				break;
		}

		if (intval($offset) < 1)
			$offset = 0;
		$query .= 'LIMIT ' . $offset . ',' . $limit;

		//$query1 = "SELECT * FROM epos_product LIMIT 0,200";
		$results = $db->query($query);
		if ($nCount > 0) {
			return $results;
		} else {
			return '0';
		}
		//return $results;
	}

	function get_ftp_location()
	{
		return;
	}

	public static function get_brands($user_info, $filter)
	{
		$db = \Config\Database::connect();
		$branch = session()->get('branch');
		$organization_id = session()->get('organization_id');	
		$cond = self::buildCondition($user_info, $filter);
		$query = "SELECT p.brand FROM epos_product as p ";
		if (!empty($user_info) && !empty($filter['favorite'])) {
			$query .= " INNER JOIN epos_favorites as ef on (ef.prod_code = p.prod_code and ef.person_id=" . $user_info->person_id . " and ef.organization_id=".$organization_id." and ef.branch=".$branch." ) ";
		}
		$query .= " WHERE " . $cond . " AND p.brand !='' ";
		$query .= " GROUP BY p.brand";

		$query = $db->query($query);
		$brands = [];
		foreach ($query->getResult() as $row) {
			$brands[] = $row->brand;
		}
		return $brands;
	}

	public static function get_priceEnds($user_info, $filter)
	{
		$db = \Config\Database::connect();
		$branch = session()->get('branch');
		$organization_id = session()->get('organization_id');	
		
		$cond = self::buildCondition($user_info, $filter);
		$query = "SELECT price_end FROM epos_product as p ";
		if (!empty($user_info) && !empty($filter['favorite'])) {
			$query .= " INNER JOIN epos_favorites as ef on (ef.prod_code = p.prod_code and ef.person_id=" . $user_info->person_id . " and ef.organization_id=".$organization_id." and ef.branch=".$branch." ) ";
		}
		$query .= " WHERE " . $cond;
		$query .= " GROUP BY price_end ORDER BY price_end";

		$query = $db->query($query);
		$priceEnds = [];
		foreach ($query->getResult() as $row) {
			$priceEnds[] = $row->price_end;
		}
		return $priceEnds;
	}

	public static function getActivePriceListIds($priceLists)
	{
		$db = \Config\Database::connect();
		$branch = session()->get('branch');
		$organization_id = session()->get('organization_id');

		$placeholders = implode(',', array_fill(0, count($priceLists), '?'));
		$query = "SELECT DISTINCT price_list FROM epos_product WHERE price_list IN ({$placeholders}) AND promo = 'Y' AND prod_sell > 0 AND price_start <= UNIX_TIMESTAMP() AND (price_end + 86399) >= UNIX_TIMESTAMP() AND is_disabled = 'N'";
		if (!empty($branch)) $query .= " AND branch = {$branch}";
		if (!empty($organization_id)) $query .= " AND organization_id = {$organization_id}";

		$result = $db->query($query, $priceLists);
		$ids = [];
		foreach ($result->getResult() as $row) {
			$ids[] = $row->price_list;
		}
		return $ids;
	}

	public static function populate(&$product, $priceList, $user_info, $spresell=0)
	{
		self::getAvailable($product);

		if(!empty($user_info)) {
			// Use pre-fetched batch data if available, otherwise query individually
			$product->cart_quantity = isset($product->_cart_quantity)
				? $product->_cart_quantity
				: self::get_cart_quantity($product->prod_code, $user_info->person_id, $spresell);

			$product->favorite = isset($product->_favorite_state)
				? $product->_favorite_state
				: self::get_favorite_state($user_info->person_id, $product->prod_code);
		}

		$product->p_label = '';
		$product->ribbon_background = '';
		$product->promo_end_text = '';
		if ($product->promo=='Y') {
			self::getPriceInfo($product, $priceList);
		} else {
			$product->price = number_format($product->prod_sell,2,'.','');
		}

		$product->non_promo_price = number_format($product->non_promo_sell_price,2,'.','');
		
		if($product->non_promo_price == 0 || $product->price == $product->non_promo_price) {
			$product->is_show_non_promo_price = false;
		} else {
			$product->is_show_non_promo_price = true;
		}

		$product->case = self::getCase($product);
	}

	public static function buildCondition($user_info, $filter)
	{
		try {
			$db = \Config\Database::connect();    
      		$organization_id = session()->get('organization_id');
		    $branch = session()->get('branch');
			$cond = " is_disabled='N' ";
			// category_id
			if (!empty($filter['category_id'])) {
				$CmsItem = new CmsItem();
				$cms_items = $CmsItem->getCmsItemsByType('category_carousel');

				$prod_codes = [];
				foreach($cms_items as $cms_itm) {
					$str_top_cat_id = $cms_itm['data']['top_cat_id'];
					$str_sub_cat_id = $cms_itm['data']['sub_cat_id'];

					if ($str_top_cat_id == $filter['category_id'] || $str_sub_cat_id == $filter['category_id']) {
						// if($cms_itm->data->brand == $filter['brand']) {
						$prod_codes[] = $cms_itm['prod_codes'];
					}
				}

				$query = "SELECT * FROM epos_categories WHERE category_id='" . $filter['category_id'] . "' ";
				if (!empty($organization_id)) {
					$query .= "AND organization_id={$organization_id} ";
				}
				$results_category = $db->query($query);
				$res_category = $results_category->getRow();
				if ($res_category->parent_id == 0) {
					$query = "SELECT * FROM epos_categories WHERE parent_id='" . $res_category->category_id . "' ";
					if (!empty($organization_id)) {
						$query .= "AND organization_id={$organization_id} ";
					}
					$results_subcategory = $db->query($query);
					$nCount = 0;
					foreach ($results_subcategory->getResult() as $res_subcategory) {
						if ($nCount == 0) {
							$cond .= "AND (group_desc = '" . $res_subcategory->filter_desc . "' ";
						} else {
							$cond .= "OR group_desc = '" . $res_subcategory->filter_desc . "' ";
						}
						$nCount++;
					}
					if (implode(',', $prod_codes)) {
						$cond .= "OR p.prod_code in (" . implode(',', $prod_codes) . ") ";
					}
					$cond .= ") ";
				} else {
					$cond .= "AND (group_desc = '" . $res_category->filter_desc . "' ";
					if (implode(',', $prod_codes)) {
						$cond .= "OR p.prod_code in (" . implode(',', $prod_codes) . ") ";
					}
					$cond .= ") ";
				}
			}
			// im_new 
			if (!empty($filter['im_new'])) {
				$cond .= "AND availability = 'N' ";
			}
			// plan_profit
			if (!empty($filter['plan_profit'])) {
				$cond .= "AND pfp = '1' ";
			}
			// rrp
			if (!empty($filter['rrp'])) {
				$cond .= "AND prod_rrp = 1.00 ";
			}
			// pmp
			if (!empty($filter['pmp'])) {
				$cond .= "AND meta Like '%PMP%' ";
			}
			// non_pmp
			if (!empty($filter['non_pmp'])) {
				$cond .= "AND meta NOT Like '%PMP%' ";
			}
			// own_label
			if (!empty($filter['own_label'])) {
				$cond .= "AND own_label='Y' ";
			}
			// brand
			if (!empty($filter['brand'])) {
				$brand_arr = json_decode($filter['brand'], true);

				$CmsItem = new CmsItem();
				$cms_items = $CmsItem->getCmsItemsByType('brand');

				$prod_codes = [];
				foreach($cms_items as $cms_itm) {
					$str_brand = $cms_itm['data']['brand'];
					if (in_array($str_brand, $brand_arr, true)) {
						// if($cms_itm->data->brand == $filter['brand']) {
						$prod_codes[] = $cms_itm['prod_codes'];
					}
				}
				$brand = $filter['brand'];
				$cond .= "AND (brand in (" . substr($brand, 1, strlen($brand) - 2) . ") ";
				if (implode(',', $prod_codes)) {
					$cond .= "OR p.prod_code in (" . implode(',', $prod_codes) . ") ";
				}
				$cond .= ") ";
			}
			// price_end
			if (!empty($filter['priceEnd'])) {
				$cond .= "AND price_end in (" . substr($filter['priceEnd'], 1, strlen($filter['priceEnd']) - 2) . ") ";
			}
			// price mode
			$codes = !empty($user_info) ? ($user_info->price_list_codes ?? []) : [];
			$priceListCond = '';
			if (!empty($filter['spresell'])) {
				$cond .= "AND price_list = '06' ";
				$priceListCond = "price_list = '06'";
				if (!empty($filter['promotion_id'])) {
					$promotion_id = $db->escape($filter['promotion_id']);
					$cond .= "AND p.promotion_id = {$promotion_id} ";
				}
			} else {
				$cond .= "AND (price_list = '99999' ";
				$priceListCond = "price_list = '99999' ";
				if (!empty($user_info)) {
					// price_mode can be an array of specific price list IDs (from promo multi-select)
					$priceModeFilter = $filter['price_mode'] ?? null;
					foreach ($codes as $c) {
						if (empty($priceModeFilter) || (is_array($priceModeFilter) && in_array($c, $priceModeFilter))) {
							$cond .= "OR price_list = '{$c}' ";
							$priceListCond .= "OR price_list = '{$c}' ";
						}
					}
					// In collection mode, include user's assigned collection price lists
					if (empty($priceModeFilter) && session()->get('order_mode') === 'collection') {
						$collectionLists = PriceList::getCollectionListIds();
						foreach ($collectionLists as $cl) {
							if (in_array($cl, $codes)) {
								$cond .= "OR price_list = '{$cl}' ";
								$priceListCond .= "OR price_list = '{$cl}' ";
							}
						}
					}
				} else {
					$cond .= "OR price_list = '999' ";
					$priceListCond = "price_list = '999'";
				}
				$cond .= ") ";
			}
			// Select all fields from the row with the cheapest price per prod_code
			$subBranchFilter = !empty($branch) ? "AND p2.branch={$branch} " : "";
			$subOrgFilter = !empty($organization_id) ? "AND p2.organization_id={$organization_id} " : "";
			$cond .= "AND p.prod_id = (SELECT p2.prod_id FROM epos_product p2 WHERE p2.prod_code = p.prod_code {$subBranchFilter}{$subOrgFilter}AND p2.is_disabled='N' AND ({$priceListCond}) AND p2.prod_sell > 0 AND p2.price_start <= UNIX_TIMESTAMP() AND (p2.price_end + 86399) >= UNIX_TIMESTAMP() ORDER BY p2.prod_sell ASC, p2.prod_id ASC LIMIT 1) ";
			// presell
			if (!empty($filter['presell'])) {
				$cond .= "AND ordered=0 ";
			}
			// promo
			if (!empty($filter['promo'])) {
				$cond .= "AND promo='Y' ";
			}
			// branch
			$branch = session()->get('branch');
			if (!empty($branch)) {
				$cond .= "AND p.branch={$branch} ";
			}
      		// organization_id
			$organization_id = session()->get('organization_id');
			if (!empty($organization_id)) {
				$cond .= "AND p.organization_id={$organization_id} ";
			}
			// search0
			if (!empty($filter['search0'])) {
				$search0 = urldecode($filter['search0']);
				try {
					$cond .= "AND (";
					$arr0 = preg_split("/\,/", $search0);
					foreach ($arr0 as $index0 => $a0) {
						if ($index0 > 0)
							$cond .= " OR ";
						$cond .= "( ";
						//------------
						$a0 = trim($a0);
						$arr = array_values(array_filter(preg_split("/\ /", $a0), fn($v) => $v !== ''));
						foreach ($arr as $index => $a) {
							$keyword = $a;
							if (strpos($keyword, '%') !== false) {
								$keyword = str_replace(['%', '_'], ['\%', '\_'], $keyword);
							} else {
								$keyword = $db->escapeLikeString($a);
							}
							if ($index > 0)
								$cond .= " AND ";
							$cond .= "( ";
							// original search engine...
								// $cond .= "p.prod_code LIKE '%" . $keyword . "' ";
								// $cond .= "OR retail LIKE '" . 	$keyword . "%' ";
								// $cond .= "OR wholesale LIKE '" . $keyword . "%' ";
								// $cond .= "OR prod_desc LIKE '%". $keyword . "%' ";
								// $cond .= "OR brand LIKE '%" . 	$keyword . "%' ";
							// new search engine, holla.ardy
							$cond .= "p.prod_code LIKE '%" . 		$keyword . "' ";		
							//----------
							$cond .= "OR retail LIKE '" . 			$keyword . "%' ";			
							//----------
							$cond .= "OR wholesale LIKE '" . 		$keyword . "%' ";		
							//----------
							$cond .= "OR prod_desc LIKE '% " . 		$keyword . "' ";		
							$cond .= "OR prod_desc LIKE '" . 		$keyword . " %' ";
							$cond .= "OR prod_desc LIKE '% " . 		$keyword . " %' ";
							$cond .= "OR prod_desc = '" . 			$keyword . "' ";
							//----------
							$cond .= "OR prod_pack_desc LIKE '%" . 	$keyword . "%' ";
							//----------
							$cond .= "OR brand LIKE '%" . 			$keyword . "%' ";
							$cond .= ") ";
						}
						//------------
						$cond .= ") ";
					}
					$cond .= ") ";
				} catch (\Exception $e) {

				}
			}
			// search1
			if (!empty($filter['search1'])) {
				$search0 = urldecode($filter['search1']);
				try {
					$cond .= "AND (";
					$arr0 = preg_split("/\,/", $search0);
					foreach ($arr0 as $index0 => $a0) {
						if ($index0 > 0)
							$cond .= " OR ";
						$cond .= "( ";
						//------------
						$a0 = trim($a0);
						$arr = array_values(array_filter(preg_split("/\ /", $a0), fn($v) => $v !== ''));
						foreach ($arr as $index => $a) {
							$keyword = $a;
							if (strpos($keyword, '%') !== false) {
								$keyword = str_replace(['%', '_'], ['\%', '\_'], $keyword);
							} else {
								$keyword = $db->escapeLikeString($a);
							}
							if ($index > 0)
								$cond .= " AND ";
							$cond .= "( ";
							// original search engine...
								// $cond .= "p.prod_code LIKE '%" . $keyword . "' ";
								// $cond .= "OR retail LIKE '" . 	$keyword . "%' ";
								// $cond .= "OR wholesale LIKE '" . $keyword . "%' ";
								// $cond .= "OR prod_desc LIKE '%". $keyword . "%' ";
								// $cond .= "OR brand LIKE '%" . 	$keyword . "%' ";
							// new search engine, holla.ardy
							$cond .= "p.prod_code LIKE '%" . 		$keyword . "' ";		
							//----------
							$cond .= "OR retail LIKE '" . 			$keyword . "%' ";			
							//----------
							$cond .= "OR wholesale LIKE '" . 		$keyword . "%' ";		
							//----------
							$cond .= "OR prod_desc LIKE '% " . 		$keyword . "' ";		
							$cond .= "OR prod_desc LIKE '" . 		$keyword . " %' ";
							$cond .= "OR prod_desc LIKE '% " . 		$keyword . " %' ";
							$cond .= "OR prod_desc = '" . 			$keyword . "' ";
							//----------
							$cond .= "OR prod_pack_desc LIKE '%" . 	$keyword . "%' ";
							//----------
							$cond .= "OR brand LIKE '%" . 			$keyword . "%' ";
							$cond .= ") ";
						}
						//------------
						$cond .= ") ";
					}
					$cond .= ") ";
				} catch (\Exception $e) {

				}
			}
			// search3
			if (!empty($filter['search3']) && $filter['search3'] != 'null') {
				$search0 = urldecode($filter['search3']);
				try {
					$cond .= "AND (";
					$arr0 = preg_split("/\,/", $search0);
					foreach ($arr0 as $index0 => $a0) {
						if ($index0 > 0)
							$cond .= " OR ";
						$cond .= "( ";
						//------------
						$a0 = trim($a0);
						$arr = array_values(array_filter(preg_split("/\ /", $a0), fn($v) => $v !== ''));
						foreach ($arr as $index => $a) {
							$keyword = $a;
							if (strpos($keyword, '%') !== false) {
								$keyword = str_replace(['%', '_'], ['\%', '\_'], $keyword);
							} else {
								$keyword = $db->escapeLikeString($a);
							}
							if ($index > 0)
								$cond .= " AND ";
							$cond .= "( ";
							// original search engine...
								// $cond .= "p.prod_code LIKE '%" . $keyword . "' ";
								// $cond .= "OR retail LIKE '" . 	$keyword . "%' ";
								// $cond .= "OR wholesale LIKE '" . $keyword . "%' ";
								// $cond .= "OR prod_desc LIKE '%". $keyword . "%' ";
								// $cond .= "OR brand LIKE '%" . 	$keyword . "%' ";
							// new search engine, holla.ardy
							$cond .= "p.prod_code LIKE '%" . 		$keyword . "' ";		
							//----------
							$cond .= "OR retail LIKE '" . 			$keyword . "%' ";			
							//----------
							$cond .= "OR wholesale LIKE '" . 		$keyword . "%' ";		
							//----------
							$cond .= "OR prod_desc LIKE '% " . 		$keyword . "' ";		
							$cond .= "OR prod_desc LIKE '" . 		$keyword . " %' ";
							$cond .= "OR prod_desc LIKE '% " . 		$keyword . " %' ";
							$cond .= "OR prod_desc = '" . 			$keyword . "' ";
							//----------
							$cond .= "OR prod_pack_desc LIKE '%" . 	$keyword . "%' ";
							//----------
							$cond .= "OR brand LIKE '%" . 			$keyword . "%' ";
							$cond .= ") ";
						}
						//------------
						$cond .= ") ";
					}
					$cond .= ") ";
				} catch (\Exception $e) {

				}
			}
			// cms_prod_codes — direct product code list from cms_item
			if (!empty($filter['cms_prod_codes'])) {
				$codes = array_map('trim', explode(',', $filter['cms_prod_codes']));
				$escaped = implode(',', array_map(function($c) use ($db) { return $db->escape($c); }, $codes));
				$cond .= "AND p.prod_code IN ({$escaped}) ";
			}
			$cond .= "AND prod_sell > 0 ";
			$cond .= "AND price_start <= UNIX_TIMESTAMP() AND (price_end + 86399) >= UNIX_TIMESTAMP() ";

			return $cond;
		} catch (\Exception $e) {
			return " is_disabled='N' ";
		}
	}

	public static function getPriceInfo(&$product, $priceList)
	{
		$ribbon_background = '';
		$p_label = '';
		$promo_end_text = '';

		if (isset($priceList[$product->price_list]) && !empty($priceList[$product->price_list]['ribbon_label'])) {
			$ribbon_background = "#{$priceList[$product->price_list]['ribbon_colour']}";
			$p_label = $priceList[$product->price_list]['ribbon_label'];
		} else {
			$ribbon_background = "#b4e9f8";
			$p_label = "CC";
		}

		$time = $product->price_end + ((23 * 3600) + 3599);
		if (time() >= $product->price_start && time() <= $time) {
			$product->price = number_format($product->prod_sell, 2, '.', '');
			$promo_end_text = 'Offer Ends ' . date('d/m', $product->price_end);
		} else {
			$product->price = 0;
			$promo_end_text = 'Offer Ended';
		}

		$product->ribbon_background = $ribbon_background;
		$product->p_label = $p_label;
		$product->promo_end_text = $promo_end_text;
	}

	public static function getAvailable(&$product)
	{
		$icon_name = '';
		$icon_title = '';
		$prod_avail = true;

		if (isset($product->availability) && $product->availability != "") {
			if ($product->availability == "C") {
				$icon_name = "coming-soon";
				$icon_title = "Coming Soon";
				$prod_avail = false;
			} else if ($product->availability == "N") {
				$icon_name = "new-item";
				$icon_title = "New Item";
				$prod_avail = true;
			} else if ($product->availability == "L") {
				$icon_name = "low-stock";
				$icon_title = "Low Stock";
				$prod_avail = true;
			} else if ($product->availability == "O") {
				$icon_name = "out-of-stock";
				$icon_title = "Out Of Stock";
				$prod_avail = false;
			} else if ($product->availability == "T") {
				$icon_name = "best-seller";
				$icon_title = "Best Seller";
				$prod_avail = true;
			}
		}

		if ($product->price_list == PriceList['Season_Presell']) {
			$prod_avail = false;
			$icon_name = "coming-soon";
			$icon_title = "Coming Soon";
		}

		$product->available = [
			'avail' => $prod_avail,
			'icon_name' => $icon_name,
			'icon_title' => $icon_title,
		];
	}

	public static function getLowestPriceProductByCode($user_info, $prod_code, $excludeDisabled = true, $spresell = 0, $branch = null, $organization_id = null)
	{
		$branch = $branch ? $branch : session()->get('branch');
		$organization_id = $organization_id ? $organization_id : session()->get('organization_id');
		$db = \Config\Database::connect();

		$query = "SELECT
						p.*, type,
						(CASE WHEN (prod_rrp * prod_uos > 0)
							THEN ROUND((1-((prod_sell * (1.00 + (CASE WHEN vat_code='A' THEN 0.2 WHEN vat_code='C' THEN 0.05 ELSE 0 END))) / (prod_rrp * prod_uos)))*100, 1)
							ELSE 0
						END)  AS por,
						prod_sell,
						price_start, price_end,
						pi.url as image_url,
						pi.version as image_version,
						vat.rate as vat_rate
          		  FROM epos_product as p ";
		$query.= "LEFT JOIN epos_product_images as pi on CAST(SUBSTRING(p.prod_code, 2, 6) AS UNSIGNED)=pi.prod_code ";
    	$query.= "LEFT JOIN epos_vat as vat on vat.code=p.vat_code ";
		$query.= "LEFT JOIN epos_categories as ct on p.group_desc = ct.filter_desc ";
		$query.= "WHERE p.branch={$branch} ";
    	if (!empty($organization_id)) {
    		$query.= "AND p.organization_id={$organization_id} ";
		}
		$query.= "AND p.prod_code={$prod_code} ";
		$query.= $spresell ?
				 "AND price_list='06' " : " AND price_list!='06' ";
		if ($excludeDisabled) {
			$query.= "AND is_disabled='N' ";
		}
		$plCond = "price_list = '99999' ";
		if (!empty($user_info)) {
			$codes = $user_info->price_list_codes ?? [];
			foreach ($codes as $code) {
				if ($code === '10') {
					$plCond .= "OR (price_list = '10' || price_list='06') ";
				} else {
					$plCond .= "OR price_list = '{$code}' ";
				}
			}
		} else {
			$plCond .= " OR price_list='999'";
		}
		$query.= "AND ({$plCond}) ";
		$query.= "AND prod_sell > 0 ";
		$query.= "AND price_start <= UNIX_TIMESTAMP() AND (price_end + 86399) >= UNIX_TIMESTAMP() ";
		$subOrgFilter = !empty($organization_id) ? "AND p2.organization_id={$organization_id} " : "";
		$subDisabledFilter = $excludeDisabled ? "AND p2.is_disabled='N' " : "";
		$query.= "AND p.prod_id = (SELECT p2.prod_id FROM epos_product p2 WHERE p2.prod_code = p.prod_code AND p2.branch={$branch} {$subOrgFilter}{$subDisabledFilter}AND ({$plCond}) AND p2.prod_sell > 0 AND p2.price_start <= UNIX_TIMESTAMP() AND (p2.price_end + 86399) >= UNIX_TIMESTAMP() ORDER BY p2.prod_sell ASC, p2.prod_id ASC LIMIT 1) ";
		$query.= "GROUP BY p.prod_code ";

		$query = $db->query($query);
		return $query->getNumRows() > 0 ? $query->getRow() : null;
	}

	public static function getLowestPriceProductsByCodes($user_info, $prod_codes, $excludeDisabled = true, $spresell = 0, $branch = null, $organization_id = null)
	{
		if (empty($prod_codes)) return [];

		$branch = $branch ? $branch : session()->get('branch');
		$organization_id = $organization_id ? $organization_id : session()->get('organization_id');
		$db = \Config\Database::connect();

		$escaped_codes = implode(',', array_map(function($code) use ($db) {
			return $db->escape(trim($code));
		}, $prod_codes));

		$query = "SELECT
						p.*, type,
						(CASE WHEN (prod_rrp * prod_uos > 0)
							THEN ROUND((1-((prod_sell * (1.00 + (CASE WHEN vat_code='A' THEN 0.2 WHEN vat_code='C' THEN 0.05 ELSE 0 END))) / (prod_rrp * prod_uos)))*100, 1)
							ELSE 0
						END)  AS por,
						prod_sell,
						price_start, price_end,
						pi.url as image_url,
						pi.version as image_version,
						vat.rate as vat_rate
          		  FROM epos_product as p ";
		$query.= "LEFT JOIN epos_product_images as pi on CAST(SUBSTRING(p.prod_code, 2, 6) AS UNSIGNED)=pi.prod_code ";
    	$query.= "LEFT JOIN epos_vat as vat on vat.code=p.vat_code ";
		$query.= "LEFT JOIN epos_categories as ct on p.group_desc = ct.filter_desc ";
		$query.= "WHERE p.branch={$branch} ";
    	if (!empty($organization_id)) {
    		$query.= "AND p.organization_id={$organization_id} ";
		}
		$query.= "AND p.prod_code IN ({$escaped_codes}) ";
		$query.= $spresell ?
				 "AND price_list='06' " : " AND price_list!='06' ";
		if ($excludeDisabled) {
			$query.= "AND is_disabled='N' ";
		}
		$plCond = "price_list = '99999' ";
		if (!empty($user_info)) {
			$codes = $user_info->price_list_codes ?? [];
			foreach ($codes as $code) {
				if ($code === '10') {
					$plCond .= "OR (price_list = '10' || price_list='06') ";
				} else {
					$plCond .= "OR price_list = '{$code}' ";
				}
			}
		} else {
			$plCond .= " OR price_list='999'";
		}
		$query.= "AND ({$plCond}) ";
		$query.= "AND prod_sell > 0 ";
		$query.= "AND price_start <= UNIX_TIMESTAMP() AND (price_end + 86399) >= UNIX_TIMESTAMP() ";
		$subOrgFilter = !empty($organization_id) ? "AND p2.organization_id={$organization_id} " : "";
		$subDisabledFilter = $excludeDisabled ? "AND p2.is_disabled='N' " : "";
		$query.= "AND p.prod_id = (SELECT p2.prod_id FROM epos_product p2 WHERE p2.prod_code = p.prod_code AND p2.branch={$branch} {$subOrgFilter}{$subDisabledFilter}AND ({$plCond}) AND p2.prod_sell > 0 AND p2.price_start <= UNIX_TIMESTAMP() AND (p2.price_end + 86399) >= UNIX_TIMESTAMP() ORDER BY p2.prod_sell ASC, p2.prod_id ASC LIMIT 1) ";
		$query.= "GROUP BY p.prod_code ";

		$result = $db->query($query);
		$products = [];
		foreach ($result->getResult() as $row) {
			$products[$row->prod_code] = $row;
		}
		return $products;
	}

	public static function getProductsByDynamicFilters($user_info, $filters, $limit = 20)
	{
		// Step 1: figure out which prod_codes belong in this carousel. We honour
		// the CMS-configured business filters (promotion_id, brand, category,
		// etc.) as MEMBERSHIP rules — a product qualifies if any of its
		// epos_product rows match. The variant actually displayed is chosen in
		// step 2 by the same lowest-price logic the detail / cart / checkout
		// pages use, so the carousel card matches every other place the
		// customer sees the product.
		$codes = self::getProdCodesMatchingFilters($filters, $limit);
		if (empty($codes)) return [];

		// Step 2: pick the customer's cheapest available variant for each code.
		$products_map = self::getLowestPriceProductsByCodes($user_info, $codes);
		if (empty($products_map)) return [];

		// Apply the requested sort on the displayed variants (PHP-side so the
		// order reflects the variant we actually return, not the one step 1
		// matched against).
		$sortBy  = $filters['sort_by'] ?? '';
		$sortFn  = self::makeSortComparator($sortBy);
		if ($sortFn) {
			uasort($products_map, $sortFn);
		}

		if (count($products_map) > $limit) {
			$products_map = array_slice($products_map, 0, $limit, true);
		}

		return $products_map;
	}

	private static function getProdCodesMatchingFilters($filters, $limit)
	{
		$branch          = session()->get('branch');
		$organization_id = session()->get('organization_id');
		$db              = \Config\Database::connect();

		$query  = "SELECT DISTINCT p.prod_code FROM epos_product p WHERE p.branch={$branch} ";
		if (!empty($organization_id)) {
			$query .= "AND p.organization_id={$organization_id} ";
		}
		$query .= "AND p.is_disabled='N' ";
		$query .= "AND p.prod_sell > 0 ";
		$query .= "AND p.price_start <= UNIX_TIMESTAMP() AND (p.price_end + 86399) >= UNIX_TIMESTAMP() ";

		if (!empty($filters['brands'])) {
			$escaped = implode(',', array_map(fn($b) => $db->escape($b), $filters['brands']));
			$query  .= "AND p.brand IN ({$escaped}) ";
		}
		if (!empty($filters['categories'])) {
			$escaped = implode(',', array_map(fn($c) => $db->escape($c), $filters['categories']));
			$query  .= "AND p.group_desc IN ({$escaped}) ";
		}
		if (!empty($filters['filter_price_lists'])) {
			$escaped = implode(',', array_map(fn($p) => $db->escape($p), $filters['filter_price_lists']));
			$query  .= "AND p.price_list IN ({$escaped}) ";
		}
		if (!empty($filters['promotion_id'])) {
			$escaped = $db->escape($filters['promotion_id']);
			$query  .= "AND p.promotion_id = {$escaped} ";
		}
		if (!empty($filters['availability'])) {
			$escaped = implode(',', array_map(fn($a) => $db->escape($a), $filters['availability']));
			$query  .= "AND p.availability IN ({$escaped}) ";
		}
		if (!empty($filters['only_promo'])) {
			$query .= "AND p.promo = 'Y' ";
		}
		if (!empty($filters['only_items'])) {
			$query .= "AND p.prod_uos = 1 ";
		}

		// Over-fetch so the post-step-2 sort/limit has room to work without
		// truncating before sorting. 5x is plenty for typical carousels.
		$fetchLimit = max((int) $limit * 5, 100);
		$query .= "ORDER BY p.prod_code ASC LIMIT {$fetchLimit}";

		$result = $db->query($query);
		$codes  = [];
		foreach ($result->getResult() as $row) {
			$codes[] = $row->prod_code;
		}
		return $codes;
	}

	private static function makeSortComparator($sortBy)
	{
		switch ($sortBy) {
			case 'price_asc':       return fn($a, $b) => (float) $a->prod_sell <=> (float) $b->prod_sell;
			case 'price_desc':      return fn($a, $b) => (float) $b->prod_sell <=> (float) $a->prod_sell;
			case 'description_asc': return fn($a, $b) => strcasecmp($a->prod_desc ?? '', $b->prod_desc ?? '');
			case 'description_desc':return fn($a, $b) => strcasecmp($b->prod_desc ?? '', $a->prod_desc ?? '');
			case 'por_asc':         return fn($a, $b) => (float) $a->por <=> (float) $b->por;
			case 'por_desc':        return fn($a, $b) => (float) $b->por <=> (float) $a->por;
			case 'brand_asc':       return fn($a, $b) => strcasecmp($a->brand ?? '', $b->brand ?? '');
			case 'brand_desc':      return fn($a, $b) => strcasecmp($b->brand ?? '', $a->brand ?? '');
			case 'best_selling':    return fn($a, $b) => (float) $b->prod_sell <=> (float) $a->prod_sell;
			default:                return null;
		}
	}

	public static function getCase($product)
	{
		$case = '';

		if (empty($product)) return '';

		if ($product->prod_uos > 1) {
			$case = 'CASE';
		}

		if (
			$product->prod_uos > 1 && $product->prod_uos < 7 && $product->group_desc == 'CIGS CIGARS TOBACCO'
			&& substr($product->prod_pack_desc, -1) != 'G' && substr($product->prod_pack_desc, -1) != 'g'
			&& substr($product->prod_pack_desc, -2) != 'gm' && substr($product->prod_pack_desc, -2) != 'GM'
		) {
			$case = 'HALF';
		}

		return $case;
	}

  public static function genCartLinePosition($person_id, $tblName = 'epos_cart')
  {
    $db = \Config\Database::connect();
    $line_position = 0;

    $query = "SELECT line_position FROM " . $tblName . " WHERE person_id=" . $person_id . " ORDER BY line_position DESC LIMIT 1";
    $query = $db->query($query);
    if ($query->getNumRows() > 0) {
        $row = $query->getRow();
        $line_position = intval($row->line_position) + 1;
    }

    return $line_position;
  }
}


?>