<?php
/**
 * SOAP backend pro living.
 *
 * @category   Dalten
 * @package    Export
 * @subpackage Api
 */
class Dalten_Export_Api_Backend_Living implements Dalten_Export_Api_Backend_LoggableBackendInterface
{
	/**
	 * Url adresa exportniho servisu.
	 *
	 * @var string
	 */
	private $_serviceUrl = '';

	/**
	 * Uri exportniho servisu.
	 *
	 * @var string
	 */
	private $_serviceUri = '';

	/**
	 * Instance komunikace s klientem.
	 *
	 * @var null|SoapClient
	 */
	private $_client = null;

	/**
	 * Instance loggeru komunikace se serverem.
	 *
	 * @var Dalten_Export_Api_Backend_Logger_LoggerInterface
	 */
	private $_logger;

	private $_portals = array();

	/**
	 * Nastavi vlastnosti objektu.
	 *
	 * @param string $serviceUrl Url adresa exportniho servisu.
	 * @param string $serviceUri Uri exportniho servisu.
	 */
	public function __construct($serviceUrl, $serviceUri)
	{
		$this->_serviceUrl = (string) $serviceUrl;
		$this->_serviceUri = (string) $serviceUri;
	}

	/**
	 * Nastavi SOAP clienta a vrati instanci vlastni tridy.
	 *
	 * @param bool $debug Zapnutim debugu je mozne zistakat komunikaci vedenou pres SOAP.
	 *
	 * @return Dalten_Export_Api_Backend_Living
	 *
	 * @throws SoapFault V pripade, ze pri nastavovani klienta nastane chyba.
	 */
	private function _connect($debug = true)
	{
		$options = array(
			'location' => $this->_serviceUrl,
			'uri' => $this->_serviceUri,
			'trace' => (integer) $debug
		);

		$this->_client = new SoapClient(null, $options);

		return $this;
	}

	/**
	 * Prihlasi uzivatele k exportnimu servisu.
	 *
	 * @param string $username Prihlasovaci jmeno uzivatele.
	 * @param string $password Heslo uzivatele.
	 *
	 * @return Dalten_Export_Api_ServerResponse
	 */
	public function login($username, $password)
	{
		$args = array(
			$username,
			$password
		);
		try {
			$this->_connect();
			$result = $this->_callClient('loginPartner', $args);
		} catch (SoapFault $e) {
			return new Dalten_Export_Api_ServerResponse(false, 500, $e->getMessage());
		} catch (Dalten_Export_Api_Backend_Exception_ResponseError $e) {
			return new Dalten_Export_Api_ServerResponse(false, 500, $e->getMessage());
		}

		return new Dalten_Export_Api_ServerResponse(true, 200, $result->msg);
	}

	/**
	 * Prida nebo preexportuje nabidku.
	 *
	 * Pridavna data mohou obsahovat nasledujici hodnoty.
	 * array(
	 *     'estate-id' => 42,
	 *     'ad-id' => 42,
	 *     'user-id' => 42,
	 * )
	 *
	 * @param array $listingData      Data nabidky ktera chceme vyexpotvoat.
	 * @param array $userData         Data uzivatele.
	 * @param array $images           Data obrazku.
	 * @param array $additionalParams Pridavna data, ktera jsou nutna pro editaci uzivatelu a inzeratu.
	 *
	 * @return Dalten_Export_Api_ServerResponse
	 */
	public function addListing(array $listingData, array $userData, array $images, array $additionalParams = array())
	{
		$responseData = array();
		$additionalParamsResult = isset($additionalParams['result']) ? $additionalParams['result'] : array();

		if (!isset($additionalParams['listing']['portals'])) {
			return new Dalten_Export_Api_ServerResponse(
				false, 500, 'Neni zadan seznam portalu na ktere se ma nabidka exportovat.'
			);
		}

		$response = $this->getPortals();
		$portals = $response->getData();
		if (empty($portals)) {
			return $response;
		}

		try {
			$branchId = isset($additionalParamsResult['branch_id']) ? $additionalParamsResult['branch_id'] : 0;
			$listingData['branch_id'] = $branchId;
			$advertiserId = $listingData['advertiser_id'];

			$userId = null;
			if (isset($userData['livingsk_id'])) {
				$userId = (integer) $userData['livingsk_id'];
			}
			$userId = (integer) $this->_processUser($userId, $userData, $advertiserId, $branchId);

			$listingData['user_id'] = $userId;
			// Nastavime si user-id do resopnse.
			$responseData['user-id'] = $userId;

			// Vytvorime nebo updatneme nabidku.
			if (isset($additionalParamsResult['ad-id'], $additionalParamsResult['estate-id'])) {
				$adId = $additionalParamsResult['ad-id'];
				$estateId = $additionalParamsResult['estate-id'];

				// V pripade editace se tyto parametr musi odstranit.
				unset($listingData['advertiser_id']);
				unset($listingData['price_type_id']);
				unset($listingData['deposit_type_id']);

				$result = $this->_callClient(
					'editAdvert', array(
						'ad-id' => $adId,
						'estate-id' => $estateId,
						'data' => $listingData
					)
				);
			} else {
				$result = $this->_callClient('addAdvert', array('data' => $listingData));
				$adId = $result->value['new-ad']['ad-id'];
				$estateId = $result->value['new-ad']['estate-id'];

				// Smazeme predchozi obrazky.
				$this->_deletePreviousImages($estateId);
			}
			// Ziskane informace si ulozime.
			$responseData['ad-id'] = $adId;
			$responseData['estate-id'] = $estateId;
			$responseData['portals'] = $portals;

			// Jeste pridame fotografie.
			$this->_addImages($estateId, $userId, $images);

			$this->activateAdOnPortals($adId, $additionalParams['listing']['portals'], $portals);
		} catch (Dalten_Export_Api_Backend_Exception_ResponseError $e) {
			$serverResponse = new Dalten_Export_Api_ServerResponse(false, $e->getCode(), $e->getMessage());
			$serverResponse->setData($responseData);
			return $serverResponse;
		}

		$serverResponse = new Dalten_Export_Api_ServerResponse(true, 200, $result->msg);
		$serverResponse->setData($responseData);

		return $serverResponse;
	}

	/**
	 * Deaktivuje inzerat na serveru.
	 *
	 * @param array $additionalParams Pole ve kterem je klic 'ad-id'.
	 *
	 * @return Dalten_Export_Api_ServerResponse
	 */
	public function deleteListing(array $additionalParams = array())
	{
		if (!isset($additionalParams['ad-id'])) {
			return new Dalten_Export_Api_ServerResponse(false, 500, 'Neni predano id inzeratu, ktere se ma smazat.');
		}
		try {
			$result = $this->_callClient('deactivateAd', array('id' => $additionalParams['ad-id']));
		} catch (Dalten_Export_Api_Backend_Exception_ResponseError $e) {
			return new Dalten_Export_Api_ServerResponse(false, $e->getCode(), $e->getMessage());
		}
		return new Dalten_Export_Api_ServerResponse(true, 200, $result->msg);
	}

	/**
	 * Aktivuje inzerat na portalech predanych v parametru.
	 *
	 * @param integer $adId        Id inzeratu ktery chceme aktivovat.
	 * @param array   $portalCodes Pole kodu portalu, na ktere chceme exportovat.
	 * @param array   $portalList  Seznam portalu na ktere muze firma exportovat.
	 */
	public function activateAdOnPortals($adId, array $portalCodes, $portalList)
	{
		foreach ($portalCodes as $code) {
			if (isset($portalList[$code])) {
				$this->_callClient('activateAd', array('adId' => $adId, 'portal_code' => $code));
			}
		}
	}

	/**
	 * Při úspěšném dotazu vrtátí v response seznam serverů na které může uživatel exportovat.
	 *
	 * @return Dalten_Export_Api_ServerResponse
	 */
	public function getPortals()
	{
		try {
			$result = $this->_callClient('listPortals', array());
		} catch (Dalten_Export_Api_Backend_Exception_ResponseError $e) {
			return new Dalten_Export_Api_ServerResponse(false, $e->getCode(), $e->getMessage());
		}
		if (!isset($result->value['portals'])) {
			return new Dalten_Export_Api_ServerResponse(false, 500, 'Server vratil necekanou odpoved.');
		}
		$response = new Dalten_Export_Api_ServerResponse(true, 200, $result->msg);

		// Nastavime klice podle kodu jednotlivych portalu.
		$portals = array();
		foreach ($result->value['portals'] as $portal) {
			$portals['code'] = $portal;
		}

		$response->setData($portals);
		return $response;
	}

	/**
	 * Prida nebo upravi uzivatele.
	 * Logika pridani/editace zavisi na tom, jestli je v promene userData klic 'id'.
	 *
	 * @param integer|null $userId       Id uzivatele nebo null.
	 * @param array        $userData     Uzivatelska data.
	 * @param integer      $advertiserId Id firmy uzivatele. Id je "livingovske".
	 * @param integer      $branchId     Id pobocky. Id je "livingovske".
	 *
	 * @uses _addNewUser()
	 * @uses _editUser()
	 * @uses _getUserId()
	 *
	 * @return integer Id uzivatele.
	 */
	private function _processUser($userId, array $userData, $advertiserId, $branchId = 0)
	{
		if ($userId) {
			$userData['id'] = (integer) $userId;
			$this->_editUser($userData, $advertiserId, $branchId);
		} else {
			$response = $this->_addNewUser($userData, $advertiserId, $branchId);
			$userId = $response->value['new-user']['id'];
		}

		return (integer) $userId;
	}


	/**
	 * Prida uzivatele k firme.
	 *
	 * <code>
	 * $userData = array(
	 *     'id' => 42,
	 *     'id_firma' => 666,
	 *     'uzivatel_os_jmeno' => 'Dalten',
	 *     'uzivatel_os_prijmeni' => 'Dalten',
	 *     'email' => 'dalten@dalten.cz',
	 *     'password' => 'netlad',
	 *     'telefon' => '+420666666666',
	 * )
	 * </code>
	 *
	 * @param array   $userData     Pole s udaji o makleri. Vice viz dokumentace o par radku nahoru.
	 * @param integer $advertiserId Cislo firmy. Pozor cislo firmy ktere pochazi z livingu a ne nase!!!!
	 * @param integer $branchId     Cislo pobocky. Pozor cislo pobocky ktere pochazi z livingu a ne nase !!!
	 *
	 * @uses __callClient()
	 *
	 * @return mixed Response clienta.
	 */
	private function _addNewUser(array $userData, $advertiserId, $branchId = 0)
	{
		$data = array(
			'advertiserId' => (integer) $advertiserId,
			'branchId' => (integer) $branchId,
			'firstname' => (string) $userData['uzivatel_os_jmeno'],
			'surname' => (string) $userData['uzivatel_os_prijmeni'],
			'login' => $this->_generateUsername($userData),
			'password' => (string) $userData['password'],
			'email' => (string) $userData['email'],
			'phone1' => (string) $userData['telefon'],
			'phone2' => (string) ''
		);
		return $this->_callClient('addUser', $data);
	}

	/**
	 * Edituje uzivatele firmy.
	 *
	 * <code>
	 * $userData = array(
	 *     'id' => '2121'
	 *     'uzivatel_os_jmeno' => 'Dalten',
	 *     'uzivatel_os_prijmeni' => 'Dalten',
	 *     'email' => 'dalten@dalten.cz',
	 *     'password' => 'netlad',
	 *     'telefon' => '+420666666666',
	 * )
	 * </code>
	 *
	 * @param array   $userData     Pole s udaji o makleri. Vice viz dokumentace o par radku nahoru.
	 * @param integer $advertiserId Cislo firmy. Pozor cislo firmy ktere pochazi z livingu a ne nase!!!!
	 * @param integer $branchId     Cislo pobocky. Pozor cislo pobocky ktere pochazi z livingu a ne nase !!!
	 *
	 * @uses __callClient()
	 *
	 * @return mixed Response clienta.
	 */
	private function _editUser(array $userData, $advertiserId, $branchId = 0)
	{
		$data = array(
			'userId' => (integer) $userData['id'],
			'advertiserId' => (integer) $advertiserId,
			'branchId' => (integer) $branchId,
			'firstname' => (string) $userData['uzivatel_os_jmeno'],
			'surname' => (string) $userData['uzivatel_os_prijmeni'],
			'login' => $this->_generateUsername($userData),
			'password' => (string) $userData['password'],
			'email' => (string) $userData['email'],
			'phone1' => (string) $userData['telefon'],
			'phone2' => (string) '',
		);
		return $this->_callClient('editUser', $data);
	}

	/**
	 * Vygeneruje uzivatelske jmeno. Pri kazdem zavolani je uzivatelske jmeno unikatni.
	 *
	 * <code>
	 * $userData = array(
	 *     'id' => 42,
	 *     'id_firma' => 666
	 * );
	 * </code>
	 *
	 * @param array $userData Data s klici 'id_firma' a 'id'
	 *
	 * @return string
	 */
	private function _generateUsername(array $userData)
	{
		return (string) uniqid($userData['id_firma'] . '-' . $userData['id'] . '-');
	}

	/**
	 * Ziska id uzivatele dotazem do rozhrani livingu. Id uzivatele paruje na zaklade loginu.
	 *
	 * @param string $advertiserId Id firmy pro kterou chceme ziskat uzivatele. Id firmy je to ktere ma u sebe ulozene
	 *                             Living.
	 * @param string $login        Login uzivatele pro ktereho chceme zjistit jeho ID.
	 *
	 * @return int|null Vrati id nebo null pokud se nepovede uzivatele sparova na zaklade predanych udaju.
	 *
	 * @throws Dalten_Export_Api_Backend_Exception_ResponseError Pokud server nevrati ocekavany tvar dat.
	 */
	protected  function _getUserId($advertiserId, $login)
	{
		$result = $this->_callClient('listUsers', array($advertiserId));
		if (!isset($result->value['users'])) {
			throw new Dalten_Export_Api_Backend_Exception_ResponseError('Nepovedlo se ziskat seznam uzivatelu.');
		}

		foreach ($result->value['users'] as $user) {
			if (isset($user['login'], $user['id']) && (string) $user['login'] === $login) {
				return (integer) $user['id'];
			}
		}

		return null;
	}

	/**
	 * Prida obrazky k inzeratu.
	 *
	 * @param integer $estateId Id nabidky pod kterym je ulozeno v living.sk.
	 * @param integer $userId   Id uzivatele ktere ma vazbu do systemu lining.sk.
	 * @param array   $images   Pole obrazku ktere chceme vyexportovat.
	 */
	private function _addImages($estateId, $userId, array $images)
	{
		$isMain = true;
		foreach (array_values($images) as $image) {
			$file = realpath($image['soubor']);
			if ($file) {
				$results[] = $this->_callClient(
					'importImage', array(
						'estateId' => (integer) $estateId,
						'userId' => (integer) $userId,
						'imageData' => base64_encode(file_get_contents($file)),
						'name' => '',
						'description' => $image['popis'],
						'main' => $isMain
					)
				);
				$isMain = false;
			}
		}
	}

	/**
	 * Smaze vsechny obrazky z nabidky.
	 *
	 * @param int $estateId Id nabidky u ktere chceme obrazky smazat.
	 */
	private function _deletePreviousImages($estateId)
	{
		$result = $this->_callClient('listImages', array($estateId));
		if (empty($result->value['images'])) {
			return;
		}
		foreach ($result->value['images'] as $image) {
			$this->_callClient('delImage', array($image['id']));
		}
	}


	/**
	 * Zavola nad klientem konkretni metodu s argumenty.
	 *
	 * Pri volani odchytava vyjiky vyhozene SOAP clientem a dale validuje ocekavanou odpoved.
	 * V pripade, ze SOAP vyhodi vyjimku nebo odpoved ze serveru je chybna ci ve spatnem formatu, vyhodi
	 * metoda vyjimku. Jinak vraci response z klienta.
	 *
	 * @param string $method Název volané metody.
	 * @param array  $args   Argumenty metody.
	 *
	 * @return mixed Odpověď serveru.
	 *
	 * @throws Dalten_Export_Api_Backend_Exception_ResponseError Pri chybne odpovedi.
	 */
	protected function _callClient($method, array $args)
	{
		if ($this->_logger instanceof Dalten_Export_Api_Backend_Logger_LoggerInterface) {
			$this->_logger->logRemoteCall($method, $args);
		}
		try {
			$result = $this->_client->__call($method, $args);
		} catch (SoapFault $e) {
			throw new Dalten_Export_Api_Backend_Exception_ResponseError($e->getMessage());
		}

		if ($this->_logger instanceof Dalten_Export_Api_Backend_Logger_LoggerInterface) {
			$this->_logger->logRemoteResponse($method, $result);
		}

		if (!property_exists($result, 'error') && !property_exists($result, 'msg')) {
			throw new Dalten_Export_Api_Backend_Exception_ResponseError('Server poslal neznamou odpoved.');
		}

		if ((integer) $result->error > 1) {
			$message = $result->msg;
			if (empty($message)) {
				// Trochu prasarnicka, ale je to bezpecne. Neda se nic delat. Server dokaze vracet prazdne zpravy.
				$message = print_r($result->value, true);
			}

			throw new Dalten_Export_Api_Backend_Exception_ResponseError($message, $result->error);
		}
		return $result;
	}

	/**
	 * Nastaví logger pro backend.
	 *
	 * Logger bude použit pouze pokud to backend dovoluje.
	 *
	 * @param Dalten_Export_Api_Backend_Logger_LoggerInterface $logger Instance loggeru.
	 *
	 * @return Dalten_Export_Api_LoggableApiInterface Fluent interface.
	 */
	public function setLogger(Dalten_Export_Api_Backend_Logger_LoggerInterface $logger)
	{
		$this->_logger = $logger;

		return $this;
	}

	/**
	 * Odstraní nastavený logger.
	 *
	 * @return Dalten_Export_Api_LoggableApiInterface Fluent interface.
	 */
	public function removeLogger()
	{
		$this->_logger = null;

		return $this;
	}
}
