<?php
/**
 * Backend pro export na Annonci. Stará se o komunikaci se servery Annonce.
 *
 * @category   Dalten
 * @package    Export
 * @subpackage Api
 */
class Dalten_Export_Api_Backend_Annonce implements Dalten_Export_Api_Backend_LoggableBackendInterface
{
	/** @var Dalten_Http_ClientInterface|null */
	protected $_http = null;
	protected $_importURL = '';
	protected $_urlLookupURL = '';
	protected $_username = '';
	protected $_password = '';
	/** @var Dalten_Export_Api_Backend_Logger_LoggerInterface|null */
	protected $_logger = null;
	protected $_lastImportedBatch = '';

	/**
	 * Konstruktor. Nastavuje závislosti backendu.
	 *
	 * @param Dalten_Http_ClientInterface $http         Instance HTTP klienta.
	 * @param string                      $importURL    Importní URL. Výchozí je ta správná pro Annonci.
	 * @param string                      $urlLookupURL URL pro zjišťování URL nabídek.
	 *                                    Slovo HASHKOD v URL bude nahrazeno hashí současné dávky.
	 *                                    Výchozí je ta správná pro Annonci.
	 */
	function __construct(
		Dalten_Http_ClientInterface $http,
		$importURL = 'http://dealergw.annonce.cz/iec/Upload-upload',
		$urlLookupURL = 'http://dealergw.annonce.cz/iec/ImportDetail?hash=HASHKOD&xml=true&ads=true'
	)
	{
		$this->_http = $http;
		$this->_importURL = $importURL;
		$this->_urlLookupURL = $urlLookupURL;
	}

	/**
	 * Sparsuje odpověď od Annonce - XML bez kořenového tagu a se špatným escapováním znaků.
	 *
	 * @param string $data Vstupní XML jako text.
	 *
	 * @return Dalten_Xml_SimpleXml|bool Sparsované SimpleXML a nebo false pokud se nepodařilo sparsovat.
	 */
	protected function _parseWeirdXml($data)
	{
		$matches = array();
		// pokud máme prolog, oddělím ho od zbytku XML
		$withProlog = preg_match('~^(<\?xml.*\?>)(.+)$~s', $data, $matches);
		if ($withProlog) {
			$data = $matches[1] . '<dummy>' . $matches[2] . '</dummy>';
		} else {
			$data = '<dummy>' . $data . '</dummy>';
		}
		$data = str_replace('&', '&amp;', $data);
		$xml = simplexml_load_string($data, 'Dalten_Xml_SimpleXml');
		return $xml;
	}

	/**
	 * Nahraje na servery annonce ZIP soubor s XML příkazem a obslouží reakce jejich serveru.
	 *
	 * @param string $zipFileName Cesta k ZIP souboru.
	 * @param string $idToCheck   Kód nabídky k oveření stavu.
	 *
	 * @return bool Povedlo se příkaz s nabídkou provést?
	 *
	 * @throws Dalten_Export_Exception_HumanReadable Pokud nastala na jejich straně nějaká chyba s vysvětlením.
	 */
	protected function _uploadXMLPackage($zipFileName, $idToCheck)
	{
		$response = $this->_http->post(
			$this->_importURL,
			array(
				'action'=>'upload_nems',
				'upload'=>1,
				'up_user'=>$this->_username,
				'up_passwd'=>$this->_password,
				'delete_way'=>0
			),
			array(
				'xfile'=>$zipFileName
			)
		);
		unlink($zipFileName); // uklízíme dočasný soubor

		$responseXML = $this->_parseWeirdXml($response);
		if (!isset($responseXML->batch_status)) {
			return false;
		}
		$this->_lastImportedBatch = $responseXML->batch_id;

		return $this->_readStatusPage($responseXML->batch_status, $idToCheck);
	}

	/**
	 * Ověří zda se povedlo naimportovat nemovitost.
	 *
	 * @param array  $data          Údaje nemovitosti.
	 * @param string $statusPageURL URL ověřovacího XML nemovitosti, zjišťuje frontend.
	 *
	 * @return StdClass Struktura popisující stav importu.
	 */
	public function verifyListing(array $data, $statusPageURL)
	{
		$idToCheck = $data['kod'];
		return $this->_readStatusPage($statusPageURL, $idToCheck);
	}

	/**
	 * Přečte stránku se stavem importu a vrátí strukturu popisující stav importu.
	 *
	 * @param string $statusPageURL URL XL souboru popisujícího aktuální stav importu.
	 * @param string $idToCheck     Idčko (kód) nabídky, kterou kontrolujeme.
	 *
	 * @return StdClass Struktura popisující stav importu.
	 *
	 * @throws Dalten_Export_Exception_HumanReadable V případě nastalé chyby.
	 */
	protected function _readStatusPage($statusPageURL, $idToCheck)
	{
		$response = new StdClass;
		$response->waiting = false;
		$response->success = false;
		$statusPage = $this->_http->get($statusPageURL);

		$statusPageXML = $this->_parseWeirdXml($statusPage);

		if ($statusPageXML===false || !isset($statusPageXML->status)) {
			throw new Dalten_Export_Exception_HumanReadable("Není možné zjistit stav importu.");
		}

		if ($statusPageXML->status=='WAITING') {
			$response->waiting = true;
			$response->data = array(
				'statusPageURL'=>(string) $statusPageURL
			);
			return $response;
		}

		if ($statusPageXML->status=='OK') {
			$response->waiting = false;
			$response->success = true;

			$url = $this->_getAdvertUrl($statusPageURL, $idToCheck);
			if ($url) {
				$response->data = array(
					'remoteUrl'=>(string) $url, // původní položka na URL (pro zpětnou kompatibilitu)
					'detailUrl'=>(string) $url // nová položka na URL
				);
			}

			return $response;
		}

		foreach ($statusPageXML->xpath('//offer') as $offer) {
			if ($offer['id']==$idToCheck) {
				if ($offer['status']==0) {
					$response->success = true;
					return $response;
				} elseif ($offer['status']==1) {
					// vracíme chybu vrácenou Annoncí
					throw new Dalten_Export_Exception_HumanReadable( (string) $offer);
				}
			}
		}

		// něco se zřejmě rozbilo
		return $response;
	}

	/**
	 * Pokusí se zjistit URL naimportované nabídky.
	 *
	 * @param string $statusPageURL URL XML souboru na kontrolování stavu.
	 * @param string $idToCheck     Idčko (kód) nabídky, jejíž URL hledáme.
	 *
	 * @return string|bool Nalezená URL nebo false, pokud se nepovedlo nic najít.
	 */
	protected function _getAdvertUrl($statusPageURL, $idToCheck)
	{
		$urlFetchURL  = $this->_urlLookupURL;
		$query = parse_url($statusPageURL, PHP_URL_QUERY);
		$params = array();
		parse_str($query, $params);
		if (isset($params['hash'])) {
			$urlFetchURL = str_replace('HASHKOD', $params['hash'], $urlFetchURL);
			$urlPage = $this->_http->get($urlFetchURL);
			$urlXML = $this->_parseWeirdXml($urlPage);
			if (!$urlXML) {
			    return false;
            }
			foreach ($urlXML->xpath('//ad') as $ad) {
				$sourceParts = explode(';', (string) $ad->source_id);
				if (isset($sourceParts[1]) && $sourceParts[1]==$idToCheck) {
					return $ad->url;
				}
			}
		}
		return false;
	}

	/**
	 * Nastaví backendu přihlašovací údaje.
	 *
	 * @param string $username Jméno.
	 * @param string $password Heslo.
	 */
	public function setLogin($username, $password)
	{
		$this->_username = $username;
		$this->_password = $password;
	}

	/**
	 * Přidá nabídku na server.
	 *
	 * @param array $data   Převedená data nabídky (+ makléře).
	 * @param array $images Pole s informacemi o obrázcích.
	 *
	 * @return bool Zda se povedlo nabídku vyexportovat.
	 */
	public function addListing(array $data, array $images)
	{
		$zipFileName = tempnam(sys_get_temp_dir(),'annonce');
		$zip = new ZipArchive();
		$zip->open( $zipFileName , ZipArchive::CREATE);
		$xml = new Dalten_Xml_SimpleXml('<?xml version="1.0" encoding="utf-8"?><offer-list/>');
		$offerXML = $xml->addChild('offer');
		foreach ($data as $key=>$value) {
			$offerXML->addChild($key, $value);
		}

		foreach ($images as $image) {
			$fileName = $image['soubor'];
			if (file_exists($fileName)) {
				$basename = basename($fileName);
				$zip->addFile($fileName, $basename);
				$offerXML->addChild('picture',$basename);
			}
		}

		$xmlToSend = $xml->getMultilineXml();
		if ($this->_logger) {
			$this->_logger->logRemoteCall('XML', $xmlToSend);
		}

		$zip->addFromString('property.xml', $xmlToSend);
		$zip->close();

		return $this->_uploadXMLPackage($zipFileName, $data['id']);
	}

	/**
	 * Stáhne nabídku ze serveru.
	 *
	 * @param array $data Nepřevedená data nabídky.
	 *
	 * @return bool Zda se podařilo nabídku stáhnout.
	 */
	public function deleteListing(array $data)
	{
		$id = $data['kod'];
		$zipFileName = tempnam(sys_get_temp_dir(),'annonce');
		$zip = new ZipArchive();
		$zip->open( $zipFileName , ZipArchive::CREATE);
		$xml = new Dalten_Xml_SimpleXml('<?xml version="1.0" encoding="utf-8"?><offer-list/>');
		$offerXML = $xml->addChild('offer');
		$offerXML['action'] = 'delete';
		$offerXML->addChild('id', $id);

		$xmlToSend = $xml->getMultilineXml();
		if ($this->_logger) {
			$this->_logger->logRemoteCall('XML', $xmlToSend);
		}

		$zip->addFromString('property.xml', $xmlToSend);
		$zip->close();

		return $this->_uploadXMLPackage($zipFileName, $id);
	}

    /**
     * Stáhne seznam zakázek ze serveru.
     *
     * @return Dalten_Export_Api_ServerResponse
     */
	public function listListings()
    {
        $rawResponse = $this->_http->get(
            sprintf(
                'http://dealergw.annonce.cz/iec/Upload-upload?action=get_nems&up_user=%s&up_passwd=%s',
                $this->_username,
                $this->_password
            )
        );

        $response = simplexml_load_string($rawResponse);

        if ($response && $response->offer_list && $response->offer_list->offer) {
            $listingList = [];
            foreach ($response->offer_list->offer as $offer) {
                $listingList[] = [
                    'kod'=>$offer['id']
                ];
            }
            return new Dalten_Export_Api_ServerResponse(true, 200, 'OK', $listingList);
        }
    }

	/**
	 * 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;
		if ($this->_http instanceof Dalten_Export_Api_Backend_LoggableBackendInterface) {
			/** @var $this->_http Dalten_Http_Client */
			$this->_http->setLogger($logger);
		}
		return $this;
	}

	/**
	 * Odstraní nastavený logger pro backend.
	 *
	 * @return Dalten_Export_Api_LoggableApiInterface Fluent interface.
	 */
	public function removeLogger()
	{
		$this->_logger = null;
		if ($this->_http instanceof Dalten_Export_Api_Backend_LoggableBackendInterface) {
			/** @var $this->_http Dalten_Http_Client */
			$this->_http->removeLogger();
		}
		return $this;
	}


}
