<?php
global $rsoc_request;
if(!isset($rsoc_request) || !$rsoc_request){
	require_once("/opt/msa/lib/php/include/dbPDO.php");
}
// include_once("/opt/msa/lib/php/include/openai/class.openai.php");

define('MACROS', array (
			"[_month_]" => date('M'),
			"[_year_]" => date('Y')
			));
define('MACROS_CHECK_ON', array('title', 'content', 'meta_title', 'sub_head'));
define('MACROS_CHECK_ON_ITINERARY', array('title', 'desc'));

define('EAST_COAST_DOMAIN_LIST', array(''));
define('EAST_COAST_PORT_MAPPING', array(
	22135 => 22142
));

const DOMAIN_WISE_REDIS_CONFIGURATION = array(
    'api.admedia.com' => array(
        'READ_PORT' => 22132,
        'WRITE_PORT' => 22132
    ),
	'dev.admedia.com' => array(
        'READ_PORT' => 6378,
        'WRITE_PORT' => 6378
    ),
);

class cms_model
{
	const REDIS_HOST = "127.0.0.1";
	const REDIS_PORT_WRITE = "22133";
	const REDIS_PORT_READ = "22133";

	const SERP_REDIS_HOST = "127.0.0.1";
	const SERP_REDIS_PORT = "22135";
    const SERP_REDIS_TIMEOUT = 1;//1 sec timeout
	const SPHINX_HOST = "127.0.0.1";
	const SPHINX_PORT = "22139";

	const SPHINX_NAME = "rep_cms_article";
	// const REDIS_HOST = "192.168."."30.39";
	// const REDIS_PORT_WRITE = "6383";
	// const REDIS_PORT_READ = "6383";
	
	const TTL = 60 * 30;
	const REDIS_KEY_PREFIX = "cms_";


	function __construct($dbconnection=1)
	{
		if($dbconnection) {
            $this->db_cms = CommonPDOConnector::connect('ConnectToCMS');
            $this->db_admin = CommonPDOConnector::connect('ConnectToAdmin');
        }
		$this->_redis = new Redis();
	}

	public function getArticles($siteID, $categoryName = null, $number = 10, $start = 0, $searchstr = null,$debug=null)
	{
		$redis_key = "";
		if (!empty($categoryName)) {
			$redis_key = "getArticles22_" . $siteID . "_" . $categoryName;
			$sql = "select ca.id, ca.user_id ,ca.images_attr, title, author, content, ca.image, ca.old_image, link, meta_title, keywords, timestamp,  cca.subcategory_id, cc.name as category, cu.name as username, ca.slug_title from rep_cms_category cc, rep_cms_articles ca, rep_cms_catart cca, rep_user cu where cc.id = cca.category_id and ca.user_id = cu.id and cca.article_id = ca.id and cc.site_id = '" . $siteID . "' and cc.name = '" . $categoryName . "' and (cu.approved = '2' or ca.approved = 1) AND ca.status = 1 ";
			if ($searchstr != null) {
				$sql .= " and ca.title like '$searchstr%'";
				$redis_key = "getArticles22_" . $siteID . "_" . $categoryName . "_" . $searchstr;
			}
			if(!empty($siteID) && $siteID=='596'){
			$sql .= " order by timestamp desc";
			}else{
			$sql .= " order by cca.article_id desc";
			}
		} else {
			$redis_key = "getAllArticles22_" . $siteID;
			$sql = "select ca.id, ca.user_id ,ca.images_attr, title, author, content, ca.image, ca.old_image, link, meta_title, keywords, timestamp,  cca.subcategory_id, cc.name as category, cu.name as username, ca.slug_title from rep_cms_category cc, rep_cms_articles ca, rep_cms_catart cca, rep_user cu where cc.id = cca.category_id and ca.user_id = cu.id and cca.article_id = ca.id and cc.site_id = '" . $siteID . "' and (cu.approved = '2' or ca.approved = 1) AND ca.status = 1 ";
			if ($searchstr != null) {
				$sql .= " and ca.title like '$searchstr%'";
				$redis_key = "getAllArticles22_" . $siteID . "_" . $searchstr;
			}
			if(!empty($siteID) && $siteID=='596'){
			$sql .= " order by timestamp desc";
			}else{
			$sql .= " order by cca.article_id desc";
			}
		}
		$result = $this->getRedisKeyData($redis_key);
		if (!empty($debug) && $debug != null) {
			echo "<pre>";
			print_r($result);die;
		}
		if (empty($result)) {
		$query = $this->db_cms->query($sql);
		$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
		$result = array();
		foreach ($query_result as $row) {
			$row = array_map(array($this, 'replaceChars'), $row);
			if (!isset($row['slug_title']) || empty($row['slug_title']) || $row['slug_title'] == "") {
				$row['slugurl'] = $this->generateSlug($row['title']);
			} else {
				$row['slugurl'] = $this->generateSlug($row['slug_title']);
			}
			$result[] = $row;
		}
			$this->setRedisKeyData($redis_key, $result);
		}

		$resultCount = count($result);
		if ($result) {
			if ($number != 0) {
				$result =  array_slice($result, $start, $number);
			}
			$result['count'] = $resultCount;
			return $result;
		} else {
			return false;
		}
	}

	public function getArticleDetail($articleID, $siteID = 0)
	{
		$key = md5("getArticleDetail1239_" . $articleID . "_" . $siteID);
		$row = array();
		$sql = "select ca.id, user_id, title, author, content, ca.image, image2, link, meta_title, keywords, ca.approved_keywords, sub_head, rating, timestamp, cc.name as category, cc.id as category_id, cu.name as username,slug_title, ca.old_image, ca.images_attr,ca.is_index from rep_cms_category cc, rep_cms_articles ca, rep_cms_catart cca, rep_user cu where cca.category_id = cc.id and ca.user_id = cu.id and cca.article_id = ca.id and ca.id = '{$articleID}' AND ca.status = 1";
		if ($siteID > 0)
			$sql .= " and cc.site_id = " . $siteID;
		$sql .= " limit 1";

		$query = $this->db_cms->query($sql);
		$query_result = $query->fetch(); #PDO::FETCH_LAZY);//fetchRow(PDO::FETCH_ASSOC);
		if (!$query_result) {
			return false;
		}
		$row = array_map(array($this, 'replaceChars'), $query_result);

		return $row;
	}

	public function getArticlesByKeyword($siteID, $categoryName, $number = 10, $start = 0, $keyword = "")
	{
		$categoryName = trim($categoryName);
		$keyword = trim($keyword);
		$query = "select ca.id , ca.images_attr, title, author, content, image, link, meta_title, keywords, rating ,timestamp, cca.subcategory_id, cc.name as category, ca.slug_title from rep_cms_category cc, rep_cms_articles ca, rep_cms_catart cca where cc.id = cca.category_id and cca.article_id = ca.id and cc.site_id = " . $siteID . " AND ca.status = 1";
		if (isset($categoryName) && !empty($categoryName)) {
			$query .= " and cc.name = '" . $categoryName . "'";
		}
		if (isset($keyword) && !empty($keyword)) {
			$query .= " and ca.keywords like '%" . $keyword . "%'";
		}
		$query .= " order by cca.article_id desc";
		$query = $this->db_cms->query($query);
		$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
		$result = array();
		foreach ($query_result as $row) {
			$row = array_map(array($this, 'replaceChars'), $row);
			if (!isset($row['slug_title']) || empty($row['slug_title']) || $row['slug_title'] == "") {
				$row['slugurl'] = $this->generateSlug($row['title']);
			} else {
				$row['slugurl'] = $this->generateSlug($row['slug_title']);
			}
			$result[] = $row;
		}
		$resultCount = count($result);
		if ($result) {
			$result =  array_slice($result, $start, $number);
			$result['count'] = $resultCount;
			return $result;
		} else {
			return false;
		}
	}

    public function getRandomArticleAnySiteByKeyword( $keyword, $number = 10, $start = 0)
    {
        return [];
    }

	public function getRandomArticleAnySiteByKeywordAI( $keyword, $force_keyword, $number = 10, $start = 0) {
        return [];
	}

    public function getHashterms($hash,$debug=0){
        $redis_key_serp_force = "sphserp_force_terms_" . $hash;
        if ($debug) {
            print "Read from Redis : " . $redis_key_serp_force . "\n";
            print "Read from Redis HOST : " . self::SERP_REDIS_HOST . " PORT " . self::SERP_REDIS_PORT . "\n";
            var_dump($this->getRedisKeyStringSERP($redis_key_serp_force));
        }
        return $this->getRedisKeyStringSERP($redis_key_serp_force);
    }

	public function getRandomArticleAnySiteByKeywordAIV2( $keyword, $force_keyword = '', $hash = '',$debug=0, $exact_match=true) {
		global $push_kwd_for_content_generation;
		$keyword = $this->clean_strings($keyword);
		$force_keyword = $this->clean_strings($force_keyword);
		$hash = $this->clean_strings($hash);

		$debug = isset($_GET['debug']) ? $_GET['debug'] : $debug;
		$ttl = 86400; // 1 day
		$redis_key_art = "spharticledetail_" . md5($keyword . "_" . $force_keyword);
		$data = array();
		$found_on_hash = 0;
		$force_terms =  "";
		$redis_key_serp = "sphserp_article_" . $hash;
		$redis_key_serp_force = "sphserp_force_terms_" . $hash;
		$query_result = array();
		if (isset($hash) && !empty($hash)) {
			$query_result_with_fterm = $this->getRedisKeyDataSERP($redis_key_serp);
			if (is_array($query_result_with_fterm) && count($query_result_with_fterm)) {
				if ($debug) print "Read from Redis : " . $redis_key_serp . "\n";
				$found_on_hash = 1;
				if (isset($query_result_with_fterm['article'])) {
					$query_result = $query_result_with_fterm['article'];
				}
				if (isset($query_result_with_fterm['force_term'])) {
					$force_terms = $query_result_with_fterm['force_term'];
				}
			} 			
		}
		$from_redis = 0;
		if (empty($query_result)) {
			// Get Detail From Redis
            if($debug) $startTime = microtime(true);
			$query_result = $this->getRedisKeyDataSERP($redis_key_art);
            if($debug) {
                $endTime = microtime(true);
                print "Step 1 Time lapse for getRedisKeyDataSERP for redis key $redis_key_art: " . ($endTime-$startTime) . " seconds\n";
            }
			if ($debug == "bypassredis" || empty($query_result)) {
				// Get Detail From Sphinx
                if($debug) $startTime = microtime(true);
				$query_result = $this->getArticleFromSphinxAndSetIntoRedis($keyword, $redis_key_art,0,$exact_match);
                if($debug) {
                    $endTime = microtime(true);
                    print "Step 2 Time lapse for getArticleFromSphinxAndSetIntoRedis for keyword $keyword to set into redis $redis_key_art: " . ($endTime-$startTime) . " seconds\n";
                }
			} else {
				if ($debug) print "Get data from Redis : $redis_key_art";
				$from_redis = 1;
			}
		}
		if (empty($query_result) || !empty($push_kwd_for_content_generation)) {
			// PUSH for SERP-26
			$redis_key = $exact_match ? "article_not_exist_for_keywords_members_area_v4" : "article_not_exist_for_keywords_v3";
			if ($this->is_valid_keyword($keyword)) {
                if($debug) $startTime = microtime(true);
				$this->_redis->pconnect(self::SERP_REDIS_HOST, self::SERP_REDIS_PORT, self::SERP_REDIS_TIMEOUT);
				// Get ip address
				$ip_address = $_SERVER['HTTP_X_FORWARDED_FOR'] ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['HTTP_X_REAL_IP'] ;
				$keyword_str = $ip_address . "##" . $keyword;
				if (isset($force_keyword) && !empty($force_keyword)) {
					$keyword_str .= "::" . $force_keyword;
				}
				$this->_redis->SADD($redis_key, $keyword_str);
                if($debug) {
                    $endTime = microtime(true);
                    print "Step 3 Time lapse for writing into redis on".self::REDIS_HOST." port is : ".self::REDIS_PORT_WRITE." for redis key $redis_key" . ($endTime-$startTime) . " seconds\n";
                }
			}
            if($debug) $startTime = microtime(true);
			$query_result = !empty($query_result) ? $query_result : $this->getRandomArticle();
            if($debug) {
                $endTime = microtime(true);
                print "Step 4 Time lapse for keyword next step : " . ($endTime-$startTime) . " seconds\n";
            }

		} else if ($from_redis == 0) {
			if ($debug) print "Get data from Sphinx";
		}

		$data = array();
		$data['article'] = $query_result;
		$data['force_term'] = $force_terms;

		return $data;
	}

	private function clean_strings($s) {
		$debug = isset($_GET['debug']) ? $_GET['debug'] : 0;
		$s = str_replace("-", " ", trim($s));
		$s = urldecode($s);
		$s = addslashes($s);
		// removed non ASCII characters from string
		$clean_string = preg_replace('/[[:^print:]]/', '', $s);
		if (!mb_check_encoding($clean_string, 'UTF-8')) {
			$clean_string = utf8_encode($keyword);
		}
		$clean_string = str_replace(array(chr(133), chr(145), chr(146), chr(147), chr(148), chr(226).chr(128).chr(157), chr(226).chr(128).chr(153)), array('...', "'", "'", '"', '"', '"', "'"), $clean_string);

		$revise_str = $this->sphinx_escape_string($clean_string);
		if ($debug) print "Ogi : $s\nClean : $clean_string\nRevised : $revise_str\n";
		return $revise_str;
	}

	// Helper function to escape special characters for Sphinx
	private function sphinx_escape_string($string) {
		$from = ['\\', '(', ')', '|', '-', '!', '@', '~', '"', '&', '/', '^', '$', '=', '<', '>', "'"];
		//$to = ['\\\\', '\(', '\)', '\|', '\-', '\!', '\@', '\~', '\"', '\&', '\/', '\^', '\$', '\=', '\<', '\>'];
		$to = ['\\\\', '\\(', '\\)', '\\|', '\\-', '\\!', '\\@', '\\~', '\\"', '\\&', '\\/', '\\^', '\\$', '\\=', '\\<', '\\>', "\\'"];
		return str_replace($from, $to, $string);
	}

	public function getArticleFromSphinxAndSetIntoRedis($keyword, $redis_key, $is_hash = 0, $exact_match=false) {
		global $push_kwd_for_content_generation;
		$debug = isset($_GET['debug']) ? $_GET['debug'] : 0;
		$this->sphinx =  new PDO( 'mysql:host='.self::SPHINX_HOST.';port='.self::SPHINX_PORT.';', '', '');
		$totalWords = count(explode(' ',$keyword));
		$sphinx_queries = array();
		// $match_no_words = ceil($totalWords/2);
		
		if(!$exact_match){
			$keyword_s = '@title"' . trim($keyword) . '"'; 
			$sphinx_queries[] ="select str_hash as hash, article_id as id, images_attr, title, author, content, image, link, meta_title, keyword, rating, str_timestamp as timestamp, slug_title from rep_cms_article where MATCH ('$keyword_s') OPTION ranker=sph04,cutoff=1";
		}else{
			$keyword_s = trim($keyword) ; 
			$sphinx_queries[] ="select str_hash as hash, article_id as id, images_attr, title, author, content, image, link, meta_title, keyword, rating, str_timestamp as timestamp, slug_title from rep_cms_article where keyword = '$keyword_s' limit 1";
		}
		//fallback query if data not returned from first query
		/*
		$keyword_arr = array();
		$keyword_arr[] = $keyword;
		$combination_data = ($totalWords > 2) ? $this->createWordsCombinations($keyword) : array();
		$keyword_arr = array_merge($keyword_arr, $combination_data);
		$keyword_s = '@title"' . implode('"|"', $keyword_arr) . '" | @keyword"' . implode('"|"', $keyword_arr) . '"';			
		$sphinx_queries[] = "select str_hash as hash, article_id as id, images_attr, title, author, content, image, link, meta_title, keyword, rating, str_timestamp as timestamp, slug_title from rep_cms_article where MATCH ('$keyword_s') OPTION ranker=sph04,cutoff=1,field_weights=( title=10, keyword=1)";
		*/
		
		$data = array();
		foreach ($sphinx_queries as $spinxSql) {
			try {
				if ($debug) print "Sphinx Query : $spinxSql\n";
				// Execute the query
				$pdoStatement = $this->sphinx->query($spinxSql);

				// Check if the query was successful
				if (!$pdoStatement) {
					// Fetch the error info from the PDO statement
					$errorInfo = $this->sphinx->errorInfo();
					if ($debug) echo "Query Failed: " . $errorInfo[2] . "\n";
					continue;
				}

				// Fetch the result row
				$row = $pdoStatement->fetch(PDO::FETCH_ASSOC);
				if (!empty($row)) {
					$data[] = $row;
					if ($is_hash == 0) {
						// setting redis cache ttl to 15 mins if keyword is pushed for article generation else ttl will be 1 day
						$redis_cache_ttl = !empty($push_kwd_for_content_generation) ?  150 : 86400 ;
						if ($debug) print "Get from Sphinx and Push into Redis : $redis_key\n";
						$this->setRedisKeyDataSERP($redis_key, $data, $redis_cache_ttl);
					}
					break;
				}else{
					if ($debug) echo "No data returned : push keyword for content generation ". "\n";
					$push_kwd_for_content_generation = empty($push_kwd_for_content_generation) ? true : $push_kwd_for_content_generation;
					/* code to generate article on the fly
					$openAiObj = new OpenAI();
					$res = $openAiObj->createExactMatchArticleAndStore($keyword, "openai_articles");
					if(!empty($res[0]['title'])){

						$data = $res;
						if(!empty($res[0]['id'])){
							// pushing article id to separate redis queue for article sync in sphinx
							$this->_redis->connect(self::SERP_REDIS_HOST, self::SERP_REDIS_PORT);
							$this->_redis->SADD('realtime_openai_article_list', $res[0]['id']);
						}
						// pushing realtime generated article to redis for caching purpose
						$this->setRedisKeyDataSERP($redis_key, $data, 86400);
						if ($debug) echo "Article generated real time and pushed to redis. ".date("Y-m-d H:i:s"). "\n";
						break;
					}else{
						$push_kwd_for_content_generation = empty($push_kwd_for_content_generation) ? true : $push_kwd_for_content_generation;
					}*/
				}
			} catch (PDOException $e) {
				// Catch any PDO exceptions and print the error message
				if ($debug) echo "Query failed: " . $e->getMessage() . "\n";
			}
		}
		if (isset($data) && count($data)) {
			return $data;
		}
		return "";
	}

	public function createWordsCombinations($keyword){
		$words_arr = explode(' ', $keyword);
		$combinations_data = array();
		for($i=0;$i<count($words_arr);$i++){
			if($i!==0){
				$combinations_data[] = trim($words_arr[$i-1]) . ' '. trim($words_arr[$i]);
			}			
		}
		return $combinations_data;
	}

    public function getRandomArticle() {
		$query_result = '';
		$query_result = $this->getRedisKeyDataSERP("random_article_rsoc");	
		$debug = isset($_GET['debug']) ? $_GET['debug'] : 0;
		if ($debug) print "Get Data from Redis : random_article_rsoc";
		return $query_result;
    }

	public function getLatestArticle() {
		$debug = isset($_GET['debug']) ? $_GET['debug'] : 0;
		$query = "select ca.id , ca.images_attr, title, author, content, image, link, meta_title, keywords, rating ,timestamp, ca.slug_title from rep_cms_articles ca  where ca.status IN (1) order by id desc LIMIT 1";

		if ($debug) {
			print "Random Article Query : " . $query . "\n";
		}
		$res = $this->db_cms->query($query);
		return $res->fetchAll(PDO::FETCH_ASSOC);
	}

	public function searchArticles($search, $siteID, $categoryName, $number = 10, $start = 0, $sort_by = "")
	{
		$search = addslashes(trim($search));
		$siteID = (int)$siteID;

		// Start query
		$sql = "SELECT
			ca.id,
			ca.images_attr,
			ca.title,
			ca.author,
			ca.content,
			ca.image,
			ca.old_image,
			ca.link,
			ca.meta_title,
			ca.keywords,
			ca.rating,
			ca.timestamp,
			cca.subcategory_id,
			cc.name AS category,
			ca.slug_title,
			MATCH (ca.title, ca.keywords, ca.content) AGAINST ('{$search}' IN BOOLEAN MODE) AS title_relevance
		FROM rep_cms_articles AS ca
		JOIN rep_user AS cu ON ca.user_id = cu.id
		JOIN rep_cms_catart AS cca ON cca.article_id = ca.id
		JOIN rep_cms_category AS cc ON cc.id = cca.category_id
		WHERE
			MATCH (ca.title, ca.keywords, ca.content) AGAINST ('{$search}' IN BOOLEAN MODE)
			AND cc.site_id = {$siteID}
			AND ca.status = 1
			AND (cu.approved = '2' OR ca.approved = 1)
		";

		// Optional category filter
		if (trim($categoryName) != '') {
			$categoryName = addslashes($categoryName);
			$sql .= " AND cc.name = '{$categoryName}'";
		}

		// Sorting logic
		if (!empty($sort_by)) {
			if ($sort_by === "highest") {
				$sql .= " ORDER BY ca.rating DESC";
			} elseif ($sort_by === "lowest") {
				$sql .= " ORDER BY ca.rating ASC";
			} else {
				$sql .= " ORDER BY ca.id DESC";
			}
		} else {
			$sql .= " ORDER BY title_relevance DESC, ca.id DESC";
		}

		$sql .= " LIMIT 50";

		$query = $this->db_cms->query($sql);
		$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
		$key = md5($sql);
		$result = array();
		foreach ($query_result as $row) {
			$row = array_map(array($this, 'replaceChars'), $row);
			if (!isset($row['slug_title']) || empty($row['slug_title']) || $row['slug_title'] == "") {
				$row['slugurl'] = $this->generateSlug($row['title']);
			} else {
				$row['slugurl'] = $this->generateSlug($row['slug_title']);
			}
			$result[] = $row;
		}
		$resultCount = count($result);
		if ($result) {
			$result =  array_slice($result, $start, $number);
			$result['count'] = $resultCount;
			return $result;
		} else {
			return false;
		}
	}

	public function replaceChars($s)
	{
		$f[] = '“'; // left side double smart quote
		$f[] = '”'; // right side double smart quote
		$f[] = '‘'; // left side single smart quote
		$f[] = '’'; // right side single smart quote
		$f[] = '…'; // elipsis
		$f[] = '—'; // em dash
		$f[] = '–'; // en dash

		$r[] = '"';
		$r[] = '"';
		$r[] = "'";
		$r[] = "'";
		$r[] = '...';
		$r[] = '-';
		$r[] = '-';
		$result = str_replace($f, $r, $s);
		return $result;
	}

	public function generateSlug($slug)
	{
		$result = strtolower($slug);
		$result = preg_replace('/[^a-z0-9\s-]/', '', $result);
		$result = trim(preg_replace('/[\s-]+/', ' ', $result));
		$result = preg_replace('/\s/', '-', $result);
		return $result;
	}

	public function getSiteCategoriesBySiteName($site_name)
	{
		if ($site_name == trim($site_name) && strpos($site_name, ' ') !== false) {
			return array();
		}
		$result = array();
		$redis_key = md5("getSiteCategoriesBySiteName_" . $site_name);
		$result = $this->getRedisKeyData($redis_key);
                if (empty($result)) {
		$ttl = 60 * 30; // 2 hours

		$result = array();
		$query = $this->db_cms->query("select cc.* from rep_cms_category cc, rep_cms_site s where s.id = cc.site_id and s.domain = '{$site_name}' order by s.id asc");
		$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
		$result = array();
		foreach ($query_result as $row) {
			$result[$row['name']] = $row['id'];
		}
		$this->setRedisKeyData($redis_key, $result);
                }

		return $result;
	}

	public function getArticleCount( $siteID, $categoryName ) {
		$sql = "SELECT
					count(ca.id) AS count
				FROM rep_cms_category cc, rep_cms_articles ca, rep_cms_catart cca, rep_user cu
				WHERE
					cc.id = cca.category_id AND cca.article_id = ca.id AND ca.user_id = cu.id
					AND cc.site_id = '" . $siteID . "' AND cc.name = '" . $categoryName . "'
					AND (cu.approved = '2' OR ca.approved = 1) AND ca.status = 1
				ORDER BY ca.id DESC";
		$query = $this->db_cms->query($sql);
		$result = $query->fetch( PDO::FETCH_ASSOC );
		return $result['count'];
	}

	public function getSiteCategoryByName($site_id, $cat)
	{

		$result = array();
		$query = $this->db_cms->query("select * from rep_cms_category where site_id = {$site_id} and name = '{$cat}' order by id asc");
		$i = 0;
		$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
		foreach ($query_result as $row) {
			$result[$i]['name'] = $row['name'];
			$result[$i]['id'] = $row['id'];
			$result[$i]['meta'] = $row['meta'];
			$result[$i]['description'] = $row['description'];
			$i++;
		}


		return $result;
	}


	public function getArticlesBySubCat($categoryName, $subcategoryName, $number = 10, $start = 0, $sort_by = "", $searchstr = null)
	{
		$redis_key = "getArticlesCat_" . preg_replace('/\s+/', '', $categoryName) . "_" . preg_replace('/\s+/', '', $subcategoryName);
		$result = $this->getRedisKeyData($redis_key);
		if (empty($result)) {
			$result = array();
			// Clean and prepare inputs
			$categoryName    = trim($categoryName);
			$subcategoryName = trim($subcategoryName);
			$searchstr       = trim($searchstr);
			$sort_by         = trim($sort_by);

			// Base SQL (optimized)
			$sql = "SELECT ca.id,
					ca.timestamp,
					ca.title,
					ca.slug_title,
					ca.author,
					ca.content,
					ca.image,
					ca.images_attr,
					ca.link,
					ca.meta_title,
					ca.keywords,
					ca.sub_head,
					ca.rating,
					cc.name AS category,
					cs.name AS subcategory,
					cs.id AS subcategory_id,
					MATCH (ca.title, ca.keywords, ca.content) AGAINST (:search IN BOOLEAN MODE) AS relevance
				FROM rep_cms_subcategory cs
				JOIN rep_cms_catart    cca ON cs.id = cca.subcategory_id
				JOIN rep_cms_category  cc  ON cs.cat_id = cc.id
				JOIN rep_cms_articles  ca  ON cca.article_id = ca.id
				JOIN rep_user          cu  ON cu.id = ca.user_id
				WHERE
					cc.name = :categoryName
					AND SUBSTRING_INDEX(cs.name, '|', -1) = :subcategoryName
					AND (cu.approved = '2' OR ca.approved = 1)
					AND ca.status = 1";

			// Apply FULLTEXT search condition
			if (!empty($searchstr)) {
				// Convert multi-word input into BOOLEAN MODE format: "+word1 +word2"
				$terms = preg_split('/\s+/', $searchstr);
				$booleanSearch = '+' . implode(' +', $terms);
				$sql .= " AND MATCH (ca.title, ca.keywords, ca.content) AGAINST (:booleanSearch IN BOOLEAN MODE)";
			}

			// Sorting
			if (!empty($sort_by)) {
				if ($sort_by === "highest") {
					$sql .= " ORDER BY ca.rating DESC";
				} elseif ($sort_by === "lowest") {
					$sql .= " ORDER BY ca.rating ASC";
				} else {
					$sql .= " ORDER BY relevance DESC, ca.id DESC";
				}
			} else {
				$sql .= " ORDER BY relevance DESC, ca.id DESC";
			}

			// Limit for performance
			//$sql .= " LIMIT 50";

			//print "Query : $sql\n";
			// Prepare statement
			$stmt = $this->db_cms->prepare($sql);

			$debug = isset($_GET['debug']) ? $_GET['debug'] : 0;
			if ($debug == 1) {
				echo $sql;
			}
			// Bind static params
			$stmt->bindValue(':categoryName', $categoryName, PDO::PARAM_STR);
			$stmt->bindValue(':subcategoryName', $subcategoryName, PDO::PARAM_STR);


			// Bind search
			if (!empty($searchstr)) {
				$stmt->bindValue(':search', $searchstr, PDO::PARAM_STR);
				$stmt->bindValue(':booleanSearch', $booleanSearch, PDO::PARAM_STR);
			} else {
				// If no search, just bind empty
				$stmt->bindValue(':search', '', PDO::PARAM_STR);
			}

			// Execute
			$stmt->execute();
			$query_result = $stmt->fetchAll(PDO::FETCH_ASSOC);

			foreach ($query_result as $row) {
				$row = array_map(array($this, 'replaceChars'), $row);
				if (!isset($row['slug_title']) || empty($row['slug_title']) || $row['slug_title'] == "") {
					$row['slugurl'] = $this->generateSlug($row['title']);
				} else {
					$row['slugurl'] = $this->generateSlug($row['slug_title']);
				}
				$result[] = $row;
			}
			$this->setRedisKeyData($redis_key, $result);
		}
		$resultCount = count($result);
		if ($result) {
			if ($number != 0) {
				$result =  array_slice($result, $start, $number);
			}
			$result['count'] = $resultCount;
			return $result;
		} else {
			return false;
		}
	}

	public function getSubCategoryByCatId($site_id, $category)
	{
		$result = array();
		$category_query = "select id, name from rep_cms_category where name = '" . $category . "' and site_id = '" . $site_id . "'";
		$cquery = $this->db_cms->query($category_query);
		$query_result = $cquery->fetch(PDO::FETCH_ASSOC);
		$catID = !empty($query_result['id']) ? $query_result['id'] : '';

		if (!empty($catID)) {
			$subcat = "SELECT cc.subcategory_id, c.name  FROM `rep_cms_catart` as cc inner join rep_cms_category as c on c.id= cc.subcategory_id where cc.category_id = {$catID} group by subcategory_id";
			$query = $this->db_cms->query($subcat);
			$i = 0;
			$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
			foreach ($query_result as $row) {
				$result[$i]['id'] = $row['subcategory_id'];
				$result[$i]['name'] = $row['name'];
				$i++;
			}
		}

		return $result;
	}

	public function getSubCategoriesByCatID($catID)
	{
		$result = array();
		$query = $this->db_cms->query("select * from rep_cms_subcategory where cat_id = " . $catID . " order by id asc");

		foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
			$result[] = $row;
		}
		return $result;
	}

	public function getSubCategoryBySiteId($site_id)
	{
		$result = array();
		$query = $this->db_cms->query("SELECT id,name FROM `rep_cms_subcategory` where cat_id in ( SELECT id FROM `rep_cms_category` WHERE site_id = '" . $site_id . "')");
		foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
			$result[$row['id']] = $row['name'];
		}

		return $result;
	}

	public function getSiteCategories($site_id)
	{
		$key = SELF::REDIS_KEY_PREFIX . $site_id . "_categories";
		$result = $this->getRedisKeyData($key);
		if (empty($result)) {
			$query = $this->db_cms->query("select * from rep_cms_category where site_id = '{$site_id}' order by id asc");

			$data = [];
			foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
				$data[$row['name']] = $row['id'];
			}

			foreach ($data as $category => $value) {
				$subcategories = $this->getSubCategories(addslashes($category));
				if (!empty($subcategories)) {
					$result[$category] = $subcategories;
				}
			}

			$this->setRedisKeyData($key, $result);
		}

		return $result;
	}

	public function getSubCategories($categoryName)
	{
		$result = [];

		$query = $this->db_cms->query("select cs.* from rep_cms_subcategory cs, rep_cms_category cc where cc.id = cs.cat_id and cc.name = '" . $categoryName . "' order by cs.id asc");

		foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
			$result[$row['name']] = $row['id'];
		}

		return $result;
	}

	public function getArticlesBySubCategory($categoryName, $subcategoryName)
	{
		$hash = self::REDIS_KEY_PREFIX . "_articles_subcat";
		$subcategory = preg_replace('/^-|-$/', '', preg_replace('/([-])\1+/', '$1', preg_replace("/[^A-Za-z0-9-]/", '-', strtolower($subcategoryName))));
		$subcategoryName = addslashes($subcategoryName);
		$result = $this->getRedisHashKeyData($hash, $subcategory);

		if (empty($result)) {
			$sql = "SELECT ca.id, ca.timestamp, ca.title, ca.slug_title, ca.image, ca.images_attr, ca.old_image, ca.meta_title, ca.keywords, ca.sub_head, ca.rating, cc.name AS category, s.name AS subcategory FROM rep_cms_category cc, rep_cms_subcategory s, rep_cms_articles ca, rep_cms_catart cca, rep_user cu WHERE ca.status !='2' AND cc.id = cca.category_id AND ca.user_id = cu.id AND cca.article_id = ca.id AND s.id = cca.subcategory_id AND ca.status = 1 AND cc.name = '" . $categoryName . "' AND SUBSTRING_INDEX(s.name, '|', -1) = '" . $subcategoryName . "' ";
			$sql .= " AND (cu.approved = '2' OR ca.approved = 1) ORDER BY cca.article_id DESC";
			if (isset($_GET['is_debug']) && $_GET['is_debug'] == "sql") {
				print $sql;
			}
			$query = $this->db_cms->query($sql);

			foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
				$row = array_map(array($this, 'replaceChars'), $row);

				if (!isset($row['slug_title']) || empty($row['slug_title']) || $row['slug_title'] == "") {
					$row['slugurl'] = $this->generateSlug($row['title']);
				} else {
					$row['slugurl'] = $this->generateSlug($row['slug_title']);
				}
				$this->macrosReplacement($row);
				$result[] = $row;
			}

			$this->setRedisHashKeyData($hash, $subcategory, $result);
		} else if (isset($_GET['is_debug']) && $_GET['is_debug'] == "sql") {
			print "|$hash|$subcategory|";
		}
		return $result;
	}

	public function getArticlesByCategoryId($siteID, $categoryId)
	{
		$hash = self::REDIS_KEY_PREFIX . $siteID . "_articles_cat";
		$result = $this->getRedisHashKeyData($hash, $categoryId);

		if (empty($result)) {
			$sql = "SELECT ca.id, ca.timestamp, ca.title, ca.slug_title, ca.image, ca.images_attr, ca.link, ca.old_image, ca.meta_title, ca.keywords, ca.sub_head, ca.rating, cc.name AS category, s.name AS sub_category FROM rep_cms_category cc, rep_cms_articles ca, rep_user cu, rep_cms_catart cca LEFT JOIN rep_cms_subcategory s ON s.id = cca.subcategory_id WHERE cc.id = cca.category_id AND ca.user_id = cu.id AND cca.article_id = ca.id AND cc.site_id = '" . $siteID . "' AND ca.status = 1 ";

			if ($categoryId != '')
				$sql .= " AND cc.id = " . $categoryId;

			$sql .= " AND (cu.approved = '2' OR ca.approved = 1) ORDER BY cca.article_id DESC";

			$query = $this->db_cms->query($sql);

			foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
				$row = array_map([$this, 'replaceChars'], $row);
				$result[] = $row;
			}

			$this->setRedisHashKeyData($hash, $categoryId, $result);
		}

		return $result;
	}

	public function getSiteArticlesByKeywords($siteID, $keyword)
	{
		$result = [];

		$sql = "SELECT ca.id, title, ca.slug_title, ca.image, ca.images_attr, meta_title, keywords, sub_head, cc.name AS category, s.name AS sub_category FROM rep_cms_category cc, rep_cms_subcategory s, rep_cms_articles ca, rep_cms_catart cca, rep_user cu WHERE cc.id = cca.category_id AND ca.user_id = cu.id AND cca.article_id = ca.id AND s.id = cca.subcategory_id AND cc.site_id = '" . $siteID . "' AND match(ca.keywords) against('\"{$keyword}\"' in boolean mode) AND (cu.approved = '2' or ca.approved = 1) AND ca.status = 1 order by cca.article_id desc";

		$query = $this->db_cms->query($sql);

		foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
			$row = array_map([$this, 'replaceChars'], $row);
			$result[] = $row;
		}

		return $result;
	}

	public function getCategoryContent($catId, $subcat)
	{
		if ($subcat != "0") {
			$query = $this->db_cms->query('select id from rep_cms_subcategory where cat_id = "' . $catId . '" and name like "%' . $subcat . '%"');
			$query->execute();
			$subcat = $query->fetchColumn();
		}

		$result = [];
		$query = $this->db_cms->query("select section_title,desc1,feature_image, secondary_image, meta_title, meta_description from rep_cms_category_content where cat_id = '{$catId}' and subcat_id = '{$subcat}' AND section_title != '' order by section_title_position asc");

		$i = 0;
		foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
			$result[$i]['section_title'] = $row['section_title'];
			$result[$i]['desc'] = $row['desc1'];
			$result[$i]['feature_image'] = $row['feature_image'];
			$result[$i]['secondary_image'] = $row['secondary_image'];
			$result[$i]['meta_title'] = $row['meta_title'];
			$result[$i]['meta_description'] = $row['meta_description'];
			$i++;
		}

		return $result;
	}

	public function getArticleExtendedDetail($id)
	{
		$result = [];
		$query = $this->db_cms->query("select * from rep_cms_article_itinerary_data where article_id = {$id}");
		$response = $query->fetch(PDO::FETCH_ASSOC);
		if (empty($response)) {
			$query = $this->db_cms->query("select * from rep_cms_article_details where article_id = {$id}");
			$result = $query->fetch(PDO::FETCH_ASSOC);
		} else {
			$itinerary = json_decode($response['data'], true);
			foreach ($itinerary as $data) {
				foreach (MACROS_CHECK_ON_ITINERARY as $field) {
					if (isset($data[$field]) && !empty($data[$field])) {
						foreach (MACROS as $key => $value) {
							$data[$field] = str_replace($key, $value, $data[$field]);
						}
					}
				}
				if (isset($data['title']) && !empty($data['title'])) {
					$result['titles'][] = $data['title'];
					$result['desc'][] = $data['desc'];
				}
			}
		}
		return $result;
	}

	public function getArticleGallery($id)
	{
		$result = array();
		$query = $this->db_cms->query("select * from rep_cms_article_photo_gallery where article_id = {$id}");
		$i = 0;
		foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
			$result[$i]['id'] = $row['id'];
			$result[$i]['image_link'] = $row['image_link'];
			$result[$i]['image_title'] = '';
			$result[$i]['image_alt'] = '';

			if ($image_data = json_decode($row['image_data'], true)) {
				if (isset($image_data['title']))
					$result[$i]['image_title'] = $image_data['title'];
				if (isset($image_data['alt']))
					$result[$i]['image_alt'] = $image_data['alt'];
			}

			$i++;
		}

		return $result;
	}

	public function getFeaturedArticlesBySiteId($site_id)
	{
		$result = [];
		$sql = "SELECT * FROM rep_cms_article_feature WHERE site_id = '$site_id' ";
		$query = $this->db_cms->query($sql);
		$top = $bottom = [];
		foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
			if ($row['type'] == 0) {
				if ($this->getArticleInfoByArticleId($row['article_id']) != false) {
					$top[] = $this->getArticleInfoByArticleId($row['article_id']);
				}
			} else {
				$bottom[] = $this->getArticleInfoByArticleId($row['article_id']);
			}
		}
		$result['top'] = $top;
		$result['bottom'] = $bottom;
		return $result;
	}

	public function getArticleInfoByArticleId($article_id)
	{
		$result = [];
		$sql = "select ca.id, user_id, title, author, content, ca.image, image2, link, meta_title, keywords, sub_head, rating, timestamp, cc.name as category, cc.id as category_id, rcs.name as subcategory, cca.subcategory_id as subcategory_id, cu.name as username,slug_title, ca.old_image, ca.images_attr from rep_cms_category cc, rep_cms_subcategory rcs, rep_cms_articles ca, rep_cms_catart cca, rep_user cu where cca.category_id = cc.id and cca.subcategory_id = rcs.id and ca.user_id = cu.id and cca.article_id = ca.id and ca.id = '{$article_id}' AND ca.status = 1";
		$query = $this->db_cms->query($sql);
		$row = $query->fetch();

		if (!$row) {
			return false;
		}

		$result = array_map(array($this, 'replaceChars'), $row);

		return $result;
	}

	public function searchKeyword_new($siteID, $keyword, $skip_unedited_articles = false)
	{
		$keyword = addslashes(trim($keyword));
        $siteID = intval($siteID); // sanitize ID

        // Break into words for LIKE search
        $keywordParts = preg_split('/\s+/', $escapedKeyword);
        $likeConditions = array_map(function($word) {
            $w = addslashes(trim($word));
            return "(ca.title LIKE '%$w%' OR ca.keywords LIKE '%$w%')";
        }, $keywordParts);

        $likeQuery = implode(' AND ', $likeConditions);

        // Final SQL
        $sql = "
                SELECT 
                    ca.id, ca.title, ca.slug_title, ca.image, ca.images_attr, ca.meta_title, 
                    ca.keywords, ca.sub_head, ca.content, 
                    cc.name AS category, s.name AS sub_category 
                FROM 
                    rep_cms_category cc, 
                    rep_cms_articles ca, 
                    rep_user cu, 
                    rep_cms_catart cca 
                    LEFT JOIN rep_cms_subcategory s ON s.id = cca.subcategory_id 
                WHERE 
                    cc.id = cca.category_id 
                    AND ca.user_id = cu.id 
                    AND cca.article_id = ca.id 
                    AND cc.site_id = '$siteID'
                    AND (
                        MATCH(ca.title, ca.keywords) AGAINST ('\"$escapedKeyword\"' IN BOOLEAN MODE) 
                        OR ($likeQuery)
                    )
                    AND (cu.approved = '2' OR ca.approved = 1) 
                    AND ca.status = 1 
                    $and_condition
                ORDER BY cca.article_id DESC 
                LIMIT 100";


        //$and_condition = ( $skip_unedited_articles == true ) ? " AND ca.is_index = 1" : '';
		//$sql = "SELECT ca.id, ca.title, ca.slug_title, ca.image, ca.images_attr, ca.meta_title, ca.keywords, ca.sub_head, ca.content, cc.name AS category, s.name AS sub_category FROM rep_cms_category cc, rep_cms_articles ca, rep_user cu, rep_cms_catart cca LEFT JOIN rep_cms_subcategory s ON s.id = cca.subcategory_id WHERE cc.id = cca.category_id AND ca.user_id = cu.id AND cca.article_id = ca.id AND cc.site_id = '" . $siteID . "' AND ( match(ca.title, ca.keywords) against('\"{$keyword}\"' in boolean mode) OR ( ca.title LIKE '%$keyword%' OR ca.keywords like '%$keyword%' ) ) AND (cu.approved = '2' or ca.approved = 1) AND ca.status = 1 $and_condition order by cca.article_id desc LIMIT 100";
		$query = $this->db_cms->query($sql);

		$result = [];
		foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
			$row = array_map(array($this, 'replaceChars'), $row);
			$this->macrosReplacement($row);
			if (!isset($row['slug_title']) || empty($row['slug_title']) || $row['slug_title'] == "") {
				$row['slugurl'] = $this->generateSlug($row['title']);
			} else {
				$row['slugurl'] = $this->generateSlug($row['slug_title']);
			}
			$result[] = $row;
		}

		return $result;
	}

	public function searchKeyword($siteID, $keyword, $skip_unedited_articles = false)
	{
		$keyword = addslashes(trim($keyword));
		$and_condition = ( $skip_unedited_articles == true ) ? " AND ca.is_index = 1" : '';
		$sql = "SELECT ca.id, ca.title, ca.slug_title, ca.image, ca.images_attr, ca.meta_title, ca.keywords, ca.sub_head, cc.name AS category, s.name AS sub_category FROM rep_cms_category cc, rep_cms_articles ca, rep_user cu, rep_cms_catart cca LEFT JOIN rep_cms_subcategory s ON s.id = cca.subcategory_id WHERE cc.id = cca.category_id AND ca.user_id = cu.id AND cca.article_id = ca.id AND cc.site_id = '" . $siteID . "' AND ( match(ca.title, ca.keywords) against('\"{$keyword}\"' in boolean mode) OR ( ca.title LIKE '%$keyword%' OR ca.keywords like '%$keyword%' ) ) AND (cu.approved = '2' or ca.approved = 1) AND ca.status = 1 $and_condition order by cca.article_id desc LIMIT 100";
		$query = $this->db_cms->query($sql);

		$result = [];
		foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
			$row = array_map(array($this, 'replaceChars'), $row);
			$this->macrosReplacement($row);
			if (!isset($row['slug_title']) || empty($row['slug_title']) || $row['slug_title'] == "") {
				$row['slugurl'] = $this->generateSlug($row['title']);
			} else {
				$row['slugurl'] = $this->generateSlug($row['slug_title']);
			}
			$result[] = $row;
		}

		return $result;
	}

	public function getArticleDetails($id, $check_status = 1)
	{
		$hash = self::REDIS_KEY_PREFIX . "_articles_all";
		$result = false;//$this->getRedisHashKeyData($hash, $id);
		if (empty($result) || (isset($_GET['is_debug']) && $_GET['is_debug'] == 1)) {
			$sql = "SELECT ca.id, ca.user_id, ca.title, cc.name as 'category', ca.author, ca.timestamp, ca.is_index, ca.content, ca.image, ca.old_image, ca.images_attr, ca.meta_title, ca.keywords, ca.approved_keywords, ca.sub_head, ca.slug_title, ca.timestamp , s.name AS sub_category
				FROM rep_cms_articles ca
				inner join rep_user cu ON  ca.user_id = cu.id
				inner join rep_cms_catart cca ON cca.article_id = ca.id
				inner join rep_cms_category cc ON cca.category_id = cc.id
				LEFT JOIN rep_cms_subcategory s ON s.id = cca.subcategory_id
				WHERE ca.status !='2' AND ca.id = $id";
			if ($check_status) {
				$sql .= " AND (cu.approved = '2' or ca.approved = 1) ";
			}
			$sql .= " order by s.name desc limit 1";

			$query = $this->db_cms->query($sql);
			if (isset($_GET['is_debug']))	print $sql;
			$result = $query->fetch(PDO::FETCH_ASSOC);
			$this->macrosReplacement($result);
			if (!empty($result)) {
				$result['article_details'] = $this->getArticleExtendedDetail($id);
				$result['article_gallery'] = $this->getArticleGallery($id);
				if(empty($result['author'])){
					$author = "Priyanka Saxena";
				}else{
					$author = $result['author'];
				}
				$result['author_image'] = !empty($this->getAuthorBio($author))?$this->getAuthorBio($author)['image']:"";
				$result['author_bio'] = !empty($this->getAuthorBio($author))?$this->getAuthorBio($author)['user_bio']:"";
				$result['author_name'] = !empty($this->getAuthorBio($author))?$this->getAuthorBio($author)['firstname']." ".$this->getAuthorBio($author)['lastname']:"";

				$this->setRedisHashKeyData($hash, $id, $result);
			}
		}

		return $result;
	}

	public function setRedisKeyData($key, $data, $ttl = self::TTL)
	{
		try {
			$redis_port = $this->domainwiseRedisPort('WRITE_PORT');
			if (empty($redis_port)) $redis_port = self::REDIS_PORT_WRITE;

			// Fix 2: Guard against stale persistent connections (PHP 8.2 FPM issue)
			try {
				$this->_redis->pconnect(self::REDIS_HOST, $redis_port);
				$this->_redis->ping();
			} catch (Exception $e) {
				// Stale connection detected, fallback to fresh connect
				$this->_redis->connect(self::REDIS_HOST, $redis_port);
			}

			if (version_compare(PHP_VERSION, '8.0.0', '>=')) {
				// PHP 8.0+ requires options array
				return $this->_redis->set($key, json_encode($data), ['EX' => (int)$ttl]);
			} else {
				// PHP 5.6, 7.0, 7.4 — plain integer TTL works fine
				return $this->_redis->set($key, json_encode($data), (int)$ttl);
			}
		} catch (Exception $e) {
			// var_dump($e->getMessage(), "LINE : " . __LINE__);
		}
	}

	public function setRedisHashKeyData($hash, $key, $data, $ttl = self::TTL)
	{
		try {
			$redis_port = $this->domainwiseRedisPort('WRITE_PORT');
			if (empty($redis_port)) $redis_port = self::REDIS_PORT_WRITE;

			// Fix 2: Guard against stale persistent connections (PHP 8.2 FPM issue)
			try {
				$this->_redis->pconnect(self::REDIS_HOST, $redis_port);
				$this->_redis->ping();
			} catch (Exception $e) {
				// Stale connection detected, fallback to fresh connect
				$this->_redis->connect(self::REDIS_HOST, $redis_port);
			}

			$this->_redis->hSet($hash, $key, json_encode($data));
			$this->_redis->expire($hash, (int)$ttl);
		} catch (Exception $e) {
			// var_dump($e->getMessage(), "LINE : " . __LINE__);
		}
	}

	public function getRedisKeyData($key)
	{
		try {
			$redis_port = $this->domainwiseRedisPort('READ_PORT');
			if (empty($redis_port)) $redis_port = self::REDIS_PORT_READ;

			// Fix 1: Stale connection guard (PHP 8.2 FPM safe)
			try {
				$this->_redis->pconnect(self::REDIS_HOST, $redis_port);
				$this->_redis->ping();
			} catch (Exception $e) {
				$this->_redis->connect(self::REDIS_HOST, $redis_port);
			}

			$value = $this->_redis->get($key);

			// Fix 2: Guard against false/null before json_decode
			// PHP 8.x: get() returns false if key missing — json_decode(false) throws TypeError
			if ($value === false || $value === null) {
				return null;  // Key does not exist or expired
			}

			return json_decode($value, true);

		} catch (Exception $e) {
			// Fix 3: Log error and return false so caller can distinguish
			// null  = key not found (valid)
			// false = redis error (something went wrong)
			// var_dump($e->getMessage(), "LINE : " . __LINE__);
			return false;
		}
	}

	public function getRedisHashKeyData($key, $field)
	{
		try {
			$redis_port = $this->domainwiseRedisPort('READ_PORT');
			if (empty($redis_port)) $redis_port = self::REDIS_PORT_READ;

			// Fix 1: Stale connection guard (PHP 8.2 FPM safe)
			try {
				$this->_redis->pconnect(self::REDIS_HOST, $redis_port);
				$this->_redis->ping();
			} catch (Exception $e) {
				$this->_redis->connect(self::REDIS_HOST, $redis_port);
			}

			// Fix 4: Use correct casing hGet (not hget) — PHP 8.x is strict
			$value = $this->_redis->hGet($key, $field);

			// Fix 2: Guard false/null before json_decode
			// hGet() returns false if key or field doesn't exist
			if ($value === false || $value === null) {
				return null;  // Hash key or field does not exist
			}

			return json_decode($value, true);

		} catch (\Exception $ex) {
			// Fix 3: Return false so caller can distinguish error vs not-found
			// var_dump($ex->getMessage(), "LINE : " . __LINE__);
			return false;
		}
	}

	public function getArticlesByNumKeyword($categoryName, $subcategoryName = null, $number = 10, $start = 0, $sort_by = "")
	{
		$result = array();
		if (!empty($categoryName)) {
			$sql = "select ca.id, ca.user_id ,ca.images_attr, title, author, content, ca.image, ca.old_image, link, meta_title, keywords, timestamp,  cca.subcategory_id, cc.name as category, cu.name as username, ca.slug_title from rep_cms_category cc, rep_cms_articles ca, rep_cms_catart cca, rep_user cu where cc.id = cca.category_id and ca.user_id = cu.id and cca.article_id = ca.id and cc.name = '" . $categoryName . "' and (cu.approved = '2' or ca.approved = 1) AND ca.status = 1";
			$sql .= " and title REGEXP '^[0-9]'";
			$sql .= " order by cca.article_id desc";
		}
		$debug = isset($_GET['debug']) ? $_GET['debug'] : 0;
		if ($debug == 1) {
			echo $sql;
		}
		$query = $this->db_cms->query($sql);
		$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
		foreach ($query_result as $row) {
			$row = array_map(array($this, 'replaceChars'), $row);
			if (!isset($row['slug_title']) || empty($row['slug_title']) || $row['slug_title'] == "") {
				$row['slugurl'] = $this->generateSlug($row['title']);
			} else {
				$row['slugurl'] = $this->generateSlug($row['slug_title']);
			}
			$result[] = $row;
		}
		$resultCount = count($result);
		if ($result) {
			if ($number != 0) {
				$result =  array_slice($result, $start, $number);
			}
			$result['count'] = $resultCount;
			return $result;
		} else {
			return false;
		}
	}

	public function getTrendingKeywords($siteID, $num=5, $category = '') {
		$trendingKeywords = array();

		if ($category == '')
			$sql = "select a.keywords from rep_cms_articles a, rep_cms_category c, rep_cms_catart ca where a.id = ca.article_id and ca.category_id = c.id and a.keywords != '' and c.site_id = '".$siteID."' and (a.user_id = 10 or a.approved = 1) AND a.status = 1 order by a.timestamp desc limit ".$num;
		else
			$sql = "select a.keywords from rep_cms_articles a, rep_cms_category c, rep_cms_catart ca, rep_cms_sitemap s where a.id = ca.article_id and ca.category_id = c.id and a.id = s.article_id and s.category = '".$category."' and a.keywords != '' and c.site_id = '".$siteID."' and (a.user_id = 10 or a.approved = 1) AND a.status = 1 order by a.timestamp desc limit ".$num;

		$query = $this->db_cms->query($sql);
		$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
		if (count($query_result)) {
			foreach($query_result as $row) {
				if(strpos($row['keywords'], ",") !== false)
					$keywordsEx = explode(",", $row['keywords']);
				else
					$keywordsEx = array($row['keywords']);

				foreach($keywordsEx as $keyword)
				{
					if(!in_array(trim($keyword), $trendingKeywords))
					{
						$trendingKeywords[] = trim($keyword);
						break;
					}
				}
			}
		}

		return $trendingKeywords;
	}

	public function getArticleCountAll($siteID, $skip_unedited_articles = false) {
		$and_condition = ( $skip_unedited_articles == true ) ? " AND ca.is_index = 1" : '';
		$redis_key = "getArticleCount_" . $siteID . "_" . md5($skip_unedited_articles);
		$count = $this->getRedisKeyData($redis_key);
		if (empty($count)) {
		$query = $this->db_cms->query("select count(ca.id) as count from rep_cms_category cc, rep_cms_articles ca, rep_cms_catart cca, rep_user cu where cc.id = cca.category_id and ca.user_id = cu.id and cca.article_id = ca.id and cc.site_id = '".$siteID."' and (cu.approved = '2' or ca.approved = 1) AND ca.status = 1 $and_condition order by ca.id desc");
		$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
		if (!count($query_result)) {
			$count = 0;
		} else {
			$count = $query_result[0]['count'];
		}
		$this->setRedisKeyData($redis_key, $count);
		}
		return $count;
	}

	public function getRecentArticles($siteID, $number = 5, $skip = 0) {
		$recentArticles = array();
		$cSql = "select ca.*, cca.subcategory_id as subcategoryId, cc.name as category, cu.name as username from rep_cms_category cc, rep_cms_articles ca, rep_cms_catart cca, rep_user cu where ca.user_id = cu.id and cc.id = cca.category_id and cca.article_id = ca.id and cc.site_id = ".$siteID." and (cu.approved = '2' or ca.approved = 1) AND ca.status = 1 order by ca.id desc limit ".$skip.", ".$number;
		$query = $this->db_cms->query($cSql);
		$query_result = $query->fetchAll(PDO::FETCH_ASSOC);

		if(count($query_result)) {
			foreach($query_result as $row) {
				$tagArr = array();
				$sSql = "select tag from rep_cms_sitemap where article_id = ".$row['id'];
				$squery = $this->db_cms->query($sSql);
				$squery_res = $squery->fetchAll(PDO::FETCH_ASSOC);
				if(count($squery_res)) {
					foreach($squery_res as $srow) {
						$tagArr[] = $srow['tag'];
					}
				}

				$row['related'] = $tagArr;
				$row = array_map(array($this, 'replaceChars'), $row);
				$recentArticles[] = $row;
			}
		}

		return $recentArticles;
	}

	//--------  CMS RSOC Factory Content Start --------
	public function getArticlesBySiteId( $siteID, $limit = 5, $skip = 0 ) {
		$sql = "SELECT ca.id, ca.timestamp, ca.title, ca.slug_title, ca.image, ca.old_image, ca.meta_title, ca.keywords, ca.sub_head, cc.name AS category, s.name AS sub_category FROM rep_cms_category cc, rep_cms_articles ca, rep_user cu, rep_cms_catart cca LEFT JOIN rep_cms_subcategory s ON s.id = cca.subcategory_id WHERE cc.id = cca.category_id AND ca.user_id = cu.id AND cca.article_id = ca.id AND cc.site_id = '" . $siteID . "' AND ca.status = 1 AND ca.is_index = 1 ORDER BY ca.id DESC LIMIT $skip,$limit";
		$query = $this->db_cms->query($sql);
		if( empty( $query ) ) return [];
		$result = $query->fetchAll(PDO::FETCH_ASSOC);

		if( empty( $result ) ) return [];

		$data = [];
		foreach( $result as $row ) {
			$row = array_map([$this, 'replaceChars'], $row);
			$data[] = $row;
		}
		return $data;
	}

	public function getArticlesBySiteIdCategoryId( $siteID, $categoryId, $limit = 5, $skip = 0 ) {
		$sql = "SELECT ca.id, ca.timestamp, ca.title, ca.slug_title, ca.image, ca.images_attr, ca.link, ca.old_image, ca.meta_title, ca.keywords, ca.sub_head, ca.rating, cc.name AS category, s.name AS sub_category FROM rep_cms_category cc, rep_cms_articles ca, rep_user cu, rep_cms_catart cca LEFT JOIN rep_cms_subcategory s ON s.id = cca.subcategory_id WHERE cc.id = cca.category_id AND ca.user_id = cu.id AND cca.article_id = ca.id AND cc.site_id = '" . $siteID . "' AND ca.status = 1 AND ca.is_index = 1 AND cc.id = " . $categoryId . " AND ( cu.approved = '2' OR ca.approved = 1 ) ORDER BY cca.article_id DESC LIMIT $skip,$limit";
		$query = $this->db_cms->query($sql);
		if( empty( $query ) ) return [];
		$result = $query->fetchAll(PDO::FETCH_ASSOC);

		if( empty( $result ) ) return [];

		$data = [];
		foreach( $result as $row ) {
			$row = array_map([$this, 'replaceChars'], $row);
			$data[] = $row;
		}
		return $data;
	}

	public function getArticleCountBySiteIdCategoryId( $siteID, $categoryId ) {
		$sql = "SELECT count( ca.id ) as count FROM rep_cms_category cc, rep_cms_articles ca, rep_user cu, rep_cms_catart cca LEFT JOIN rep_cms_subcategory s ON s.id = cca.subcategory_id WHERE cc.id = cca.category_id AND ca.user_id = cu.id AND cca.article_id = ca.id AND cc.site_id = '" . $siteID . "' AND ca.status = 1 AND ca.is_index = 1 AND cc.id = " . $categoryId . " AND ( cu.approved = '2' OR ca.approved = 1 ) ORDER BY cca.article_id DESC";
		$query = $this->db_cms->query( $sql );
		if( empty( $query ) ) return 0;
		$result = $query->fetch( PDO::FETCH_ASSOC );
		$count = isset( $result['count'] ) ? $result['count'] : 0;
		return $count;
	}

	public function getSiteCategoriesBySiteId( $site_id ) {
		$redis_key = md5("getSiteCategoriesBySiteId_" . $site_id);
		$categories = $this->getRedisKeyData($redis_key);
		if (empty($categories)) {
		$sql = "SELECT id, name, site_id FROM rep_cms_category WHERE site_id = '{$site_id}'";
		$query = $this->db_cms->query( $sql );
		if( empty( $query ) ) return [];
		$result = $query->fetchAll( PDO::FETCH_ASSOC );

		$categories = [];
		foreach( $result as $row ) {
			$categories[$row['name']] = $row;
		}
		$this->setRedisKeyData($redis_key, $categories);
		}

		return $categories;
	}
	//--------  CMS RSOC Factory Content End --------

	public function getRelatedArticles($siteID, $article_id, $number=5, $start = 0) {
		$result = array();
		$cat_name = '';
		$query = $this->db_cms->query("select ca.timestamp , cc.name as name,ca.slug_title from rep_cms_category cc, rep_cms_articles ca, rep_cms_catart cca, rep_user cu where ca.id = cca.article_id and ca.user_id = cu.id and cca.category_id = cc.id and ca.id = {$article_id} and (cu.approved = '2' or ca.approved = 1) AND ca.status = 1 order by cca.category_id asc limit 1");
		$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
		if (count($query_result) == 1) {
			$row = $query_result[0];
			$cat_name = $row['name'];
		}

		$query = $this->db_cms->query("select ca.id , ca.timestamp , title, ca.slug_title, author, content, ca.image,ca.images_attr, link, meta_title, keywords, cca.subcategory_id, cc.name as category from rep_cms_category cc, rep_cms_articles ca, rep_cms_catart cca, rep_user cu where cc.id = cca.category_id and cca.article_id = ca.id and ca.user_id = cu.id and cc.site_id = '".$siteID."' and cc.name = '".$cat_name."' and ca.id != {$article_id}  and (cu.approved = '2' or ca.approved = 1) AND ca.status = 1 order by ca.id desc");
		$query_result = $query->fetchAll(PDO::FETCH_ASSOC);

		if (count($query_result)) {
			foreach($query_result as $row) {
				$row = array_map(array($this, 'replaceChars'), $row);
				$result[] = $row;
			}
		}

		if ($result) {
			return array_slice($result, $start, $number);
		} else {
			return false;
		}
	}

	public function getNextArticle($id, $siteID)
	{
		$hash = self::REDIS_KEY_PREFIX . $siteID . "_getNextArticle";
		$result = $this->getRedisHashKeyData($hash, $id);

		if (empty($result) || (isset($_GET['is_debug']) && $_GET['is_debug'] == 1)) {
			$sql = "SELECT ca.id, ca.slug_title, ca.title, cc.name as 'category'
						FROM rep_cms_category cc, rep_cms_articles ca, rep_cms_catart cca, rep_user cu
						WHERE cc.id = cca.category_id and ca.user_id = cu.id and cca.article_id = ca.id and cc.site_id = ".$siteID." and ca.id > ".$id." and (cu.approved = '2' or ca.approved = 1) AND ca.status = 1 order by ca.id asc limit 1";
		    
			$query = $this->db_cms->query($sql);
			$result = $query->fetch(PDO::FETCH_ASSOC);

			if (!empty($result)) {
				$result['article_details'] = $this->getArticleExtendedDetail($id);
				$result['article_gallery'] = $this->getArticleGallery($id);

				$this->setRedisHashKeyData($hash, $id, $result);
			}
		}

		return $result;
	}

	public function getPrevArticle($id, $siteID)
	{
		$hash = self::REDIS_KEY_PREFIX . $siteID . "_getPrevArticle";
		$result = $this->getRedisHashKeyData($hash, $id);

		if (empty($result) || (isset($_GET['is_debug']) && $_GET['is_debug'] == 1)) {
			$sql = "SELECT ca.id, ca.slug_title, ca.title, cc.name as 'category'
					FROM rep_cms_category cc, rep_cms_articles ca, rep_cms_catart cca, rep_user cu
					WHERE cc.id = cca.category_id and ca.user_id = cu.id and cca.article_id = ca.id and cc.site_id = ".$siteID." and ca.id < ".$id." and (cu.approved = '2' or ca.approved = 1) AND ca.status = 1 order by ca.id desc limit 1";
		    
			$query = $this->db_cms->query($sql);
			$result = $query->fetch(PDO::FETCH_ASSOC);

			if (!empty($result)) {
				$result['article_details'] = $this->getArticleExtendedDetail($id);
				$result['article_gallery'] = $this->getArticleGallery($id);

				$this->setRedisHashKeyData($hash, $id, $result);
			}
		}

		return $result;
	}

	public function searchArticlesCount($search, $siteID, $categoryName) {
		// $search = trim($this->db_cms->quote($search));
		$sql = "select count(*) as count from rep_cms_category cc, rep_cms_articles ca, rep_cms_catart cca, rep_user cu where ca.user_id = cu.id and match(title, keywords,content) against('" . $search . "' in boolean mode) and cc.id = cca.category_id and cca.article_id = ca.id and cc.site_id = " . $siteID . " AND ca.status = 1";
		if (trim($categoryName) != '') {
			$sql .= " and cc.name = '" . $categoryName . "'";
		}
		$sql .= " and (cu.approved = '2' or ca.approved = 1) ";
	
		// $sql = "select count(*) as count from rep_cms_category cc, rep_cms_articles ca, rep_cms_catart cca, rep_user cu where ca.user_id = cu.id and match(title, keywords) against('\"".$search."\"' in boolean mode) and cc.id = cca.category_id and cca.article_id = ca.id and cc.site_id = ".$siteID." and (cu.approved = '2' or ca.approved = 1) AND ca.status = 1";
		// if(trim($categoryName) != '')
		// 	$sql .= " and cc.name = '".$categoryName."'";
		$count = 0;
		if (!$count) {
			$query = $this->db_cms->query($sql);
			if (!$query)	return $count;
			$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
			if (!count($query_result)) {
				return 0;
			}
			$count = $query_result[0]['count'];
		}

                return $count;
	}
	public function getVideosBySiteId($site_id, $number = 10, $start = 0)
	{
		$key = md5("getVideosBySiteId_".$site_id);
		$ttl = 60 * 30; // 2 hours
		$result = $this->getRedisKeyData($key);

		if(!is_array($result))
		{
			$result = array();

			$query = $this->db_admin->query("select v.*, c.name as category from rep_videos v, rep_videos_cat c where (v.cat_id = c.id) and c.site_id = '".$site_id."' and v.enabled = 1 order by v.id desc");
			$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
			if (count($query_result) > 0) {
			{
				foreach($query_result as $row) 
				{
					$row = array_map('replaceChars', $row);
					if($row['id'] < 10) {
						$row['id'] = '00'.$row['id'];
					} else if($row['id'] >= 10 && $row['id'] < 100) {
						$row['id'] = '0'.$row['id'];
					}
					$result[] = $row;
				}
			}
			$this->setRedisKeyData($key, $result);
		}
		if (count($result) > 0) {
			return array_slice($result, $start, $number);
		} else {
			return $result;
		}
	}
	}
	public function getGalleries($siteID, $number = 10, $start = 0, $approved = 1)
	{
		$result = array();

		$sql = "select name, id from rep_cms_gallery where site_id = ".$siteID;
		if($approved)
			$sql .= " and (user_id = 10 or approved = 1)";
		
		$sql .= " order by id desc limit ".$start.", ".$number;
		
		$key = md5($sql);
		$ttl = 60 * 30; // 2 hours
		$result = $this->getRedisKeyData($key);

		if(!is_array($result))
		{
			$gquery = $this->db_cms->query($sql);
			$gquery_result = $gquery->fetchAll(PDO::FETCH_ASSOC);

			$result = array();
			if($gquery){
				if(count($gquery_result) > 0) {
					foreach($gquery_result as $grow) {
						$image = '';
						$query = $this->db_cms->query("select image from rep_cms_gallery_pages where gallery_id = ".$grow['id']." and image != '' order by id asc limit 1");
						$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
						if($query){
							if (count($query_result) == 1) {
								$row = $query_result[0];
								$image = $row['image'];
							}
						}
						$result[] = array('id'=>$grow['id'], 'name'=>$grow['name'], 'image'=>$image);
					}
				}
			}
			$this->setRedisKeyData($key, $result);
		}
		return $result;
	}
	public function getMetaDetails($catId, $subcatId) {
		if ($subcatId == '') {
			//fetch details of category
			$query = $this->db_cms->query("select meta_detail_title, meta from rep_cms_category where id = '{$catId}'");
			$key = md5("getMetaDetails_".$catId);
			
		} else {
			//fetch details of subcategory
			$query = $this->db_cms->query("select meta_detail_title, meta_detail_description from rep_cms_subcategory where id = '{$subcatId}' and cat_id = '{$catId}'");
			$key = md5("getMetaDetails_".$catId."_".$subcatId);
		}
        $ttl = 60 * 30; // 2 hours
		$result = $this->getRedisKeyData($key);
		if(!is_array($result) || empty($result))
		{
			$result = array();
			
			$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
			if (count($query_result) > 0) 
			{
				
				foreach($query_result as $row) {
						$result['meta_title'] = $row['meta_detail_title'];
						$result['meta_description'] = $row['meta']?$row['meta']:$row['meta_detail_description'];
				}
			}
			$this->setRedisKeyData($key, $result);
		}
		return $result;
	}
	public function getGalleryDetail($galleryID, $page)
	{
		$key = md5("getGalleryDetail_241_".$galleryID."_".$page);
		$ttl = 60 * 30; // 2 hours
		$row = $this->getRedisKeyData($key);

		if(!is_array($row))
		{
			$row = array();
			$query = $this->db_cms->query("select g.name as galleryName, gp.id, gp.title, gp.text, gp.image from rep_cms_gallery g, rep_cms_gallery_pages gp where gp.gallery_id = g.id and g.id = ".$galleryID." order by gp.id asc limit ".($page-1).", 1");
			$query_result = $query->fetchAll(PDO::FETCH_ASSOC);

			if (count($query_result) == 0) {
				return false;
			}
			$row = $query_result[0];
			$row = array_map('replaceChars', $row);

			$this->setRedisKeyData($key, $row);
		}
		return $row;
	}
	public function getGalleryCount($galleryID)
	{
		$count = 0;
		$key = md5("getGalleryCount_".$galleryID);
		$ttl = 60 * 30; // 2 hours
		$result = $this->getRedisKeyData($key);

		if(!$count)
		{
			$query = $this->db_cms->query("select count(*) as count from rep_cms_gallery g, rep_cms_gallery_pages gp where gp.gallery_id = g.id and g.id = ".$galleryID);
			$query_result = $query->fetchAll(PDO::FETCH_ASSOC);

			if (count($query_result) == 0) {
				return false;
			}
			$count = count($query_result);

			$this->setRedisKeyData($key, $result);
		}
		return $count;
	}
	public function getNextGalleryDetailBySiteID($galleryID, $siteID, $page)
	{
		$key = md5("getNextGalleryDetailBySiteID".$galleryID."_".$siteID."_".$page);
		$ttl = 60 * 30; // 2 hours
		$row = $this->getRedisKeyData($key);

		if(!is_array($row))
		{
			$row = array();
			$query = $this->db_cms->query("select g.name as galleryName, g.id as gid, gp.id, gp.title, gp.text, gp.image from rep_cms_gallery g, rep_cms_gallery_pages gp where gp.gallery_id = g.id and g.id > ".$galleryID." and g.site_id = ".$siteID." and (g.user_id = 10 or g.approved = 1) order by g.id asc limit ".($page-1).", 1");
			$query_result = $query->fetchAll(PDO::FETCH_ASSOC);

			if (count($query_result) == 0) {
				//return false;
				$query = $this->db_cms->query("select g.name as galleryName, g.id as gid, gp.id, gp.title, gp.text, gp.image from rep_cms_gallery g, rep_cms_gallery_pages gp where gp.gallery_id = g.id and g.id > 0 and g.site_id = ".$siteID." and (g.user_id = 10 or g.approved = 1) order by g.id asc limit ".($page-1).", 1");
				$query_result2 = $query->fetchAll(PDO::FETCH_ASSOC);

				if (count($query_result2) == 0)
					return false;
			}
			$row = $query_result[0];
			$row = array_map('replaceChars', $row);

			$this->setRedisKeyData($key, $row);
		}
		return $row;
	}

	public function getMetaContent($category_id, $subcat) {
		$key = md5("catMeta" . $category_id);
		$ttl = 60 * 30;
		$row = $this->getRedisKeyData($key);
		if (!is_array($row)) {
			$row = array();
			$sql1 = "SELECT `meta`, `meta_detail_title` FROM `rep_cms_category` WHERE `id` = " . $category_id;
			$query = $this->db_cms->query($sql1);
			$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
			if (count($query_result) != 0) {
				$row = $query_result[0];
				$row = array_map('replaceChars', $row);
				$this->setRedisKeyData($key, $row);
			}
		}
		$result = array(
			'category' => array(
				'meta_title' => '',
				'meta_description' => ''
			),
			'sub_category' => array(
				'meta_title' => '',
				'meta_description' => ''
			)
		);
		if (count($row)) {
			$result['category']['meta_title'] = $row['meta_detail_title'];
			$result['category']['meta_description'] = $row['meta'];
		}

		if ($subcat != "0") {
			$key = md5("subcatMeta" . $category_id ) . md5($subcat);
			$ttl = 60 * 30;
			$row = $this->getRedisKeyData($key);
			if (!is_array($row)) {
				$row = array();
				$sql2 = "SELECT `meta_detail_title`, `meta_detail_description` FROM `rep_cms_subcategory` WHERE `cat_id` = '" . $category_id . "' AND SUBSTRING_INDEX(name, '|', -1) = '" . $subcat . "'";
				$query = $this->db_cms->query($sql2);
				$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
				if (count($query_result) != 0) {
					$row = $query_result[0];
					$row = array_map('replaceChars', $row);
					$this->setRedisKeyData($key, $row);
				}
			}
			if (count($row)) {
				$result['sub_category']['meta_title'] = $row['meta_detail_title'];
				$result['sub_category']['meta_description'] = $row['meta_detail_description'];
			}
		}
		return $result;
	}

	public function macrosReplacement(&$result) {
		foreach (MACROS_CHECK_ON as $field) {
			if (isset($result[$field]) && !empty($result[$field])) {
				foreach (MACROS as $key => $value) {
					$result[$field] = str_replace($key, $value, $result[$field]);
				}
			}
		}
	}

	public function getSiteVideos($siteID, $trailer = 0, $number = 10, $start = 0)
	{
		$sql = "select * from rep_videos where site_id = ".$siteID." and trailer = ".$trailer." and enabled = 1 order by id desc limit ".$start.", ".$number;
		$key = md5("getSiteVideos".$siteID);
		$ttl = 60 * 30; // 2 hours
		$result = $this->getRedisKeyData($key);

		if(!is_array($result))
		{
			$result = array();
			$query = $this->db_admin->query($sql);
			$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
			if (count($query_result) != 0) {
				foreach($query_result as $row) {
					$row = array_map('replaceChars', $row);
					$result[] = $row;
				}
			}
			$this->setRedisKeyData($key, $result);
		}

		return $result;
	}

	public function getVideoCategories($site_id = 0)
	{
		$key = md5("getVideoCategories_12_n".$site_id);
		$ttl = 60 * 30; // 2 hours
		$result = $this->getRedisKeyData($key);

		if(!is_array($result))
		{
			$result = array();
		
			$sql = "select * from rep_videos_cat";
			if($site_id != 0)
				$sql .= " where site_id = ".$site_id;
			$query = $this->db_admin->query($sql);
			$result_array = $query->fetchAll(PDO::FETCH_ASSOC);
			if(count($result_array) > 0) 
			{
				foreach($result_array as $row) 
				{
					$result[] = array('id'=>$row['id'], 'name'=>trim($row['name']));
				}
			}
			$this->setRedisKeyData($key, $result);
		}
		return $result;
	}

	public function getVideoSubCategories($site_id = 0)
	{
		$key = md5("getVideoSubCategories3_in3");
		$ttl = 60 * 30; // 2 hours
		$result = $this->getRedisKeyData($key);

		if(!is_array($result))
		{
			$result = array();
			
			$sql = "select rs.* from rep_videos_subcat rs";
			if($site_id != 0)
				$sql .= ", rep_videos_cat rc where (rs.cat_id = rc.id) and rc.site_id = ".$site_id;
			$query = $this->db_admin->query($sql);
			$result_array = $query->fetchAll(PDO::FETCH_ASSOC);
			if(count($result_array) > 0) 
			{
				foreach($result_array as $row) 
				{
					$result[$row['cat_id']][] = array('id'=>$row['id'], 'name'=>trim($row['name']));
				}
			}
			$this->setRedisKeyData($key, $result);
		}

		return $result;
	}

	public function getVideoSubCategoriesByCategory($category)
	{
		$key = md5("getVideoSubCategoriesByCategory_".$category);
		$ttl = 60 * 30; // 2 hours
		$result = $this->getRedisKeyData($key);

		if(!is_array($result))
		{
			$result = array();
			$query = $this->db_admin->query("select s.* from rep_videos_subcat s, rep_videos_cat c where c.id = s.cat_id and c.name like '".$category."' order by s.id asc");
			$result_array = $query->fetchAll(PDO::FETCH_ASSOC);

			if (count($result_array) > 0) {
				foreach($result_array as $row) {
					$result[] = $row;
				}
			}
			$this->setRedisKeyData($key, $result);
		}

		return $result;
	}

	public function getVideosByCategory($categoryName, $number = 10, $start = 0, $aol = false)
	{
	    if($aol){
	        $table = "rep_aol_videos";
	    }
	    else{
	        $table = "rep_videos";
	    }
		$key = md5($table."_getVideosByCategory_".$categoryName);
		$ttl = 60 * 30; // 2 hours
		$result = $this->getRedisKeyData($key);

		if(!is_array($result))
		{
			$result = array();
			if(!$aol)
				$query = $this->db_admin->query("select v.* from $table v, rep_videos_cat c where (v.cat_id = c.id) and c.name like '".$categoryName."' and v.enabled = 1 order by v.id desc");
			else
				$query = $this->db_admin->query("select v.* from $table v, rep_videos_cat c where (v.cat_id = c.id) and c.name like '".$categoryName."' order by v.id desc");
			
			$result_array = $query->fetchAll(PDO::FETCH_ASSOC);
			if(count($result_array) > 0) 
			{
				foreach($result_array as $row) 
				{
					$row = array_map('replaceChars', $row);
					if($aol){
					    $row['id'] = "aol-".$row['id'];
					}
					else{
					    if($row['id'] < 10) {
					        $row['id'] = '00'.$row['id'];
					    } else if($row['id'] >= 10 && $row['id'] < 100) {
					        $row['id'] = '0'.$row['id'];
					    }
					}
					$result[] = $row;
				}
			}
			$this->setRedisKeyData($key, $result);
		}
		if (count($result) > 0 && $number > 0) {
			return array_slice($result, $start, $number);
		} else {
			return $result;
		}
	}

	public function getVideosCountByCategory($categoryName, $aol = false)
	{
	    if($aol){
	        $table = "rep_aol_videos";
	    }
	    else{
	        $table = "rep_videos";
	    }
		$count = 0;

		$key = md5($table."_getVideosCountByCategory_".$categoryName);
		$ttl = 60 * 30; // 2 hours
		$count = $this->getRedisKeyData($key);

		if(!$count)
		{
			$query = $this->db_admin->query("select count(v.id) as count from $table v, rep_videos_cat c where (v.cat_id = c.id) and c.name like '".$categoryName."' and v.enabled = 1 order by v.id desc");
			$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
			if(count($query_result) != 1) 
			{
				return 0;
			}
			$count = $query_result[0]['count'];
			$this->setRedisKeyData($key, $count);
		}
		return $count;
	}

	public function getVideosBySubCategory($categoryName, $subcategoryName, $number = 10, $start = 0)
	{
		$key = md5("getVideosBySubCategory_".$categoryName."_".$subcategoryName);
		$ttl = 60 * 30; // 2 hours
		$result = $this->getRedisKeyData($key);

		if(!is_array($result))
		{
			$result = array();

			$query = $this->db_admin->query("select v.* from rep_videos v, rep_videos_cat c, rep_videos_subcat s where v.cat_id = c.id and v.subcat_id = s.id and c.name like '".$categoryName."' and s.name like '".$subcategoryName."' and v.enabled = 1 order by v.id desc");
			$result_array = $query->fetchAll(PDO::FETCH_ASSOC);

			if(count($result_array) > 0) 
			{
				foreach($result_array as $row) 
				{
					$row = array_map('replaceChars', $row);
					if($row['id'] < 10) {
						$row['id'] = '00'.$row['id'];
					} else if($row['id'] >= 10 && $row['id'] < 100) {
						$row['id'] = '0'.$row['id'];
					}
					$result[] = $row;
				}
			}
			$this->setRedisKeyData($key, $result);
		}

		if (count($result) > 0) {
			return array_slice($result, $start, $number);
		} else {
			return $result;
		}
	}

	public function getVideosCountBySubCategory($categoryName, $subcategoryName)
	{
		$count = 0;		
		$key = md5("getVideosCountBySubCategory_".$categoryName."_".$subcategoryName);
		$ttl = 60 * 30; // 2 hours
		$result = $this->getRedisKeyData($key);

		if(!$count)
		{
			$query = $this->db_admin->query("select count(v.id) as count from rep_videos v, rep_videos_cat c, rep_videos_subcat s where v.cat_id = c.id and v.subcat_id = s.id and c.name like '".$categoryName."' and s.name like '".$subcategoryName."' v.enabled = 1 order by v.id desc");
			$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
			if(count($query_result) != 1) 
			{
				return 0;
			}
			$count = $query_result[0]['count'];
			$this->setRedisKeyData($key, $count);
		}
		
		return $count;
	}

	public function getSiteVideosCount($siteID, $trailer = 0)
	{
		$count = 0;
		$key = md5("getSiteVideosCount_".$siteID."_".$trailer);
		$ttl = 60 * 30; // 2 hours
		$result = $this->getRedisKeyData($key);

		if(!$count)
		{
			$query = $this->db_admin->query("select count(*) as count from rep_videos where site_id = ".$siteID." and enabled = 1 and trailer = ".$trailer);
			
			$query_result = $query->fetchAll(PDO::FETCH_ASSOC);
			if(count($query_result) != 1) 
			{
				return 0;
			}
			$count = $query_result[0]['count'];
			$this->setRedisKeyData($key, $count);
		}
		return $count;
	}

	public function getSitewiseCategories() {
		$key = self::REDIS_KEY_PREFIX . "_sitewise_categories";
		$result = $this->getRedisKeyData($key);

		if (empty($result)) {
			$sql = "select s.domain, cc.id, cc.name from rep_cms_category cc, rep_cms_site s where s.id = cc.site_id order by s.id asc";
			$query = $this->db_cms->query($sql);
			foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
				$row = array_map([$this, 'replaceChars'], $row);
				$result[$row['domain']][] = array("cat_id" => $row['id'], "cat_name" => $row['name']);
			}
			if (count($result) == 0) {
				$result = array();
			} else {
				$this->setRedisKeyData($key, $result);
			}
		}

		return $result;
	}

	public function getSitewiseCategoriesAndSubcategories() {
		$key = self::REDIS_KEY_PREFIX . "_sitewise_categories_subcategories";
		$result = $this->getRedisKeyData($key);

		if( empty( $result ) ) {
			$sql = "SELECT
				rcs.domain as website,
				rcc.id as category_id,
				rcc.name as category_name,
				rcsc.id as subcategory_id,
				rcsc.name as subcategory_name
			FROM rep_cms_site rcs
			JOIN rep_cms_category rcc ON rcc.site_id = rcs.id
			LEFT JOIN rep_cms_subcategory rcsc ON rcsc.cat_id = rcc.id
			ORDER BY rcs.id ASC";
			$query = $this->db_cms->query($sql);
			$data = $query->fetchAll(PDO::FETCH_ASSOC);

			foreach( $data as $row ) {

				$website = $row['website'];
				$catId   = trim($row['category_id']);

				// Initialize website bucket
				if( !isset( $result[$website] ) ) $result[$website] = [];

				// Initialize category bucket
				if( !isset( $result[$website][$catId] ) ) {
					$result[$website][$catId] = [
						'cat_id'   => $catId,
						'cat_name' => $row['category_name'],
						'subcategories' => []
					];
				}

				// Add subcategory only if exists
				if( !empty($row['subcategory_id'] ) && !empty( $row['subcategory_name'] ) ) {
					$result[$website][$catId]['subcategories'][] = [
						'id'   => $row['subcategory_id'],
						'name' => $row['subcategory_name']
					];
				}
			}

			// Reindex arrays (important for clean JSON output)
			foreach( $result as $website => $categories ) {
				$result[$website] = array_values($categories);
			}

			if( empty( $result ) ) return [];
			$this->setRedisKeyData( $key, $result );
		}

		return $result;
	}

	public function getArticlesUsingCategoryID( $categoryId, $subCategoryId = NULL ) {
		$hash = self::REDIS_KEY_PREFIX . "_all_articles_cat";
		$result = $this->getRedisHashKeyData( $hash, $categoryId );
		$subcategory_condition = NULL;
		$redis_key = $categoryId;

		if( !empty( $subCategoryId ) ) {
			$hash = self::REDIS_KEY_PREFIX . "_all_articles_subcat";
			$result = $this->getRedisHashKeyData( $hash, $subCategoryId );
			$subcategory_condition = "AND s.id = $subCategoryId";
			$redis_key = $subCategoryId;
		}

		if( empty( $result ) ) {
			$sql = "SELECT
						ca.id, ca.timestamp, ca.title, ca.slug_title, ca.image, ca.images_attr, ca.meta_title, ca.keywords, ca.sub_head, ca.rating, cc.name AS category, s.name AS sub_category
					FROM rep_cms_category cc, rep_cms_articles ca, rep_user cu, rep_cms_catart cca
					LEFT JOIN rep_cms_subcategory s ON s.id = cca.subcategory_id
					WHERE
						cc.id = cca.category_id AND ca.user_id = cu.id AND cca.article_id = ca.id
						AND ca.status = 1
						AND cc.id = $categoryId $subcategory_condition
						AND (cu.approved = '2' OR ca.approved = 1)
					ORDER BY cca.article_id DESC";

			$query = $this->db_cms->query( $sql );
			$data = $query->fetchAll( PDO::FETCH_ASSOC );
			if( empty( $data ) ) return [];

			foreach( $data as $row ) {
				$row = array_map( [$this, 'replaceChars'], $row );
				$result[] = $row;
			}

			$this->setRedisHashKeyData( $hash, $redis_key, $result );
		}

		return $result;
	}

	function getAuthorBio($name){
		$author_result = [];
		if(!empty($name)){
			$asql = "SELECT * from cms_users where MATCH (firstname,lastname) AGAINST ('".$name."' IN NATURAL LANGUAGE MODE) ";
			$query = $this->db_cms->query($asql);
			$author_result = $query->fetch(PDO::FETCH_ASSOC);
			if(empty($author_result)){
				$asql = "SELECT * from cms_users where MATCH (firstname,lastname) AGAINST ('Priyanka' IN NATURAL LANGUAGE MODE) ";
				$query = $this->db_cms->query($asql);
				$author_result = $query->fetch(PDO::FETCH_ASSOC);
			}
		}
		return $author_result;
	}

	private function is_valid_keyword($keyword) {
		if (isset($keyword) && empty($keyword))	return 0;
		if (isset($keyword) && strlen($keyword) <= 2)	return 0;
		// Validate the email input
		if (filter_var($keyword, FILTER_VALIDATE_EMAIL)) {
			return 0;
		}
		return 1;
	}

	public function setRedisKeyDataSERP($key, $data, $ttl = self::TTL) {
		try {
			// $redis_port = $this->checkAndGetPort(self::SERP_REDIS_PORT);
			$this->_redis->pconnect(self::SERP_REDIS_HOST, self::SERP_REDIS_PORT, self::SERP_REDIS_TIMEOUT);
			return $this->_redis->set($key, json_encode($data), $ttl);
		} catch (Exception $e) {
			var_dump($e->getMessage(), "LINE : " . __LINE__);
		}
	}

	public function getRedisKeyDataSERP($key) {
		try {
			// $redis_port = $this->checkAndGetPort(self::SERP_REDIS_PORT);
			$this->_redis->pconnect(self::SERP_REDIS_HOST, self::SERP_REDIS_PORT, self::SERP_REDIS_TIMEOUT);
			return json_decode($this->_redis->get($key), true);
		} catch (Exception $e) {
			var_dump($e->getMessage(), "LINE : " . __LINE__);
		}
	}

	public function getRedisKeyStringSERP($key) {
		try {
			// $redis_port = $this->checkAndGetPort(self::SERP_REDIS_PORT);
			$this->_redis->pconnect(self::SERP_REDIS_HOST, self::SERP_REDIS_PORT, self::SERP_REDIS_TIMEOUT);
			return $this->_redis->get($key);
		} catch (Exception $e) {
			var_dump($e->getMessage(), "LINE : " . __LINE__);
		}
	}

	private function getDomain() {
		$pieces = parse_url($_SERVER['HTTP_HOST']);
		return $domain = isset($pieces['host']) ? $pieces['host'] : $pieces['path'];
	}

	private function checkAndGetPort($port) {
		// if (in_array($this->getDomain(), EAST_COAST_DOMAIN_LIST) && array_key_exists($port, EAST_COAST_PORT_MAPPING)) {
		// 	$port = EAST_COAST_PORT_MAPPING[$port];
		// }
		return $port;
	}

	private function domainwiseRedisPort($port_type) {
		$debug = isset($_GET['debug']) ? $_GET['debug'] : false;
		$port = "";
		$domain = $this->getDomain();
		$redis_config = DOMAIN_WISE_REDIS_CONFIGURATION; // assign constant to variable

		if (
			array_key_exists($domain, $redis_config) &&
			!empty($redis_config[$domain][$port_type])
		) {
			$port = $redis_config[$domain][$port_type];
		}
		if ($debug) {
			print "Domain : $domain, $port_type : $port\n";
		}
		return $port;
	}
}
