<?php
declare(strict_types=1);

namespace App\Model\Table;

use Cake\ORM\Table;

class VideosTable extends Table
{
	public array $customFields = [
		"Videos.id",
		"Videos.user_id",
		"Videos.description",
		"Videos.video",
		"Videos.thum",
		"Videos.thum_small",
		"Videos.gif",
		"Videos.view",
		"Videos.sound_id",
		"Videos.privacy_type",
		"Videos.allow_comments",
		"Videos.allow_duet",
		"Videos.duration",
		"Videos.promote",
		"Videos.pin_comment_id",
		"Videos.pin",
		"Videos.location_string",
		"Videos.location_id",
		"Videos.lat",
		"Videos.long",
		"Videos.width",
		"Videos.height",
		"Videos.user_thumbnail",
		"Videos.default_thumbnail"
	];

	public function initialize(array $config): void
	{
		parent::initialize($config);

		$this->setTable('video');
		$this->setPrimaryKey('id');

		// BelongsTo Associations
		$this->belongsTo('Users', [
			'foreignKey' => 'user_id',
			'fields' => USER_FIELDS,
		]);

		$this->belongsTo('Sounds', [
			'foreignKey' => 'sound_id',
		]);

		$this->belongsTo('Locations', [
			'foreignKey' => 'location_id',
		]);

		$this->belongsTo('PinComments', [
			'className' => 'VideoComments',
			'foreignKey' => 'pin_comment_id',
		]);

		$this->belongsTo('Products', [
			'foreignKey' => 'product_id',
		]);

		// HasMany Associations
		$this->hasMany('VideoComments', [
			'foreignKey' => 'video_id',
			'dependent' => true,
		]);

		$this->hasMany('VideoFavourites', [
			'foreignKey' => 'video_id',
			'dependent' => true,
		]);

		$this->hasMany('VideoProducts', [
			'foreignKey' => 'video_id',
			'dependent' => true,
		]);

		$this->hasMany('VideoLikes', [
			'foreignKey' => 'video_id',
			'dependent' => true,
		]);

		$this->hasMany('VideoWatches', [
			'foreignKey' => 'video_id',
			'dependent' => true,
		]);

		$this->hasMany('Notifications', [
			'foreignKey' => 'video_id',
			'dependent' => true,
		]);
	}
	
	public function getDetails($id): ?\Cake\Datasource\EntityInterface
	{
		return $this->find()
			->select($this->custom_fields)
			->contain(['VideoProduct'])
			->where(['Videos.id' => $id])
			->first();
	}

	public function getDetailsAdmin($id): ?\Cake\Datasource\EntityInterface
	{
		return $this->find()
			->contain(['VideoComment', 'Sound'])
			->where(['Videos.id' => $id])
			->first();
	}

	public function getObjectsForCache($id): ?\Cake\Datasource\EntityInterface
	{
		return $this->find()
			->contain(['Location', 'Sound'])
			->where(['Videos.id' => $id])
			->first();
	}

	public function getUserStory($user_id, $date): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->contain(['Sound'])
			->where([
				'Videos.user_id' => $user_id,
				'Videos.created >=' => $date,
				'Videos.story' => 1,
			])
			->all();
	}

	public function getUserVideosIDs($user_id): array
	{
		return $this->find()
			->select(['id'])
			->where([
				'Videos.user_id' => $user_id,
				'Videos.repost_user_id' => 0,
			])
			->enableHydration(false)
			->all()
			->extract('id')
			->toList();
	}

	public function getDetailsAgainstOldVideoID($old_video_id): ?\Cake\Datasource\EntityInterface
	{
		return $this->find()
			->where(['Videos.old_video_id' => $old_video_id])
			->first();
	}

	public function ifUserRepostedVideo($user_id, $video_id): ?\Cake\Datasource\EntityInterface
	{
		return $this->find()
			->where([
				'Videos.repost_user_id' => $user_id,
				'Videos.repost_video_id' => $video_id,
			])
			->first();
	}

	public function getOnlyVideoDetails($video_id): ?\Cake\Datasource\EntityInterface
	{
		return $this->find()
			->contain(['User'])
			->where(['Videos.id' => $video_id])
			->first();
	}

	public function getOnlyVideoDetailsAgainstVideoAndUserID($user_id, $video_id): ?\Cake\Datasource\EntityInterface
	{
		return $this->find()
			->contain(['User'])
			->where([
				'Videos.id' => $video_id,
				'Videos.user_id' => $user_id,
			])
			->first();
	}

	public function getVideoDetailsAgainstJobID($job_id): ?\Cake\Datasource\EntityInterface
	{
		return $this->find()
			->where(['Videos.job_id' => $job_id])
			->first();
	}

	public function getVideoDetailsLocation($location_id): ?\Cake\Datasource\EntityInterface
	{
		return $this->find()
			->where([
				'Videos.location_id' => $location_id,
				'Videos.tag_store_id' => 0,
			])
			->first();
	}

	public function getSearchResults($keyword, $starting_point, $user_id): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->select($this->custom_fields)
			->contain(['VideoProduct'])
			->where([
				'Videos.description LIKE' => $keyword . '%',
				'Videos.repost_user_id' => 0,
			])
			->limit(15)
			->offset($starting_point * 15)
			->all();
	}

	public function checkDuplicate(array $data): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->contain(['Location', 'Product', 'User.PrivacySetting', 'User.PushNotification', 'Sound'])
			->where([
				'Videos.description' => $data['description'],
				'Videos.user_id' => $data['user_id'],
				'Videos.video' => $data['video'],
				'Videos.thum' => $data['thum'],
				'Videos.gif' => $data['gif'],
				'Videos.repost_user_id' => 0,
			])
			->limit(10)
			->all();
	}

	public function getUserRecentVideos($user_id, $start_datetime, $end_datetime): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->contain(['Location', 'Product', 'User.PrivacySetting', 'User.Store', 'User.PushNotification', 'Sound'])
			->where([
				'Videos.user_id' => $user_id,
				'Videos.privacy_type' => 'public',
				'Videos.repost_user_id' => 0,
				'Videos.story' => 0,
				'Videos.created >=' => $start_datetime,
				'Videos.created <=' => $end_datetime,
			])
			->limit(7)
			->orderDesc('Videos.id')
			->all();
	}

	public function getUserTrendingVideos($user_id, $start_datetime, $end_datetime): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->contain(['Location', 'Product', 'User.PrivacySetting', 'User.Store', 'User.PushNotification', 'Sound'])
			->where([
				'Videos.user_id' => $user_id,
				'Videos.privacy_type' => 'public',
				'Videos.repost_user_id' => 0,
				'Videos.story' => 0,
				'Videos.created >=' => $start_datetime,
				'Videos.created <=' => $end_datetime,
			])
			->limit(9)
			->orderDesc('Videos.view')
			->all();
	}

	public function getUserPublicVideos($user_id, $starting_point): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->select($this->custom_fields)
			->contain(['VideoProduct'])
			->where([
				'Videos.user_id' => $user_id,
				'Videos.privacy_type' => 'public',
				'Videos.repost_user_id' => 0,
			])
			->limit(APP_RECORDS_PER_PAGE)
			->offset($starting_point * APP_RECORDS_PER_PAGE)
			->orderDesc('Videos.id')
			->all();
	}

	public function getUserProductTaggedVideos($user_id, $starting_point): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->select($this->custom_fields)
			->contain(['VideoProduct'])
			->where([
				'Videos.user_id' => $user_id,
				'Videos.privacy_type' => 'public',
				'Videos.repost_user_id' => 0,
				'Videos.tag_product' => 1,
				'Videos.story' => 0,
			])
			->limit(APP_RECORDS_PER_PAGE)
			->offset($starting_point * APP_RECORDS_PER_PAGE)
			->orderDesc('Videos.id')
			->all();
	}

	public function getUserPublicVideosAdmin($user_id): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->where([
				'Videos.user_id' => $user_id,
				'Videos.privacy_type IN' => ['public', 'Public'],
				'Videos.repost_user_id' => 0,
				'Videos.story' => 0,
			])
			->orderDesc('Videos.id')
			->all();
	}

	public function getUserPrivateVideosAdmin($user_id): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->where([
				'Videos.user_id' => $user_id,
				'Videos.privacy_type' => 'private',
				'Videos.repost_user_id' => 0,
			])
			->orderDesc('Videos.id')
			->all();
	}

	public function getUserVideos($user_id): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->contain(['Location', 'Product', 'User.PrivacySetting', 'User.Store', 'User.PushNotification', 'Sound'])
			->where([
				'Videos.user_id' => $user_id,
				'Videos.repost_user_id' => 0,
			])
			->orderDesc('Videos.id')
			->all();
	}

	public function getAllVideos($starting_point): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->contain(['User', 'Sound'])
			->orderDesc('Videos.id')
			->limit(ADMIN_RECORDS_PER_PAGE)
			->offset($starting_point * ADMIN_RECORDS_PER_PAGE)
			->all();
	}

	public function getAllNudityDetectedVideos($starting_point): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->contain(['User', 'Sound'])
			->where(['Videos.nudity_found' => 1])
			->orderDesc('Videos.id')
			->limit(ADMIN_RECORDS_PER_PAGE)
			->offset($starting_point * ADMIN_RECORDS_PER_PAGE)
			->all();
	}

	public function tempvideos(): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->where(function ($exp) {
				return $exp->or_([
					'ai_json IS' => null,
					'CHAR_LENGTH(ai_json) <=' => 4
				]);
			})
			->andWhere(['ai_done !=' => 2])
			->orderDesc('Videos.id')
			->limit(100)
			->all();
	}

	public function getAllUserVideos($user_id): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->where(['Videos.user_id' => $user_id])
			->orderDesc('Videos.id')
			->all();
	}

	public function getUserPrivateVideos($user_id, $starting_point): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->select($this->custom_fields)
			->contain(['VideoProduct'])
			->where([
				'Videos.user_id' => $user_id,
				'Videos.privacy_type' => 'private',
				'Videos.repost_user_id' => 0,
			])
			->limit(APP_RECORDS_PER_PAGE)
			->offset($starting_point * APP_RECORDS_PER_PAGE)
			->orderDesc('Videos.id')
			->all();
	}

	public function getUserVideosCount($user_id): int
	{
		return $this->find()
			->where([
				'Videos.user_id' => $user_id,
				'Videos.repost_user_id' => 0,
			])
			->count();
	}

	public function countUserVideosViews($user_id): int
	{
		$result = $this->find()
			->select(['total_views' => 'SUM(Videos.view)'])
			->where([
				'Videos.user_id' => $user_id,
				'Videos.repost_user_id' => 0,
			])
			->enableHydration(false)
			->first();
		return (int)$result['total_views'];
	}

	public function getVideosCountAgainstSound($sound_id): int
	{
		return $this->find()
			->where(['Videos.sound_id' => $sound_id])
			->count();
	}

	public function getFrequentlyUsedSounds(): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->select(['sound_id', 'count' => 'COUNT(*)'])
			->where([
				'Videos.privacy_type' => 'public',
				'Videos.repost_user_id' => 0,
			])
			->group('Videos.sound_id')
			->orderDesc('count')
			->all();
	}

	public function getVideosAgainstSoundID($user_id, $starting_id, $sound_id): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->select($this->custom_fields)
			->contain(['VideoProduct'])
			->where([
				'Videos.sound_id' => $sound_id,
				'Videos.repost_user_id' => 0,
				'Videos.privacy_type' => 'public',
			])
			->limit(APP_RECORDS_PER_PAGE)
			->offset($starting_id * APP_RECORDS_PER_PAGE)
			->orderDesc('Videos.view')
			->all();
	}

	public function getVideosAgainstLocation($location_id, $starting_point): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->select($this->custom_fields)
			->contain(['VideoProduct'])
			->where([
				'Videos.location_id' => $location_id,
				'Videos.repost_user_id' => 0,
				'Videos.privacy_type' => 'public',
			])
			->limit(APP_RECORDS_PER_PAGE)
			->offset($starting_point * APP_RECORDS_PER_PAGE)
			->orderDesc('Videos.view')
			->all();
	}

	public function getVideosAgainstSoundIDWeb($user_id, $device_id, $starting_id, $sound_id): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->contain(['Location', 'Product', 'User.PrivacySetting', 'User.Store', 'User.PushNotification', 'Sound'])
			->where([
				'Videos.sound_id' => $sound_id,
				'Videos.repost_user_id' => 0,
				'Videos.privacy_type' => 'public',
			])
			->orderDesc('Videos.view')
			->all();
	}

	public function getPromotedVideo(): ?\Cake\Datasource\EntityInterface
	{
		return $this->find()
			->contain(['Location', 'Product', 'User.PrivacySetting', 'User.PushNotification', 'Sound'])
			->where([
				'Videos.promote' => 1,
				'Videos.repost_user_id' => 0,
			])
			->order('rand()')
			->first();
	}

	public function getRelatedVideosNotWatched($user_id, $device_id, $tag_product, $starting_id): \Cake\Datasource\ResultSetInterface
	{
		$query = $this->find()
			->contain(['Location', 'Product', 'User.PrivacySetting', 'User.PushNotification', 'User.Store', 'Sound', 'PinComment', 'VideoProduct.Store.User', 'VideoProduct.Store.StoreAddress', 'VideoProduct.Store.StoreLocalHours'])
			->where([
				'Videos.block' => 0,
				'Videos.repost_user_id' => 0,
				'Videos.story' => 0,
				'Videos.privacy_type' => 'public',
			])
			->limit(15)
			->offset($starting_id * 15)
			->orderDesc('Videos.view');
		$query->leftJoinWith('NotInterestedVideo', function ($q) use ($user_id) {
			return $q->where(['NotInterestedVideo.user_id' => $user_id]);
		});
		$query->leftJoinWith('BlockUser', function ($q) use ($user_id) {
			return $q->where(['BlockUser.user_id' => $user_id]);
		});
		$query->leftJoinWith('VideoWatch', function ($q) use ($user_id) {
			return $q->where(['VideoWatch.user_id' => $user_id]);
		});
		return $query->all();
	}

	public function getAllVideosAgainstVideoIDS(array $video_ids): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->select($this->custom_fields)
			->where(['Videos.id IN' => $video_ids])
			->contain(['VideoProduct'])
			->all();
	}

	public function getNearbyVideos($lat, $long, $user_id, $starting_id): \Cake\Datasource\ResultSetInterface
	{
		$distance = '3959 * ACOS(
			COS(RADIANS(' . $lat . ')) * COS(RADIANS(Videos.lat)) * COS(RADIANS(Videos.long) - RADIANS(' . $long . ')) +
			SIN(RADIANS(' . $lat . ')) * SIN(RADIANS(Videos.lat))
		)';
		$query = $this->find()
			->select($this->custom_fields)
			->contain(['VideoProduct'])
			->where([
				'Videos.block' => 0,
				'Videos.repost_user_id' => 0,
				'Videos.story' => 0,
				'Videos.privacy_type' => 'public',
			])
			->having([$distance . ' <' => 1000])
			->order([$distance => 'ASC'])
			->limit(25)
			->offset($starting_id * 25);
		$query->leftJoinWith('BlockUser', function ($q) use ($user_id) {
			return $q->where(['BlockUser.user_id' => $user_id]);
		});
		return $query->all();
	}

	public function getNearbyRelatedVideosWatchedLimit($lat, $long, $user_id, $starting_id, $limit): \Cake\Datasource\ResultSetInterface
	{
		$distance = '3959 * ACOS(
			COS(RADIANS(' . $lat . ')) * COS(RADIANS(Videos.lat)) * COS(RADIANS(Videos.long) - RADIANS(' . $long . ')) +
			SIN(RADIANS(' . $lat . ')) * SIN(RADIANS(Videos.lat))
		)';
		$query = $this->find()
			->select($this->custom_fields)
			->contain(['VideoProduct'])
			->where([
				'Videos.block' => 0,
				'Videos.repost_user_id' => 0,
				'Videos.story' => 0,
				'Videos.privacy_type' => 'public',
			])
			->having([$distance . ' <' => 10000])
			->order([$distance => 'ASC'])
			->limit($limit)
			->offset($starting_id * 15);
		$query->leftJoinWith('NotInterestedVideo', function ($q) use ($user_id) {
			return $q->where(['NotInterestedVideo.user_id' => $user_id]);
		});
		$query->leftJoinWith('BlockUser', function ($q) use ($user_id) {
			return $q->where(['BlockUser.user_id' => $user_id]);
		});
		return $query->all();
	}

	public function getVideosWhichAreNotCompressedAndNotQualityChecked(): ?\Cake\Datasource\EntityInterface
	{
		return $this->find()
			->where([
				'Videos.privacy_type' => 'public',
				'Videos.compression' => 0,
				'Videos.quality_check' => 0,
				'Videos.error' => 0,
			])
			->orderDesc('Videos.id')
			->first();
	}

	public function getRelatedVideos($user_id, $starting_id): \Cake\Datasource\ResultSetInterface
	{
		$query = $this->find()
			->select($this->custom_fields)
			->contain(['VideoProduct'])
			->where([
				'Videos.block' => 0,
				'Videos.repost_user_id' => 0,
				'Videos.story' => 0,
				'Videos.privacy_type' => 'public',
			])
			->limit(25)
			->offset($starting_id * 25)
			->orderDesc('Videos.viral');
		$query->leftJoinWith('BlockUser', function ($q) use ($user_id) {
			return $q->where(['BlockUser.user_id' => $user_id]);
		});
		return $query->all();
	}

	public function getRelatedVideosDemo($user_id, $starting_id): \Cake\Datasource\ResultSetInterface
	{
		return $this->getRelatedVideos($user_id, $starting_id);
	}

	public function getRelatedVideosRandom($user_id, $starting_id): \Cake\Datasource\ResultSetInterface
	{
		$query = $this->find()
			->select($this->custom_fields)
			->contain(['VideoProduct'])
			->where([
				'Videos.block' => 0,
				'Videos.repost_user_id' => 0,
				'Videos.story' => 0,
				'Videos.privacy_type' => 'public',
			])
			->limit(25)
			->offset($starting_id * 25)
			->order('RAND()');
		$query->leftJoinWith('BlockUser', function ($q) use ($user_id) {
			return $q->where(['BlockUser.user_id' => $user_id]);
		});
		return $query->all();
	}

	public function getRelatedVideosWatchedLimit($user_id, $device_id, $tag_product, $starting_id, $limit): \Cake\Datasource\ResultSetInterface
	{
		$query = $this->find()
			->contain(['Location', 'Product', 'User.PrivacySetting', 'User.PushNotification', 'User.Store', 'Sound', 'VideoProduct.Store.User', 'VideoProduct.Store.StoreAddress', 'VideoProduct.Store.StoreLocalHours'])
			->where([
				'Videos.block' => 0,
				'Videos.repost_user_id' => 0,
				'Videos.story' => 0,
				'Videos.privacy_type' => 'public',
			])
			->limit($limit)
			->offset($starting_id * $limit)
			->orderDesc('Videos.view');
		$query->leftJoinWith('NotInterestedVideo', function ($q) use ($user_id) {
			return $q->where(['NotInterestedVideo.user_id' => $user_id]);
		});
		$query->leftJoinWith('BlockUser', function ($q) use ($user_id) {
			return $q->where(['BlockUser.user_id' => $user_id]);
		});
		return $query->all();
	}

	public function getFollowingVideos($user_id, $starting_id, $ids): \Cake\Datasource\ResultSetInterface
	{
		$query = $this->find()
			->select($this->custom_fields)
			->contain(['VideoProduct'])
			->where([
				'Videos.privacy_type' => 'public',
				'Videos.repost_user_id' => 0,
				'Videos.story' => 0,
				'Videos.block' => 0,
				'Videos.user_id IN' => $ids,
			])
			->limit(15)
			->offset($starting_id * 15)
			->orderDesc('Videos.id');
		$query->leftJoinWith('BlockUser', function ($q) use ($user_id) {
			return $q->where(['BlockUser.user_id' => $user_id]);
		});
		return $query->all();
	}

	public function getFollowingVideosWatched($user_id, $starting_id, $ids, $limit): \Cake\Datasource\ResultSetInterface
	{
		$query = $this->find()
			->contain(['Location', 'Product', 'User.PrivacySetting', 'User.PushNotification', 'Sound', 'User.Store', 'VideoProduct.Store.User', 'VideoProduct.Store.StoreAddress', 'VideoProduct.Store.StoreLocalHours'])
			->where([
				'EXISTS (SELECT 1 FROM follower WHERE follower.receiver_id = Videos.user_id AND follower.sender_id =' . $user_id . ')',
				'Videos.block' => 0,
				'Videos.story' => 0,
				'Videos.repost_user_id' => 0,
				'Videos.user_id IN' => $ids,
				'Videos.privacy_type' => 'public',
			])
			->limit($limit)
			->offset($starting_id * $limit)
			->orderDesc('Videos.id');
		$query->leftJoinWith('NotInterestedVideo', function ($q) use ($user_id) {
			return $q->where(['NotInterestedVideo.user_id' => $user_id]);
		});
		$query->leftJoinWith('BlockUser', function ($q) use ($user_id) {
			return $q->where(['BlockUser.user_id' => $user_id]);
		});
		return $query->all();
	}

	public function getAllVideosAgainstSoundID($sound_id): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->where([
				'Videos.sound_id' => $sound_id,
				'Videos.repost_user_id' => 0,
				'Videos.story' => 0,
			])
			->all();
	}

	public function getAllVideostemp(): \Cake\Datasource\ResultSetInterface
	{
		return $this->find()
			->where([
				'Videos.lat IS NOT' => null,
				'Videos.lat !=' => '',
				'Videos.location_id' => 0,
			])
			->all();
	}

	public function updateSoundIDs($ids): void
	{
		$this->updateAll(['sound_id' => 0], ['Videos.sound_id IN' => $ids]);
	}
}
?>
