<?php


define("WSDL_LOGON", "https://product-api.affili.net/Authentication/Logon.svc?wsdl");
define("WSDL_PROD", "https://product-api.affili.net/V3/WSDLFactory/Product_ProductData.wsdl");

define("DIY_AFF_USER", "720303");
define("DIY_AFF_PUBLISHER_PASS", "N4Qe53rHWcJm15fkD81w");
define("DIY_AFF_PRODUCT_PASS", "NlAi2kcUQvtxCifvCw8d");

class DiyAffHandler {

  var $soap_login;
  var $soap_product;
  var $login_token;

  public function __construct() {
    $this->soap_login = new SoapClient(WSDL_LOGON);
    $this->soap_product = new SoapClient(WSDL_PROD);
  }

  public function login() {
    $token = $this->soap_login->Logon(array(
      'Username' => DIY_AFF_USER,
      'Password' => DIY_AFF_PRODUCT_PASS,
      'WebServiceType' => 'Product'
    ));

    if($token) {
      $this->login_token = $token;
    }
  }

  public function refresh() {
    if(!empty($this->login_token)) {
      $expiration = $this->soap_login->GetIdentifierExpiration($this->login_token);
      $expiration_time = strtotime($expiration);
      $now = time();

      if($now + 300 > $expiration_time) {
        $this->login();
      }
    }
  }

  public function getShopList() {

    $req = $this->soap_product->GetShopList(array(
      'PublisherId' => DIY_AFF_USER,
      'CredentialToken' => $this->login_token,
      'LogoScale' => 'Logo468',
      'PageSettings' => array(
        'PageSize' => 5000
      ),
    ));

    if(isset($req->Shops->Shop) && !empty($req->Shops->Shop)) {
      return $req->Shops->Shop;
    }

    return false;
  }

  public function getProductsForShop($shop, $category) {

    $req = $this->soap_product->SearchProducts(array(
      'PublisherId' => DIY_AFF_USER,
      'CredentialToken' => $this->login_token,
      'ShopIds' => array($shop->ShopId),
      'CategoryIds' => array($category->Id),
      'PageSettings' => array(
        'CurrentPage' => 1,
        'PageSize' => 500
      ),
      'ImageScales' => array('OriginalImage'),
      'SortBy' => 'Price',
      'SortOrder' => 'ascending',
      'MinimumPrice' => 0
    ));

    // Additional validation
    if(!isset($req->Products->Product) || empty($req->Products->Product)) {

      // if total pages is one, or bigger, but product is empty, something went wrong
      if($req->ProductsSummary->TotalPages >= 1) {
        $empty_cs = variable_get('diy_affilinet_empty_cs', '');
        $empty_cs_pieces = explode(';', $empty_cs);
        $empty_cs_pieces[] = $shop->ShopId . ':' . $category->Id;

        variable_set('diy_affilinet_empty_cs', implode(';', $empty_cs_pieces));
      }

      return;
    }

    if($req->ProductsSummary->TotalPages > 1) {
      $totalPages = $req->ProductsSummary->TotalPages;
      $products = $req->Products->Product;

      $last_product = array_pop($req->Products->Product);
      $min_price = $last_product->PriceInformation->PriceDetails->Price;

      $loop = 0;

      while(TRUE) {

        $req = $this->soap_product->SearchProducts(array(
          'PublisherId' => DIY_AFF_USER,
          'CredentialToken' => $this->login_token,
          'ShopIds' => array($shop->ShopId),
          'CategoryIds' => array($category->Id),
          'PageSettings' => array(
            'CurrentPage' => 1,
            'PageSize' => 500
          ),
          'ImageScales' => array('OriginalImage'),
          'SortBy' => 'Price',
          'SortOrder' => 'ascending',
          'MinimumPrice' => $min_price
        ));

        $pages_left = $req->ProductsSummary->TotalPages;

        $last_product = array_pop($req->Products->Product);
        $min_price = $last_product->PriceInformation->PriceDetails->Price;

        $req->Products->Product = $this->_fixSingleProductIssue($req->Products->Product);
        $products = array_merge($products, $req->Products->Product);
        $loop++;

        if($pages_left == 1 || $loop == $totalPages + 10) {
          break;
        }
      }

      $req->Products->Product = $products;
    }

    $req->Products->Product = $this->_fixSingleProductIssue($req->Products->Product);

    if(!empty($req->Products->Product)) {
      return $req;
    }

    return false;
  }

  public function getCategoriesForShop($shop) {
    $req = $this->soap_product->GetCategoryList(array(
      'PublisherId' => DIY_AFF_USER,
      'CredentialToken' => $this->login_token,
      'ShopId' => $shop->ShopId
    ));

    if(isset($req->Categories->Category) && !empty($req->Categories->Category)) {
      return $req->Categories->Category;
    }

    return false;
  }

  /**
   * Search for all products with given ean
   */
  public function searchProducts($ean) {

    $req = $this->soap_product->SearchProducts(array(
      'PublisherId' => DIY_AFF_USER,
      'CredentialToken' => $this->login_token,
      'PageSettings' => array(
        'CurrentPage' => 1,
        'PageSize' => 500
      ),
      'FilterQueries' => array(
        array(
          'DataField' => 'EAN',
          'FilterValue' => $ean
        )
      )
    ));

    if(isset($req->Products->Product) && !empty($req->Products->Product)) {
      $products = $this->_fixSingleProductIssue($req->Products->Product);
      return $products;
    }

    return false;
  }

  public function handleProduct($product, $shop_nid) {

    // dont try to add products without external id or without title
    if(!isset($product->ProductId) || !isset($product->ProductName)) {
      return;
    }

    $result = db_select('diy_product', 'd')
      ->fields('d', array('external_id', 'updated'))
      ->condition('d.external_id', $product->ProductId,'=')
      ->condition('d.source', 'affilinet','=')
      ->execute()
      ->fetchAssoc();

    if($result && isset($result['external_id'])) {
      // Check Change
      if(strtotime($product->LastProductChange) > $result['updated']) {
        // update Product
        $this->updateProduct($product, $shop_nid);
      } else {
        // product is up to date
      }

    } else {
      // insert new Product
      $this->insertProduct($product, $shop_nid);
    }
  }

  public function updateProduct($product, $shop_nid) {
    $fields = $this->getProductFields($product, $shop_nid);

    $ProductId = db_update('diy_product')
      ->fields($fields)
      ->condition('external_id', $product->ProductId)
      ->condition('source', 'affilinet','=')
      ->execute();

    // additionally update drupal node, if one exists
    $query = new EntityFieldQuery();
    $query->entityCondition('entity_type', 'node')
      ->entityCondition('bundle', 'product')
      ->fieldCondition('field_external_id', 'value', $fields['external_id'], '=')
      ->fieldCondition('field_source', 'value', $fields['source'], '=')
      ->fieldCondition('field_overwriten', 'value', 1, '!=')
      ->fieldCondition('field_last_change', 'value', $fields['updated'], '<')
      ->addMetaData('account', user_load(1));
    $result = $query->execute();

    if(isset($result['node'])) {
      $keys = array_keys($result['node']);
      foreach($keys as $nid) {
        $product_node = node_load($nid);

        $Product = new DiyProduct();
        $Product->data = $fields;
        $Product->updateProduct($product_node);
      }
    }
  }

  public function insertProduct($product, $shop_nid) {
    $fields = $this->getProductFields($product, $shop_nid);
    $fields['created'] = time();

    $ProductId = db_insert('diy_product')
      ->fields($fields)
      ->execute();
  }

  public function getProductFields($product, $shop_nid = NULL) {
    $fields = array(
      'external_id' => $product->ProductId,
      'article_number' => $product->ArticleNumber,
      'description' => $product->Description,
      'title' => $product->ProductName,
      'deeplink' => $product->Deeplink1,
      'producer' => $product->Brand,
      'ean' => $product->EAN
    );

    if($shop_nid) {
      $fields['shop_nid'] = $shop_nid;
    } else {
      if($shop_nid = diy_product_get_shop_by_shopid($product->ShopId)) {
        $fields['shop_nid'] = $shop_nid;
      } else {
        return false;
      }
    }

    $fields['source'] = 'affilinet';
    $fields['updated'] = strtotime($product->LastProductChange);

    if(isset($product->PriceInformation->PriceDetails->Price)) {
      $fields['price'] = $product->PriceInformation->PriceDetails->Price;
    }

    if(isset($product->PriceInformation->ShippingDetails->Shipping)) {
      $fields['delivery_costs'] = $product->PriceInformation->ShippingDetails->Shipping;
    }

    if(isset($product->Images->ImageCollection->Image->URL)) {
      $fields['image_url'] = $product->Images->ImageCollection->Image->URL;
    }

    if(isset($product->Properties->Property)) {

      // regarding the documentation, Properties should always be a array
      // Contorion does NOT have a array here, when there is only one property
      if(is_array($product->Properties->Property)) {
        $props = $product->Properties->Property;
      } else {
        // workaround for contorion - does not provide a array here
        $props = array($product->Properties->Property);
      }

      foreach($props as $property) {
        if($property->PropertyName == 'CF_delivery_time') {
          $fields['delivery_time'] = $property->PropertyValue;
        }
        if($property->PropertyName == 'CF_delivery') {
          $fields['delivery_time'] = $property->PropertyValue;
        }
        if($property->PropertyName == 'CF_Lieferzeit') {
          $fields['delivery_time'] = $property->PropertyValue;
        }
      }
    }

    return $fields;
  }

  public function handleShop($shop) {
    $query = new EntityFieldQuery();

    $query->entityCondition('entity_type', 'node')
      ->entityCondition('bundle', 'shop')
      ->fieldCondition('field_external_id', 'value', $shop->ShopId, '=')
      ->range(0, 1)
      ->addMetaData('account', user_load(1));
    $result = $query->execute();

    if(isset($result['node'])) {
      $keys = array_keys($result['node']);
      $first = reset($keys);
      $shop_node = node_load($first);

      $field_last_change = field_get_items('node', $shop_node, 'field_last_change');

      if(strtotime($shop->LastUpdate) > $field_last_change[0]['value']) {
        $this->updateShop($shop, $shop_node);
      }

      return $shop_node->nid;
    } else {
      return $this->insertShop($shop);
    }
  }

  public function updateShop($shop, $shop_node) {
    diy_product_log('update shop' . $shop->ShopTitle);
    $this->saveShop($shop, $shop_node);
  }

  public function insertShop($shop) {
    global $user;

    diy_product_log('insert shop' . $shop->ShopTitle);

    $values = array(
      'type' => 'shop',
      'uid' => $user->uid,
      'status' => 1,
      'promote' => 0,
    );

    $entity = entity_create('node', $values);
    return $this->saveShop($shop, $entity);
  }

  function saveShop($shop, $entity) {
    $ewrapper = entity_metadata_wrapper('node', $entity);

    $ewrapper->title->set($shop->ShopTitle);

    if(isset($shop->ShopId)) {
      $ewrapper->field_external_id->set(intval($shop->ShopId));
    }

    if(isset($shop->ShopLink)) {
      $ewrapper->field_link->set(array('url' => $shop->ShopLink));
    }

    if(isset($shop->LastUpdate)) {
      $ewrapper->field_last_change->set(strtotime($shop->LastUpdate));
    }

    // do not replace existing shop logo DIY-404
    $existing_logo = $ewrapper->field_logo->value();
    if(!isset($existing_logo['fid'])) {
      if(isset($shop->Logo) && isset($shop->Logo->URL) && !empty($shop->Logo->URL)) {
        $file_info = system_retrieve_file($shop->Logo->URL, 'public://product/', TRUE);
        if($file_info->fid) {
          $ewrapper->field_logo->set(array('fid' => $file_info->fid));
        }
      }
    }

    $ewrapper->save();
    return $ewrapper->nid->value();
  }

  public function _transformDateTime($dateTime) {
    return date("Y-m-d H:m:s", strtotime($dateTime));
  }

  public function _fixSingleProductIssue($p) {
    if(!is_array($p)) {
      return array($p);
    }

    return $p;
  }
}