<?php

namespace XF\Entity;

use XF\CustomField\Set;
use XF\Mvc\Entity\AbstractCollection;
use XF\Mvc\Entity\Entity;
use XF\Mvc\Entity\Structure;
use XF\Repository\UserFieldRepository;
use XF\Util\Str;

use function in_array, intval;

/**
 * COLUMNS
 * @property int $user_id
 * @property int $dob_day
 * @property int $dob_month
 * @property int $dob_year
 * @property string $signature
 * @property string $website
 * @property string $website_
 * @property string $location
 * @property string $location_
 * @property array $following
 * @property array|null $ignored
 * @property int $avatar_crop_x
 * @property int $avatar_crop_y
 * @property int $banner_date
 * @property int|null $banner_position_y
 * @property bool $banner_optimized
 * @property string $about
 * @property array|null $custom_fields_
 * @property array|null $connected_accounts
 * @property int $password_date
 *
 * GETTERS
 * @property-read bool|int $age
 * @property-read array $birthday
 * @property Set $custom_fields
 *
 * RELATIONS
 * @property-read User|null $User
 * @property-read AbstractCollection<UserFieldValue> $CustomFields
 */
class UserProfile extends Entity
{
	public function isFollowing($user)
	{
		if ($user instanceof User)
		{
			$userId = $user->user_id;
		}
		else
		{
			$userId = $user;
		}

		return in_array($userId, $this->following);
	}

	protected function verifyLocation($location)
	{
		if ($this->getOption('admin_edit'))
		{
			return true;
		}

		if ($this->isUpdate() && $location == $this->getExistingValue('location'))
		{
			return true;
		}

		if ($this->getOption('location_required') && $location === '')
		{
			$this->error(\XF::phrase('please_enter_valid_location'), 'location');
			return false;
		}

		return true;
	}

	protected function verifyLongStringField($value, $key)
	{
		$maxLength = $this->getOption('max_long_string_length');
		if ($maxLength && Str::strlen($value) > $maxLength)
		{
			$this->error(\XF::phrase('please_enter_message_with_no_more_than_x_characters', ['count' => $maxLength]), $key);
			return false;
		}

		return true;
	}

	public function setDob($day, $month, $year = 0)
	{
		$day = intval($day);
		$month = intval($month);
		$year = intval($year);

		if (!$day || !$month)
		{
			$this->dob_day = 0;
			$this->dob_month = 0;
			$this->dob_year = 0;
			return true;
		}

		if ($year && $year < 100)
		{
			$year += $year < 30 ? 2000 : 1900;
		}

		$testYear = $year ?: 2008; // leap year

		if ($testYear < 1900
			|| !checkdate($month, $day, $testYear)
			|| gmmktime(0, 0, 0, $month, $day, $testYear) > \XF::$time + 86400 // +1 day to be careful with TZs ahead of GMT
		)
		{
			$this->dob_day = 0;
			$this->dob_month = 0;
			$this->dob_year = 0;

			$this->error(\XF::phrase('please_enter_valid_date_of_birth'), 'dob');

			return false;
		}

		$this->dob_day = $day;
		$this->dob_month = $month;
		$this->dob_year = $year;
		return true;
	}

	public function calculateAge($year, $month, $day)
	{
		[$cYear, $cMonth, $cDay] = explode('-', $this->app()->language()->date(\XF::$time, 'Y-m-d'));
		$age = $cYear - $year;
		if ($cMonth < $month || ($cMonth == $month && $cDay < $day))
		{
			$age--;
		}

		return max(0, $age);
	}

	/**
	 * @param bool $bypassPrivacy
	 * @return bool|int
	 */
	public function getAge($bypassPrivacy = false)
	{
		if (empty($this->dob_year) || empty($this->dob_month) || empty($this->dob_day))
		{
			return false;
		}

		if ($this->dob_year && ($bypassPrivacy || ($this->User->Option->show_dob_date && $this->User->Option->show_dob_year)))
		{
			return $this->calculateAge($this->dob_year, $this->dob_month, $this->dob_day);
		}
		else
		{
			return false;
		}
	}

	/**
	 * @param bool $bypassPrivacy
	 *
	 * @return array
	 */
	public function getBirthday($bypassPrivacy = false)
	{
		if ($this->dob_day && ($bypassPrivacy || $this->User->Option->show_dob_date))
		{
			if ($this->dob_year && ($bypassPrivacy || $this->User->Option->show_dob_year))
			{
				return [
					'age' => $this->getAge($bypassPrivacy),
					'timeStamp' => new \DateTime("$this->dob_year-$this->dob_month-$this->dob_day"),
					'format' => 'absolute',
				];
			}
			else
			{
				return [
					'age' => false,
					'timeStamp' => new \DateTime("2000-$this->dob_month-$this->dob_day"),
					'format' => 'monthDay',
				];
			}
		}
		else
		{
			return [];
		}
	}

	public function getAbstractedBannerPath($size)
	{
		$userId = $this->user_id;

		return sprintf(
			'data://profile_banners/%s/%d/%d.jpg',
			$size,
			floor($userId / 1000),
			$userId
		);
	}

	public function getBannerUrl($sizeCode, $canonical = false)
	{
		$app = $this->app();

		$sizeMap = $app->container('profileBannerSizeMap');
		if (!isset($sizeMap[$sizeCode]))
		{
			// Always fallback to 'l' by default in the event of an unknown size (most common)
			$sizeCode = 'l';
		}

		if ($this->banner_date)
		{
			$group = floor($this->user_id / 1000);
			return $app->applyExternalDataUrl(
				"profile_banners/{$sizeCode}/{$group}/{$this->user_id}.jpg?{$this->banner_date}",
				$canonical
			);
		}
		else
		{
			return null;
		}
	}

	/**
	 * @return Set
	 */
	public function getCustomFields()
	{
		$class = Set::class;
		$class = $this->app()->extendClass($class);

		$fieldDefinitions = $this->app()->container('customFields.users');

		return new $class($fieldDefinitions, $this);
	}

	public function getNewProfilePost()
	{
		$profilePost = $this->_em->create(ProfilePost::class);
		$profilePost->profile_user_id = $this->user_id;

		return $profilePost;
	}

	public function rebuildUserFieldValuesCache()
	{
		$this->repository(UserFieldRepository::class)->rebuildUserFieldValuesCache($this->user_id);
	}

	protected function _postSave()
	{
		$user = $this->User;

		if ($this->isUpdate() && $this->isChanged('password_date') && $user->security_lock)
		{
			$user->whenSaveable(function (User $user)
			{
				$user->security_lock = '';
				$user->save();
			});
		}
	}

	public static function getStructure(Structure $structure)
	{
		$structure->table = 'xf_user_profile';
		$structure->shortName = 'XF:UserProfile';
		$structure->primaryKey = 'user_id';
		$structure->columns = [
			'user_id' => ['type' => self::UINT, 'required' => true, 'changeLog' => false],
			'dob_day' => ['type' => self::UINT, 'max' => 31, 'default' => 0],
			'dob_month' => ['type' => self::UINT, 'max' => 12, 'default' => 0],
			'dob_year' => ['type' => self::UINT, 'max' => 2100, 'default' => 0],
			'signature' => ['type' => self::STR, 'maxLength' => 20000, 'default' => '',
				'verify' => 'verifyLongStringField',
			],
			'website' => ['type' => self::STR, 'default' => '',
				'censor' => true,
				'match' => self::MATCH_URL_EMPTY,
			],
			'location' => ['type' => self::STR, 'maxLength' => 50, 'default' => '',
				'censor' => true,
			],
			'following' => ['type' => self::LIST_COMMA, 'default' => [],
				'list' => ['type' => 'posint', 'unique' => true, 'sort' => SORT_NUMERIC],
				'changeLog' => false,
			],
			'ignored' => ['type' => self::JSON_ARRAY, 'default' => [], 'changeLog' => false],
			'avatar_crop_x' => ['type' => self::UINT, 'default' => 0, 'changeLog' => false],
			'avatar_crop_y' => ['type' => self::UINT, 'default' => 0, 'changeLog' => false],
			'banner_date' => ['type' => self::UINT, 'default' => 0],
			'banner_position_y' => ['type' => self::UINT, 'max' => 100, 'default' => null, 'nullable' => true, 'changeLog' => false],
			'banner_optimized' => ['type' => self::BOOL, 'default' => false, 'changeLog' => false],
			'about' => ['type' => self::STR, 'maxLength' => 20000, 'default' => '',
				'verify' => 'verifyLongStringField',
			],
			'custom_fields' => ['type' => self::JSON_ARRAY, 'default' => [], 'changeLog' => 'customFields'],
			'connected_accounts' => ['type' => self::JSON_ARRAY, 'default' => [], 'changeLog' => false],
			'password_date' => ['type' => self::UINT, 'default' => 1, 'changeLog' => false],
		];
		$structure->behaviors = [
			'XF:ChangeLoggable' => ['contentType' => 'user'],
			'XF:CustomFieldsHolder' => ['valueTable' => 'xf_user_field_value'],
		];
		$structure->getters = [
			'age' => true,
			'birthday' => true,
			'custom_fields' => true,
		];
		$structure->relations = [
			'User' => [
				'entity' => 'XF:User',
				'type' => self::TO_ONE,
				'conditions' => 'user_id',
				'primary' => true,
			],
			'CustomFields' => [
				'entity' => 'XF:UserFieldValue',
				'type' => self::TO_MANY,
				'conditions' => 'user_id',
				'key' => 'field_id',
			],
		];

		$options = \XF::options();

		$structure->options = [
			'max_long_string_length' => !empty($options->messageMaxLength) ? $options->messageMaxLength : 10000,
			'location_required' => !empty($options->registrationSetup['requireLocation']),
			'admin_edit' => false,
		];

		return $structure;
	}
}
