<?php
namespace Models;

// Модель товаров
class Products extends \DB\SQL\Mapper {
	public $db, $f3;

	public $units=array(
		796=>array('title'=>'Штуки, единицы', 'number'=>0),
		163=>array('title'=>'Грамм', 'number'=>10),
		166=>array('title'=>'Килограмм', 'number'=>11),
		168=>array('title'=>'Тонна', 'number'=>12),
		4=>array('title'=>'Сантиметр', 'number'=>20),
		5=>array('title'=>'Дециметр', 'number'=>21),
		6=>array('title'=>'Метр', 'number'=>22),
		51=>array('title'=>'Квадратный сантиметр', 'number'=>30),
		53=>array('title'=>'Квадратный дециметр', 'number'=>31),
		55=>array('title'=>'Квадратный метр', 'number'=>32),
		111=>array('title'=>'Миллилитр', 'number'=>40),
		112=>array('title'=>'Литр', 'number'=>41),
		113=>array('title'=>'Кубический метр', 'number'=>42),
		245=>array('title'=>'Киловатт час', 'number'=>50),
		233=>array('title'=>'Гигакалория', 'number'=>51),
		359=>array('title'=>'Сутки (день)', 'number'=>70),
		356=>array('title'=>'Час', 'number'=>71),
		355=>array('title'=>'Минута', 'number'=>72),
		354=>array('title'=>'Секунда', 'number'=>73),
		256=>array('title'=>'Килобайт', 'number'=>80),
		257=>array('title'=>'Мегабайт', 'number'=>81),
		2553=>array('title'=>'Гигабайт', 'number'=>82),
		2554=>array('title'=>'Терабайт', 'number'=>83),
		0=>array('title'=>'Иные единицы измерения', 'number'=>255),
	);

	public function __construct($db, $f3) {
		parent::__construct($db, 'products' ); // подключаемся к таблице товаров
		$this->db=$db;
		$this->f3=$f3;
	}

	// Информация об открываемой странице
	function page_data($page='sales') {
		$pages=array(
			'search_form'=>array(
				'title'=>'Поиск товара',
				'inc'=>'products_search_form.htm'
			),
			'search'=>array(
				'title'=>'Результаты поиска',
				'inc'=>'products_search.htm'
			),
			'fast_products'=>array(
				'title'=>'Быстрые товары',
				'inc'=>'products_search.htm'
			),
			'pricetags'=>array(
				'title'=>'Печать ценников',
				'inc'=>'products_pricetags.htm'
			),
			'img'=>array(
				'title'=>'Изображение товара',
				'inc'=>'products_img.htm'
			),
			'currentstock'=>array(
				'title'=>'Остатки на складе',
				'inc'=>'products_currentstock.htm'
			),
			'list'=>array(
				'title'=>'Категории и товары',
				'inc'=>'products_list.htm'
			),
			'not_found'=>array(
				'title'=>'Товар не найден',
				'inc'=>'products_not_found.htm'
			),
			'add'=>array(
				'title'=>'Добавить новую номенклатуру',
				'inc'=>'products_form.htm'
			),
			'change'=>array(
				'title'=>'Редактировать номенклатуру',
				'inc'=>'products_form.htm'
			),
			'directory'=>array(
				'title'=>'Справочник номенклатуры',
				'inc'=>'products_directory.htm'
			),
			'detail'=>array(
				'title'=>'Информация о товаре',
				'inc'=>'products_detail.htm'
			),
			'stoplist'=>array(
				'title'=>'Стоп-лист',
				'inc'=>'products_stoplist.htm'
			),
			'addstop'=>array(
				'title'=>'Добавить в стоп-лист',
				'inc'=>'products_addstop.htm'
			),
			'deletestop'=>array(
				'title'=>'Удалить из стоп-листа',
				'inc'=>'products_deletestop.htm'
			),
			'clearstop'=>array(
				'title'=>'Удалить все из стоп-листа',
				'inc'=>'products_clearstop.htm'
			),
			'searchstop'=>array(
				'title'=>'Поиск товаров для добавления в стоп-лист',
				'inc'=>'products_searchstop.htm'
			)
		);
		$pages[$page]['page']=$page;
		return $pages[$page];
	}

	function barcodes($product_id) {
		return $this->db->exec("SELECT * FROM barcodes WHERE product_id=?", $product_id);
	}

	// Выбор из базы
	function select_product($query=null, $options=null, $kiosk_ignore=0) {
		if(!$query) {
			$query=array('p.id>?', 0); // чтобы хоть что-то вставить в фильтры
		}

		// формируем сортировку и лимиты
		$option='';
		if($options['order']) {
			$option.=' ORDER BY '.$options['order'];
		}
		if($options['limit'] && !$options['offset']) {
			$option.=' LIMIT '.$options['limit'];
		}
		if($options['limit'] && $options['offset']) {
			$option.=' LIMIT '.$options['offset'].', '.$options['limit'];
		}
		if($this->f3->get('authorization_user.kiosk')==1 && !$kiosk_ignore) {
			if($query[0]!='') $query[0].=' AND ';
			$query[0].=' (c.kiosk_noshow=0 OR p.category_id=0)';
		}
		// собственно сам запрос
		$sql="SELECT p.*, c.title AS `category_title` FROM `products` AS `p` LEFT JOIN `productcategories` AS `c` ON c.id=p.category_id WHERE p.date_delete=0 AND ".$query[0].' '.$option;
		$res=$this->db->exec($sql, $query[1]);
		$products=array();
		if($res) {
			foreach($res as $r) {
				$r['barcodes']=$this->db->exec("SELECT barcode FROM barcodes WHERE product_id=?", $r['id']);
				$r['images']=json_decode($r['img'], true);
				$r['units_fr']=isset($this->units[$r['units']])?$this->units[$r['units']]['number']:255;
				$products[]=$r;
			}
		}
		return $products;
	}

	// Составление релевантного запроса
	function relevant($search0) {
		$search0=str_replace("'","\'",$search0);
		$search0=trim(htmlspecialchars($search0));
		$search=str_replace("'",'',$search0);
		$search=str_replace('"','',$search);
		// Разобрать искомую строку $search на отдельные слова
		preg_match_all('/[[:alnum:]]{1,}/isu', stripslashes($search), $matches);
		$words = array_unique($matches[0]);
 
		$true_words=Array();
		if (count($words)) {
    		foreach($words as $word) {
        		// Обрабатывать только слова длиннее 1 символов
        		if (strlen($word)>1) {
            		// От слов длиннее 7 символов отрезать 2 последних буквы
            		if (strlen($word)>7) {
                		$word=substr($word,0,(strlen($word)-2));
            		}
            		// От слов длиннее 5 символов отрезать последнюю букву
            		elseif (strlen($word)>5) {
                		$word=substr($word,0,(strlen($word)-1));
            		}
            		$true_words[]=addcslashes(addslashes($word),'%_');
        		}
    		}    
		}
		// Список уникальных поисковых слов
		$true_words=array_unique($true_words);
		if(count($true_words)==0) {
			return array("query"=>"", "where"=>"", "order"=>"title");
		}
		$coeff_title=round((20/count($true_words)),2);
 
		// Формируем запрос к базе
		// Условия для полного совпадения фразы в заголовке и тексте
		$query = ", (CASE WHEN p.title LIKE '%".$search."%' THEN 60 ELSE 0 END ";
		// Условия для каждого из слов
		foreach($true_words as $word) {
    		$query .= "+ CASE WHEN p.title LIKE '%".$word."%' THEN ".number_format($coeff_title, 2, '.', '')." ELSE 0 END ";
		}
		$query.=") AS relevant ";
 
		// Условие выборки - вхождение фразы или отдельных слов в заголовок или текст
		$where= " OR (";
		$where .= " p.title LIKE '%".$search0."%'";
		// Условия для каждого из слов
		foreach($true_words as $word) {
    		$where .= " OR p.title LIKE '%".$word."%'";
		}
		$where.=") ";
		return array("query"=>$query, "where"=>$where, "order"=>"relevant DESC");
	}

	// Быстрые товары
	function fast_products($start, $limit) {
		$count=0;
		$products=array();
		$limit_txt='';
		if($start>0 && $limit>0) {
			$limit_txt='LIMIT '.$start.', '.$limit;
		}
		elseif($limit>0) {
			$limit_txt='LIMIT '.$limit;
		}
		$sql="SELECT p.*, c.title AS category_title FROM products AS p LEFT JOIN productcategories AS c ON c.id=p.category_id WHERE p.fast>0 AND p.date_delete=0 ORDER BY title ".$limit_txt;
		$products=$this->db->exec($sql);
		$count=count($products);
		if($count>0) {
			foreach ($products as $i => $p) {
				$products[$i]['img']=json_decode($p['img'], true);
			}
		}
		if($limit_txt) {
			$sql="SELECT COUNT(*) AS count FROM products WHERE date_delete=0 AND fast>0";
			$res=$this->db->exec($sql);
			$count=$res[0]['count'];
		}
		return array(
			'success'=>true,
			'count'=>$count,
			'products'=>$products
		);
	}

	// поиск товара
	function search($term, $start=0, $limit=30, $barcodes_only=false, $exact_match=false, $in_stock=false, $order=false) {
		$term=mb_strtoupper($term);
		$limit_txt='';
		if($start>0 && $limit>0) {
			$limit_txt='LIMIT '.$start.', '.$limit;
		}
		elseif($limit>0) {
			$limit_txt='LIMIT '.$limit;
		}
		if($in_stock) {
			$stock_sql=' AND p.stock>0 ';
		}
		else {
			$stock_sql='';
		}
		$kiosk_where='';
		if($this->f3->get('authorization_user.kiosk')==1) {
			$kiosk_where=' AND ((c.kiosk_noshow=0 AND c.show=1) OR p.category_id=0)';
		}

		if($term) {
			// Пытаемся найти штрих-код
			$bsql="SELECT p.*, c.title AS category_title FROM barcodes AS b JOIN products AS p ON p.id=b.product_id AND p.date_delete=0 ".$stock_sql." LEFT JOIN productcategories AS c ON c.id=p.category_id ".$kiosk_where." WHERE b.barcode=?";
			$products=$this->db->exec($bsql, $term);
			$count=count($products);
		}
		if(empty($products) && !$barcodes_only) { // если не находим то ищем по названию и коду
			if(!$exact_match) {
				$w=$this->relevant($term);
			}
			else {
				$term=str_replace("'","\'",$term);
				$term=trim(htmlspecialchars($term));
				$w=array(
					'query'=>'',
					'where'=>"OR p.title like '%".$term."%'",
					'order'=>'title'
				);
			}
			if($order) {
				$w['order']=$order;
			}
			$sql="SELECT p.*, IFNULL(p.stock, 0) AS stock, c.title AS category_title ".$w['query']." FROM products AS p LEFT JOIN productcategories AS c ON c.id=p.category_id WHERE (p.date_delete=0 ".$stock_sql." AND (p.code=? ".$w['where'].")) ".$kiosk_where." ORDER BY ".$w['order'].' '.$limit_txt;
			$products=$this->db->exec($sql, $term);
			$count=count($products);
			if($limit_txt) {
				$sql="SELECT COUNT(*) AS count ".$w['query']." FROM products AS p WHERE (p.date_delete=0 ".$stock_sql." AND (p.code=? ".$w['where']."))";
				$res=$this->db->exec($sql, $term);
				$count=$res[0]['count'];
			}
		}
		if($count>0) {
			$success=true;
			foreach ($products as $i => $p) {
				$products[$i]['img']=json_decode($p['img'], true);
			}
		}
		else {
			$success=false;
		}
		return array(
			'success'=>$success,
			'count'=>$count,
			'products'=>$products
		);
	}

	// Сохранение товаров
	function save_product($data, $action='add') {
		if(!isset($data['title'])) {
			return array(
				'success'=>false,
				'txt'=>'not_enough_data'
			);
		}

		if($data['barcode']) {
			$barcode=$this->db->exec("SELECT b.product_id FROM barcodes AS b JOIN products AS p ON p.id=b.product_id AND p.date_delete=0 WHERE b.barcode=? LIMIT 1", $data['barcode']);
			if($barcode && $barcode[0]['product_id']!=$data['id']) {
				return array(
					'success'=>false,
					'txt'=>'barcode_is_exists'
				);
			}
		}


		if(!$data['id'] && $action=='add') {
			$data['id']=$this->db->exec("SELECT MAX(id) AS max_id FROM products")[0]['max_id']+1;
			$data['date_delete']=0;
			$data['date_update']=microtime(true);
		}
		if(!$data['date_delete'])$data['date_delete']=0;

		$this->reset();
		if($action=='change' && $data['id']) {
			$this->load(array('id=?', $data['id']));
			if(!$data['price']) $data['price']=$this->price;
		}

		$data['title']=mb_strtoupper($data['title']);
		$data['price']=(float)$data['price'];
		$data['max_discount']=(float)$data['max_discount'];

		$this->copyFrom($data);
		if($this->save()===false) {
			return array(
				'success'=>false,
				'txt'=>'db_error'
			);
		}
		$this->reset();
		if($data['barcode']) {
			$barcode=$this->db->exec("SELECT barcode FROM barcodes WHERE product_id=? LIMIT 1", $data['id']);
			if($barcode) {
				$this->db->exec("UPDATE barcodes SET barcode=? WHERE product_id=?", array($data['barcode'], $data['id']));
			}
			else {
				$this->db->exec("INSERT INTO barcodes (product_id, barcode) VALUES (?, ?)", array($data['id'], $data['barcode']));
			}
		}
		return array(
			'success'=>true,
			'txt'=>'save_successful',
			'reload'=>array('.products'),
			'close_all_window'=>true,
			'id'=>$data['id']
		);
	}

	// Информация о товаре для клиента
	function barcode($barcode) {
		if($barcode=='test') {
			return array(
				'success'=>true,
				'txt'=>"ok\n"
			);
		}
		if(!$barcode) {
			return array(
				'success'=>false,
				'txt'=>'not_enough_data',
			);
		}

		$product=$this->search($barcode, 0, 1, $barcodes_only=true)['products'][0];
		if(!$product) {
			return array(
				'success'=>false,
				'txt'=>'not_found',
			);
		}

		$price=$product['price']+($product['price']*($product['vat']/100))*max(0, $product['vat_type']);
		$price=round($price, 2);
		if($product['img']) {
			$img=array_shift($product['img']);
		}
		else {
			$img='';
		}
		return array(
			'success'=>true,
			'txt'=>$product['title']."\n"."На складе: ".numberformat($product['stock'], 3, '.', '')."\n"."Цена: ".number_format($price, 2, '.', '')."\n",
			'price'=>$price,
			'stock'=>$product['stock'],
			'img'=>$img
		);
	}

	// Ищем товар на маркете
	function market_search($barcode) {
		$web=\Web::instance();
		$products=array();
		$options = array(
			'method' => 'GET',
    		'follow_location' => 1
		);
		$query=$this->f3->get('market_url').'/api/getproduct?barcode='.$barcode;
		$request=$web->request($query, $options);
		if(mb_strpos($request['headers'][0], '200')>0 || mb_strpos($request['headers'][1], '200')>0) {
			$products=json_decode($request['body'], true)[0];
		}
		return $products;
	}

	// Отдаем список модификаторов
	function modifiers($id) {
		$modifiers=array();
		$this->reset();
		$this->load(array('id=?', $id), array('limit'=>1));
		$related=json_decode($this->related, true);
		if(!$related) {
			return $modifiers;
		}
		$category=new \Models\Productcategories($this->db, $this->f3);
		$ids='';
		$category_ids='';
		foreach ($related as $r) {
			if($r['type']=='product') {
				if($ids!='') $ids.=', ';
				$ids.=$r['id'];
			}
			elseif($r['type']=='category') {
				if($category_ids!='') $category_ids.=', ';
				$category_ids.=$r['id'];
				$subcats=$category->categories_list($r['id']);
				if($subcats) {
					$category_ids.=', ';
					$category_ids.=implode(', ', array_column($subcats, 'id'));
				}
			}
		}
		$where='';
		if($ids!='') {
			$where='p.id IN ('.$ids.')';
		}
		if($category_ids!='') {
			if($where!='') $where.=' OR ';
			$where.='p.category_id IN ('.$category_ids.')';
		}
		if($where=='') {
			return $modifiers;
		}
		$modifiers=$this->select_product(array('p.date_delete=0 AND ('.$where.')'), null, 1);
		return $modifiers;
	}

	function delete($id) {
		$this->reset();
		if(!$id) {
			return array(
				'success'=>false,
				'txt'=>'not_enough_data'
			);
		}
		$this->load(array('id=?', $id), array('limit'=>1));
		if($this->dry()) {
			return array(
				'success'=>false,
				'txt'=>'not_found'
			);
		}
		$products=$this->db->exec("SELECT COUNT(l.id) AS count FROM receiptlines AS l JOIN receipts AS r ON r.id=l.receipt_id AND r.time=0 WHERE l.product_id=?", array($id));
		if($products[0]['count']>0) {
			return array(
				'success'=>false,
				'txt'=>'products_can_not_delete'
			);
		}
		$this->date_delete=microtime(true);
		if($this->update()===false) {
			return array(
				'success'=>false,
				'txt'=>'db_error'
			);
		}
		return array(
			'success'=>true,
			'txt'=>'save_successful',
			'reload'=>array('.products'),
		);
	}

	function clearstop() {
		$res=$this->db->exec("UPDATE products SET stop=0 WHERE stop=1");
		if($res!==false) {
			return array(
				'success'=>true,
				'txt'=>'save_successful',
				'reload'=>array('.products'),
			);
		}
		return array(
			'success'=>false,
			'txt'=>'db_error'
		);
	}

	function add_lineset($json) {
		$rl=new \Models\Receiptlines($this->db, $this->f3);
		$receipt_id=$this->f3->get('COOKIE.receipt_id');
		$ids=array_column($json, 'id');
		$products=$this->select_product(array('p.id IN ('.implode(', ', $ids).')'));
		if($products) {
			$products=array_combine(array_column($products, 'id'), $products);
		}
		foreach ($json as $line) {
			if(!$line['title']) {
				$line['title']=$products[$line['id']]['title'];
			}
			if(!$line['units_fr']) {
				$line['units_fr']=$products[$line['id']]['units_fr'];
			}
			$res=$rl->add_line($receipt_id, $line, $line['count']);
			if(!$res['success']) {
				$res['txt']=$this->messages->message($res['txt'])['txt'];
				break;
			}
		}
		return $res;
	}
}
