<?php
use shop\Order;

error_reporting( E_ALL ^ E_NOTICE ^ E_STRICT ^ E_WARNING ^ E_DEPRECATED );

function __autoload_my_classes( $classname )
{
  $q = explode( '\\', $classname );
  $c = array_pop( $q );
  $f = 'autoload/' . implode( '/', $q ) . '/class.' . $c . '.php';

  if ( file_exists( $f ) )
    require_once( $f );
}

spl_autoload_register( '__autoload_my_classes' );
date_default_timezone_set( 'Europe/Warsaw' );

require_once 'config.php';
require_once 'libraries/medoo/medoo.php';
require_once 'libraries/phpmailer/class.phpmailer.php';
require_once 'libraries/phpmailer/class.smtp.php';

session_start();

if ( !isset( $_SESSION[ 'check' ] ) )
{
  session_regenerate_id();
  $_SESSION[ 'check' ] = true;
  $_SESSION[ 'ip' ] = $_SERVER[ 'REMOTE_ADDR' ];
}

if ( $_SESSION[ 'ip' ] !== $_SERVER[ 'REMOTE_ADDR' ] )
{
  session_destroy();
  header( 'Location: /' );
  exit;
}

$mdb = new medoo( [
    'database_type' => 'mysql',
    'database_name' => $database[ 'name' ],
    'server'        => $database[ 'host' ],
    'username'      => $database[ 'user' ],
    'password'      => $database[ 'password' ],
    'charset'       => 'utf8'
  ] );

$settings = \front\factory\Settings::settings_details();
$baselinker_settings = \front\factory\Shop::baselinker_settings();
$apilo_settings = \admin\factory\Integrations::apilo_settings();
$baselinker_settings = \admin\factory\Integrations::baselinker_settings();
$sellasist_settings = \admin\factory\Integrations::sellasist_settings();

function parsePaczkomatAddress($input) {
  $pattern = '/^(\w+)\s+\|\s+([^,]+),\s+(\d{2}-\d{3})\s+(.+)$/';

  if (preg_match($pattern, $input, $matches)) {
      return [
          'code' => $matches[1],
          'address' => $matches[2],
          'postalCode' => $matches[3],
          'city' => $matches[4],
      ];
  } else {
      return null;
  }
}

function getImageUrlById($id) {
  $apiUrl = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['SERVER_NAME'] . '/api/v1/product.php';

  $ch = curl_init();

  curl_setopt($ch, CURLOPT_URL, $apiUrl . '?id=' . urlencode($id));
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, array(
      'Content-Type: application/json',
  ));

  $response = curl_exec($ch);
  if ($response === false) {
      curl_close($ch);
      return null;
  }

  curl_close($ch);

  $data = json_decode($response, true);

  return isset($data['img']) ? $data['img'] : null;
}

// pobieranie informacji o produkcie z sellasist.pl
if ( $sellasist_settings['enabled'] and $sellasist_settings['sync_products'] and $sellasist_settings['api_code'] )
{
  if ( $result = $mdb -> query( 'SELECT id, sellasist_product_id, sellasist_get_data_date, sellasist_product_name FROM pp_shop_products WHERE sellasist_product_id IS NOT NULL AND sellasist_product_id != 0 AND ( sellasist_get_data_date IS NULL OR sellasist_get_data_date <= \'' . date( 'Y-m-d H:i:s', strtotime( '-10 minutes', time() ) ) . '\' ) ORDER BY sellasist_get_data_date ASC LIMIT 1' ) -> fetch( \PDO::FETCH_ASSOC ) )
  {
    $url = "https://projectpro.sellasist.pl/api/v1/products/" . $result['sellasist_product_id'] . "/";

    $api_code = \admin\factory\Integrations::sellasist_settings( 'api_code' );

    $ch = curl_init( $url );
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
    curl_setopt( $ch, CURLOPT_HTTPHEADER, [
      "apiKey: " . $api_code,
      "accept: application/json"
    ] );

    $response = curl_exec( $ch );
    $responseData = json_decode( $response, true );

    if ( $responseData['id'] )
    {
      $price_brutto = $responseData['price'];
      $vat = $mdb -> get( 'pp_shop_products', 'vat', [ 'apilo_product_id' => $result['apilo_product_id'] ] );
      $price_netto = $price_brutto / ( ( 100 + $vat ) / 100 );

      $mdb -> update( 'pp_shop_products', [ 'price_netto' => \S::normalize_decimal( $price_netto, 2 ), 'price_brutto' => \S::normalize_decimal( $price_brutto, 2 ) ], [ 'sellasist_product_id' => $result['sellasist_product_id'] ] );

      \admin\factory\ShopProduct::update_product_combinations_prices( $result['id'], $price_brutto, $vat, null );
    }

    // aktualizowanie stanu magazynowego
    $mdb -> update( 'pp_shop_products', [ 'quantity' => $responseData['storages'][0]['quantity'] ], [ 'sellasist_product_id' => $result['sellasist_product_id'] ] );

    $mdb -> update( 'pp_shop_products', [ 'sellasist_get_data_date' => date( 'Y-m-d H:i:s' ) ], [ 'sellasist_product_id' => $result['sellasist_product_id'] ] );
    echo '<p>Zaktualizowałem dane produktu <b>' . $result['sellasist_product_name'] . ' #' . $result['id'] . '</b></p>';
  }
}

// pobieranie informacji o produkcie z apilo.com
if ( $apilo_settings['enabled'] and $apilo_settings['sync_products'] and $apilo_settings['access-token'] )
{
  if ( $result = $mdb -> query( 'SELECT id, apilo_product_id, apilo_get_data_date, apilo_product_name FROM pp_shop_products WHERE apilo_product_id IS NOT NULL AND apilo_product_id != 0 AND ( apilo_get_data_date IS NULL OR apilo_get_data_date <= \'' . date( 'Y-m-d H:i:s', strtotime( '-10 minutes', time() ) ) . '\' ) ORDER BY apilo_get_data_date ASC LIMIT 1' ) -> fetch( \PDO::FETCH_ASSOC ) )
  {
    $access_token = \admin\factory\Integrations::apilo_get_access_token();
    $url = 'https://projectpro.apilo.com/rest/api/warehouse/product/' . $result['apilo_product_id'] . '/';
    $curl = curl_init( $url );
    curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
    curl_setopt( $curl, CURLOPT_HTTPHEADER, [
      "Authorization: Bearer " . $access_token,
      "Accept: application/json"
    ] );

    $response = curl_exec( $curl );
    $responseData = json_decode( $response, true );

    // aktualizowanie stanu magazynowego
    $mdb -> update( 'pp_shop_products', [ 'quantity' => $responseData['quantity'] ], [ 'apilo_product_id' => $result['apilo_product_id'] ] );
    // aktualizowanie ceny
    $mdb -> update( 'pp_shop_products', [ 'price_netto' => \S::normalize_decimal( $responseData['priceWithoutTax'], 2 ), 'price_brutto' => \S::normalize_decimal( $responseData['priceWithTax'], 2 ) ], [ 'apilo_product_id' => $result['apilo_product_id'] ] );

    $mdb -> update( 'pp_shop_products', [ 'apilo_get_data_date' => date( 'Y-m-d H:i:s' ) ], [ 'apilo_product_id' => $result['apilo_product_id'] ] );

    echo '<p>Zaktualizowałem dane produktu (APILO) <b>' . $result['apilo_product_name'] . ' #' . $result['id'] . '</b></p>';
  }
}

// synchronizacja cen apilo.com
if ( $apilo_settings['enabled'] and $apilo_settings['access-token'] and ( !$apilo_settings['pricelist_update_date'] or $apilo_settings['pricelist_update_date'] <= date( 'Y-m-d H:i:s', strtotime( '-1 hour', time() ) ) ) )
{
  $access_token = \admin\factory\Integrations::apilo_get_access_token();

  $url = 'https://projectpro.apilo.com/rest/api/warehouse/price-calculated/?price=' . $apilo_settings['pricelist_id'];

  $curl = curl_init( $url );
  curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
  curl_setopt( $curl, CURLOPT_CUSTOMREQUEST, "GET" );
  curl_setopt( $curl, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer " . $access_token,
    "Accept: application/json",
    "Content-Type: application/json"
  ] );

  $response = curl_exec( $curl );
  $responseData = json_decode( $response, true );

  if ( $responseData['list'] )
  {
    foreach ( $responseData['list'] as $product_price )
    {
      //aktualizowanie ceny
      if ( $product_price['customPriceWithTax'] )
      {
        $price_brutto = $product_price['customPriceWithTax'];
        $vat = $vat = $mdb -> get( 'pp_shop_products', 'vat', [ 'apilo_product_id' => $result['apilo_product_id'] ] );
        $price_netto = $price_brutto / ( ( 100 + $vat ) / 100 );

        $mdb -> update( 'pp_shop_products', [ 'price_netto' => \S::normalize_decimal( $price_netto, 2 ), 'price_brutto' => \S::normalize_decimal( $price_brutto, 2 ) ], [ 'apilo_product_id' => $product_price['product'] ] );
        $product_id = $mdb -> get( 'pp_shop_products', 'id', [ 'apilo_product_id' => $product_price['product'] ] );

        \admin\factory\ShopProduct::update_product_combinations_prices( $product_id, $price_brutto, $vat, null );
      }
    }
  }
  \admin\factory\Integrations::apilo_settings_save( 'pricelist_update_date', date( 'Y-m-d H:i:s' ) );
  echo '<p>Zaktualizowałem ceny produktów (APILO)</p>';
}

// pobieranie informachji o produkcie w tym: cen, cen promocyjnych, wagi, stanów magazynowych
if ( $baselinker_settings['enabled'] and $baselinker_settings['sync_products'] and $baselinker_settings['api_code'] )
{
  if ( $result = $mdb -> query( 'SELECT id, baselinker_product_id, baselinker_get_data_date FROM pp_shop_products WHERE baselinker_product_id IS NOT NULL AND baselinker_product_id != 0 AND ( baselinker_get_data_date IS NULL OR baselinker_get_data_date <= \'' . date( 'Y-m-d H:i:s', strtotime( '-1440 minutes', time() ) ) . '\' ) ORDER BY baselinker_get_data_date ASC LIMIT 1' ) -> fetch( \PDO::FETCH_ASSOC ) )
  {
    $methodParams = '{
      "inventory_id": "' . $baselinker_settings['inventory_id'] . '",
      "products": [' . $result['baselinker_product_id'] . ']
    }';

    $apiParams = [
      "token" => $baselinker_settings['api_code'],
      "method" => "getInventoryProductsData",
      "parameters" => $methodParams
    ];

    $curl = curl_init( "https://api.baselinker.com/connector.php" );
    curl_setopt( $curl, CURLOPT_POST, 1 );
    curl_setopt( $curl, CURLOPT_POSTFIELDS, http_build_query( $apiParams ) );
    curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
    $response = json_decode( curl_exec( $curl ), true );

    $i = 0;

    if ( $response['status'] == 'SUCCESS' and count( $response['products'] ) )
    {
      foreach ( $response['products'] as $baselinker_product_id => $baselinker_product )
      {
        // aktualizowanie ceny
        if ( $vat = $mdb -> get( 'pp_shop_products', 'vat', [ 'baselinker_product_id' => $baselinker_product_id ] ) )
        {
          $price_brutto = $baselinker_product['prices'][$baselinker_settings['price_group']];
          $price_netto = $baselinker_product['prices'][$baselinker_settings['price_group']] / ( ( 100 + $vat ) / 100 );

          $price_brutto_promo = $baselinker_product['prices'][ $baselinker_settings['price_group_promo'] ];

          if ( $price_brutto_promo and $price_brutto_promo != $price_brutto )
          {
            $price_netto_promo = $baselinker_product['prices'][$baselinker_settings['price_group_promo']] / ( ( 100 + $vat ) / 100 );
            $mdb -> update( 'pp_shop_products', [ 'price_netto_promo' => \S::normalize_decimal( $price_netto_promo, 2 ), 'price_brutto_promo' => \S::normalize_decimal( $price_brutto_promo, 2 ) ], [ 'baselinker_product_id' => $baselinker_product_id ] );
          }
          else
            $mdb -> update( 'pp_shop_products', [ 'price_netto_promo' => null, 'price_brutto_promo' => null ], [ 'baselinker_product_id' => $baselinker_product_id ] );

          $mdb -> update( 'pp_shop_products', [ 'price_netto' => \S::normalize_decimal( $price_netto, 2 ), 'price_brutto' => \S::normalize_decimal( $price_brutto, 2 ) ], [ 'baselinker_product_id' => $baselinker_product_id ] );

          $product_id = $mdb -> get( 'pp_shop_products', 'id', [ 'baselinker_product_id' => $baselinker_product_id ] );
          $vat = $mdb -> get( 'pp_shop_products', 'vat', [ 'baselinker_product_id' => $baselinker_product_id ] );

          \admin\factory\ShopProduct::update_product_combinations_prices( $product_id, $price_brutto, $vat, $price_brutto_promo );
        }

        // aktualizowanie wagi
        $mdb -> update( 'pp_shop_products', [ 'weight' => $baselinker_product['weight'] ], [ 'baselinker_product_id' => $baselinker_product_id ] );

        // aktualizowanie stanu magazynowego
        $mdb -> update( 'pp_shop_products', [ 'quantity' => $baselinker_product['stock'][$baselinker_settings['stock_id']] ], [ 'baselinker_product_id' => $baselinker_product_id ] );

        $mdb -> update( 'pp_shop_products', [ 'baselinker_get_data_date' => date( 'Y-m-d H:i:s' ) ], [ 'baselinker_product_id' => $baselinker_product_id ] );

        echo '<p>Zaktualizowałem dane produktu <b>' . $baselinker_product['text_fields']['name'] . ' #' . $result['id'] . '</b></p>';
      }
      \S::clear_redis_cache();
    }
    else
    {
      $mdb -> update( 'pp_shop_products', [ 'baselinker_get_data_date' => date( 'Y-m-d H:i:s' ) ], [ 'baselinker_product_id' => $baselinker_product_id ] );
      echo '<p>Z powodu błędu pominąłem produkt o ID: <b>' . $result['id'] . '</b></p>';
    }
  }
}

// sprawdzanie statusów zamówień w sellasist.pl jeżeli zamówienie nie jest zrealizowane, anulowane lub nieodebrane
if ( $sellasist_settings['enabled'] and $sellasist_settings['sync_orders'] and $sellasist_settings['api_code'] and $sellasist_settings['sync_orders_date_start'] <= date( 'Y-m-d H:i:s' ) )
{
  $order = $mdb -> query( 'SELECT id, sellasist_order_id, sellasist_order_status_date, number FROM pp_shop_orders WHERE sellasist_order_id IS NOT NULL AND sellasist_order_id != 0 AND ( status != 6 AND status != 8 AND status != 9 ) AND ( sellasist_order_status_date IS NULL OR sellasist_order_status_date <= \'' . date( 'Y-m-d H:i:s', strtotime( '-30 minutes', time() ) ) . '\' ) ORDER BY sellasist_order_status_date ASC LIMIT 1' ) -> fetch( \PDO::FETCH_ASSOC );
  if ( $order['sellasist_order_id'] )
  {
    $url = "https://projectpro.sellasist.pl/api/v1/orders/" . $order['sellasist_order_id'] . "/";

    $api_code = \admin\factory\Integrations::sellasist_settings( 'api_code' );

    $ch = curl_init( $url );
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
    curl_setopt( $ch, CURLOPT_HTTPHEADER, [
      "apiKey: " . $api_code,
      "accept: application/json"
    ] );

    $response = curl_exec( $ch );
    $responseData = json_decode( $response, true );

    if ( $responseData['id'] and $responseData['status']['id'] ) {
      $shop_status_id = \front\factory\ShopStatuses::get_shop_status_by_integration_status_id( 'sellasist', $responseData['status']['id'] );
      $mdb -> update( 'pp_shop_orders', [ 'status' => $shop_status_id, 'sellasist_order_status_date' => date( 'Y-m-d H:i:s' ) ], [ 'id' => $order['id'] ] );
      echo '<p>Zaktualizowałem status zamówienia <b>' . $order['number'] . '</b></p>';
    }
  }
}

// wysyłanie zamówień do sellasist.pl
if ( $sellasist_settings['enabled'] and $sellasist_settings['sync_orders'] and $sellasist_settings['api_code'] and $sellasist_settings['sync_orders_date_start'] <= date( 'Y-m-d H:i:s' ) )
{
  $orders = $mdb -> select( 'pp_shop_orders', '*', [ 'AND' => [ 'sellasist_order_id' => null, 'date_order[<=]' => date( 'Y-m-d H:i:s', strtotime( '-1 minutes', time() ) ), 'date_order[>=]' => $sellasist_settings['sync_orders_date_start'] ], 'ORDER' => [ 'date_order' => 'ASC' ], 'LIMIT' => 1 ] );
  foreach ( $orders as $order )
  {
    $z = 0;

    $products = $mdb ->  select( 'pp_shop_order_products', '*', [ 'order_id' => $order['id'] ] );
    $products_array = [];
    foreach ( $products as $product )
    {
      $json_data['carts'][] = [
        'id' => $product['product_id'],
        'product_id' => \front\factory\ShopProduct::get_sellasist_product_id( $product['product_id'] ),
        'name' => $product['name'],
        'quantity' => $product['quantity'],
        'price' => $product['price_brutto_promo'] ? $product['price_brutto_promo'] : $product['price_brutto'],
        'message' => strip_tags( $product['attributes'] ) . ' | ' . $product['message']
      ];
    }

    $json_data['id'] = $order['id'];
    $json_data['currency'] = 'pln';
    $json_data['payment_status'] = $order['paid'] ? 'paid' : 'unpaid';
    $json_data['paid'] = $order['paid'] ? str_replace( ',', '.', $order['summary'] ) : 0;
    $json_data['status'] = \front\factory\ShopStatuses::get_sellasist_status_id( $order['status'] );
    $json_data['email'] = $order['client_email'];
    // date
    $json_data['date'] = date( 'Y-m-d H:i:s', strtotime( $order['date_order'] ) );
    // shipment_price
    $json_data['shipment_price'] = $order['transport_cost'];
    // payment_id
    $json_data['payment_id'] = \front\factory\ShopPaymentMethod::get_sellasist_payment_method_id( $order['payment_method_id'] );
    // payment_name
    $json_data['payment_name'] = $order['payment_method'];
    // shipment_id
    $json_data['shipment_id'] = \front\factory\ShopTransport::get_sellasist_transport_id( $order['transport_id'] );
    // shipment_name
    $json_data['shipment_name'] = strip_tags( $order['transport'] );
    // invoice
    $json_data['invoice'] = 0;
    // comment
    $json_data['comment'] = $order['message'];
    // bill_address
    $json_data['bill_address'] = [
      'name' => $order['client_name'],
      'surname' => $order['client_surname'],
      'street' => $order['client_street'],
      'city' => $order['client_city'],
      'postcode' => $order['client_postal_code'],
      'phone' => $order['client_phone'],
      'email' => $order['client_email'],
      'country' => [
        'id' => 170,
        'name' => 'Poland',
        'code' => 'PL'
      ]
    ];
    // shipment_address
    $json_data['shipment_address'] = [
      'name' => $order['client_name'],
      'surname' => $order['client_surname'],
      'street' => $order['client_street'],
      'city' => $order['client_city'],
      'postcode' => $order['client_postal_code'],
      'phone' => $order['client_phone'],
      'email' => $order['client_email'],
      'country' => [
        'id' => 170,
        'name' => 'Poland',
        'code' => 'PL'
      ]
    ];

    // pickup_point
    if ( $order['inpost_paczkomat'] )
    {
      $pickup = explode( ' | ', $order['inpost_paczkomat'] );
      $pickup_code = $pickup[0];
      $pickup_address = $pickup[1];

      $json_data['pickup_point'] = [
        'code' => $pickup_code,
        'type' => 'inpost',
        'address' => $pickup_address
      ];
    }

    // URL docelowe
    $url = "https://projectpro.sellasist.pl/api/v1/orders";

    // Nagłówki
    $headers = array(
      "accept: application/json",
      "apiKey: " . $sellasist_settings['api_code'],
      "Content-Type: application/json"
    );

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($json_data) );
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    if (curl_errno($ch)) {
      echo 'Błąd cURL: ' . curl_error($ch);
    }
    curl_close($ch);
    $response = json_decode( $response, true );

    if ( $response['status'] == 'exist' ) {

      $mdb -> update( 'pp_shop_orders', [ 'sellasist_order_id' => $response['order_id'] ], [ 'id' => $order['id'] ] );

    } else if ( $response['id'] ) {

      $mdb -> update( 'pp_shop_orders', [ 'sellasist_order_id' => $response['id'] ], [ 'id' => $order['id'] ] );
      echo '<p>Wysłałem zamówienie do sellasist.pl</p>';

    }
  }
}

// wysyłanie zamówień do apilo
if ( $apilo_settings['enabled'] and $apilo_settings['sync_orders'] and $apilo_settings['access-token'] and $apilo_settings['sync_orders_date_start'] <= date( 'Y-m-d H:i:s' ) )
{
  $orders = $mdb -> select( 'pp_shop_orders', '*', [ 'AND' => [ 'apilo_order_id' => null, 'date_order[>=]' => $apilo_settings['sync_orders_date_start'] ], 'ORDER' => [ 'date_order' => 'ASC' ], 'LIMIT' => 1 ] );
  foreach ( $orders as $order )
  {
    $products = $mdb ->  select( 'pp_shop_order_products', '*', [ 'order_id' => $order['id'] ] );
    $products_array = [];
    foreach ( $products as $product )
    {
      $sku = \front\factory\ShopProduct::get_product_sku( $product['product_id'] );

      $products_array[] = [
        'idExternal' => $product['product_id'],
        'ean' => \front\factory\ShopProduct::get_product_ean( $product['product_id'] ),
        'sku' => $sku ? $sku : md5( $product['product_id'] ),
        'originalName' => $product['name'],
        'originalPriceWithTax' => $product['price_brutto_promo'] ? str_replace( ',', '.', $product['price_brutto_promo'] ) : str_replace( ',', '.', $product['price_brutto'] ),
        'originalPriceWithoutTax' => $product['price_brutto_promo'] ? str_replace( ',', '.', round( $product['price_brutto_promo'] / ( 1 + $product['vat']/100 ), 2 ) ) : str_replace( ',', '.', round( $product['price_brutto'] / ( 1 + $product['vat']/100 ), 2 ) ),
        'quantity' => $product['quantity'],
        'tax' => number_format( $product['vat'], 2, '.', '' ),
        'status' => 1,
        'type' => 1,
        'media' => $product['message']
      ];

      $order_message .= '<table>';
        $order_message .= '<tr>';
          $order_message .= '<td style="width: 150px; vertical-align: middle; text-align: center;">';
            $order_message .= '<img src="' . getImageUrlById( $product['parent_product_id'] ) . '" style="width: 150px;" alt="">';
          $order_message .= '</td>';
          $order_message .= '<td style="vertical-align: middle;">';
            $order_message .= '<strong>' . $product['name'] . '</strong><br>';
            $order_message .= strip_tags( $product['attributes'] ) . '<br>';
            $order_message .= preg_replace( '/\s+/', ' ', htmlspecialchars( $product['message'] ) ) . '<br>';
            $order_message .= strip_tags( str_replace( '<br>', ' | ', preg_replace( '/\s+/', ' ', $product['custom_fields'] ) ) ) . '<br>';
          $order_message .= '</td>';
        $order_message .= '</tr>';
      $order_message .= '</table>';
      $order_message .= '<hr>';
    }

    //TODO: ostatnio był problem kiedy wiadomość miała mniej 1024 znaki ale zawierała przeniesienie tekstu '<br>' i do tego jeszcze miała emoji. Wtedy APILO tego nie przepuszczał.
    if ( strlen( $order_message ) > 1024 )
      $order_message = '<p><strong>Wiadomość do zamówienia była zbyt długa. Sprawdź szczegóły w panelu sklepu</strong></p>';

    // add transport as product
    $products_array[] = [
      'idExternal' => '',
      'ean' => null,
      'sku' => '',
      'originalName' => strip_tags( $order['transport'] ),
      'originalPriceWithTax' => str_replace( ',', '.', $order['transport_cost'] ),
      'originalPriceWithoutTax' => str_replace( ',', '.', round( $order['transport_cost'] / ( 1 + 23/100 ), 2 ) ),
      'quantity' => 1,
      'tax' => number_format( 23, 2, '.', '' ),
      'status' => 1,
      'type' => 2,
      'media' => null
    ];

    $access_token = \admin\factory\Integrations::apilo_get_access_token();

    $order_date = new DateTime( $order['date_order'] );

    $paczkomatData = parsePaczkomatAddress( $order['inpost_paczkomat'] );

    $postData = [
      'idExternal' => $order['id'],
      'isInvoice' => false,
      'customerLogin' => $order['client_email'],
      'paymentType' => (int)\front\factory\ShopPaymentMethod::get_apilo_payment_method_id( $order['payment_method_id'] ),
      'originalCurrency' => 'PLN',
      'originalAmountTotalWithTax' => str_replace( ',', '.', $order['summary'] ),
      'orderItems' => $products_array,
      'orderedAt' => $order_date -> format('Y-m-d\TH:i:s\Z'),
      'addressCustomer' => [
        'name' => $order['client_name'] . ' ' . $order['client_surname'],
        'phone' => $order['client_phone'],
        'email' => $order['client_email'],
        'streetName' => $order['client_street'] ? $order['client_street'] : $paczkomatData['address'],
        'city' => $order['client_city'] ? $order['client_city'] : $paczkomatData['city'],
        'zipCode' => $order['client_postal_code'] ? $order['client_postal_code'] : $paczkomatData['postalCode']
      ],
      'addressDelivery' => [
        'name' => $order['client_name'] . ' ' . $order['client_surname'],
        'phone' => $order['client_phone'],
        'email' => $order['client_email'],
        'streetName' => $order['client_street'] ? $order['client_street'] : $paczkomatData['address'],
        'city' => $order['client_city'] ? $order['client_city'] : $paczkomatData['city'],
        'zipCode' => $order['client_postal_code'] ? $order['client_postal_code'] : $paczkomatData['postalCode']
      ],
      'carrierAccount' => (int)\front\factory\ShopTransport::get_apilo_carrier_account_id( $order['transport_id'] ),
      'orderNotes' => [ [
        'type' => 1,
        'comment' => 'Wiadomość do zamówienia:<br>' . $order['message'] . '<br><br>' . $order_message
      ] ],
      'status' => (int)\front\factory\ShopStatuses::get_apilo_status_id( $order['status'] ),
      'platformId' => $apilo_settings['platform-id']
    ];

    // jeżeli paczkomat
    if ( $order['inpost_paczkomat'] )
    {
      $postData['addressDelivery']['parcelName'] = $order['inpost_paczkomat'] ? 'Paczkomat: ' . $order['inpost_paczkomat'] : null;
      $postData['addressDelivery']['parcelIdExternal'] = $order['inpost_paczkomat'] ? ( $paczkomat = trim( explode( '|', $order['inpost_paczkomat'] )[0] ) ) : null;

      if ( !$postData['addressDelivery']['zipCode'] or !$postData['addressDelivery']['city'] )
      {
        preg_match("/\b\d{2}-\d{3}\b/", $order['inpost_paczkomat'], $postalCodeMatches);
        $postalCode = $postalCodeMatches[0] ?? '';

        $cityPattern = "/\d{2}-\d{3}\s+(.+)/";
        preg_match($cityPattern, $order['inpost_paczkomat'], $cityMatches);
        $city = $cityMatches[1] ?? '';

        $postData['addressDelivery']['zipCode'] = $postalCode;
        $postData['addressDelivery']['city'] = $city;
      }
    }

    if ( $order['paid'] )
    {
      $payment_date = new DateTime( $order['date_order'] );

      $postData['orderPayments'][] = [
        'amount' => str_replace( ',', '.', $order['summary'] ),
        'paymentDate' => $payment_date -> format('Y-m-d\TH:i:s\Z'),
        'type' => \front\factory\ShopPaymentMethod::get_apilo_payment_method_id( $order['payment_method_id'] )
      ];
    }
    
    $ch = curl_init();
    curl_setopt( $ch, CURLOPT_URL, "https://projectpro.apilo.com/rest/api/orders/" );
    curl_setopt( $ch, CURLOPT_POST, 1 );
    curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( $postData ) );
    curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, "POST" );
    curl_setopt( $ch, CURLOPT_HTTPHEADER, array(
      "Authorization: Bearer " . $access_token,
      "Accept: application/json",
      "Content-Type: application/json"
    ));
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
    $response = curl_exec( $ch );
    if (curl_errno( $ch ) ) {
      echo 'Błąd cURL: ' . curl_error( $ch );
    }
    curl_close( $ch );

    $response = json_decode( $response, true );

    if ( $config['debug']['apilo'] )
    {
      file_put_contents( $_SERVER['DOCUMENT_ROOT'] . '/logs/apilo.txt', date( 'Y-m-d H:i:s' ) . " --- SEND ORDER TO APILO\n\n", FILE_APPEND );
      file_put_contents( $_SERVER['DOCUMENT_ROOT'] . '/logs/apilo.txt', print_r( $postData, true ) . "\n\n", FILE_APPEND );
      file_put_contents( $_SERVER['DOCUMENT_ROOT'] . '/logs/apilo.txt', print_r( $response, true ) . "\n\n", FILE_APPEND );
    }

    if ( $response['message'] == 'Order already exists' )
    {
      $apilo_order_id = str_replace( 'Order id: ', '', $response['description'] );
      $mdb -> update( 'pp_shop_orders', [ 'apilo_order_id' => $apilo_order_id ], [ 'id' => $order['id'] ] );
      echo '<p>Zaktualizowałem id zamówienia na podstawie zamówienia apilo.com</p>';
    }
    elseif ( $response['message'] == 'Validation error' )
    {
      \S::pre( $response );
      \S::pre( $postData );
    }
    else
    {
      $mdb -> update( 'pp_shop_orders', [ 'apilo_order_id' => $response['id'] ], [ 'id' => $order['id'] ] );
      echo '<p>Wysłałem zamówienie do apilo.com: ID: ' . $order['id'] . ' - ' . $response['id'] . '</p>';
    }
  }
}

// sprawdzanie statusów zamówień w apilo.com jeżeli zamówienie nie jest zrealizowane, anulowane lub nieodebrane
if ( $apilo_settings['enabled'] and $apilo_settings['sync_orders'] and $apilo_settings['access-token'] and $apilo_settings['sync_orders_date_start'] <= date( 'Y-m-d H:i:s' ) )
{
  $order = $mdb -> query( 'SELECT id, apilo_order_id, apilo_order_status_date, number FROM pp_shop_orders WHERE apilo_order_id IS NOT NULL AND ( status != 6 AND status != 8 AND status != 9 ) AND ( apilo_order_status_date IS NULL OR apilo_order_status_date <= \'' . date( 'Y-m-d H:i:s', strtotime( '-30 minutes', time() ) ) . '\' ) ORDER BY apilo_order_status_date ASC LIMIT 1' ) -> fetch( \PDO::FETCH_ASSOC );
  if ( $order['apilo_order_id'] )
  {
    $access_token = \admin\factory\Integrations::apilo_get_access_token();
    $url = 'https://projectpro.apilo.com/rest/api/orders/' . $order['apilo_order_id'] . '/';

    $ch = curl_init( $url );
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
    curl_setopt( $ch, CURLOPT_HTTPHEADER, [
      "Authorization: Bearer " . $access_token,
      "Accept: application/json"
    ] );

    $response = curl_exec( $ch );
    $responseData = json_decode( $response, true );

    if ( $responseData['id'] and $responseData['status'] )
    {
      $shop_status_id = \front\factory\ShopStatuses::get_shop_status_by_integration_status_id( 'apilo', $responseData['status'] );

      $order_tmp = new Order( $order['id'] );
      $order_tmp -> update_status( $shop_status_id, false );
      $order_tmp -> update_aplio_order_status_date( date( 'Y-m-d H:i:s' ) );
      echo '<p>Zaktualizowałem status zamówienia <b>' . $order['number'] . '</b></p>';
    }
  }
}

// sprawdzanie statusów zamówień w baselinker.com jeżeli zamówienie nie jest zrealizowane, anulowane lub nieodebrane
if ( $baselinker_settings['enabled'] and $baselinker_settings['sync_orders'] and $baselinker_settings['api_code'] and $baselinker_settings['sync_orders_date_start'] <= date( 'Y-m-d H:i:s' ) )
{
  $order = $mdb -> query( 'SELECT id, baselinker_order_id, baselinker_order_status_date, number FROM pp_shop_orders WHERE baselinker_order_id IS NOT NULL AND baselinker_order_id != 0 AND ( status != 6 AND status != 8 AND status != 9 ) AND ( baselinker_order_status_date IS NULL OR baselinker_order_status_date <= \'' . date( 'Y-m-d H:i:s', strtotime( '-30 minutes', time() ) ) . '\' ) ORDER BY baselinker_order_status_date ASC LIMIT 1' ) -> fetch( \PDO::FETCH_ASSOC );
  if ( $order['baselinker_order_id'] )
  {
    $methodParams = '{
      "order_id": ' . $order['baselinker_order_id'] . '
    }';

    $apiParams = [
      "token" => $baselinker_settings['api_code'],
      "method" => "getOrders",
      "parameters" => $methodParams
    ];

    $curl = curl_init( "https://api.baselinker.com/connector.php" );
    curl_setopt( $curl, CURLOPT_POST, 1 );
    curl_setopt( $curl, CURLOPT_POSTFIELDS, http_build_query( $apiParams ) );
    curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
    $response = json_decode( curl_exec( $curl ), true );

    if ( $response['status'] == 'SUCCESS' and count( $response['orders'] ) )
    {
      $shop_status_id = \shop\ShopStatus::get_shop_status_by_baselinker_status( (int) $response['orders'][0]['order_status_id'] );

      $order_tmp = new Order( $order['id'] );
      $order_tmp -> update_status( $shop_status_id, false );
      $order_tmp -> update_baselinker_order_status_date( date( 'Y-m-d H:i:s' ) );
      echo '<p>Zaktualizowałem status zamówienia <b>' . $order['number'] . '</b></p>';
    }
    else
    {
      $mdb -> update( 'pp_shop_orders', [ 'baselinker_order_status_date' => date( 'Y-m-d H:i:s' ) ], [ 'id' => $order['id'] ] );
      echo '<p>Z powodu błędu pominąłem zamówienie o ID: <b>' . $order['id'] . '</b></p>';
    }
  }
}

// wysyłanie zamówień do baselinker
if (  $baselinker_settings['enabled'] and $baselinker_settings['sync_orders'] and $baselinker_settings['api_code'] and $baselinker_settings['sync_orders_date_start'] <= date( 'Y-m-d H:i:s' ) )
{
  $orders = $mdb -> select( 'pp_shop_orders', '*', [ 'AND' => [ 'baselinker_order_id' => null, 'date_order[<=]' => date( 'Y-m-d H:i:s', strtotime( '-1 minutes', time() ) ), 'date_order[>=]' => $baselinker_settings['sync_orders_date_start'] ], 'ORDER' => [ 'date_order' => 'ASC' ], 'LIMIT' => 1 ] );
  foreach ( $orders as $order )
  {
    if ( $order['transport_id'] == 2 )
    {
      $pickup = explode( ' | ', $order['inpost_paczkomat'] );
      $pickup_name = $pickup[0];
      $pickup_address = $pickup[1];
    }

    $methodParams = '{
        "order_status_id": "' . \front\factory\ShopStatuses::get_baselinker_order_status_id( $order['status'] ) . '",
        "date_add": "' . strtotime( $order['date_order'] ) . '",
        "user_comments": "' . preg_replace('/\s+/', ' ', \S::remove_special_chars( $order['message'] ) ) . '",
        "admin_comments": "' . $order['notes'] . '",
        "phone": "' . $order['client_phone'] . '",
        "email": "' . $order['client_email'] . '",
        "user_login": "' . $order['client_name'] . ' ' . $order['client_surname'] . '",
        "currency": "PLN",
        "payment_method": "' . $order['payment_method'] . '",
        "payment_method_cod": "' . ( $order['payment_method_id'] == 3 ? 1 : 0 ) . '",
        "paid": "' . $order['paid'] . '",
        "delivery_method": "' . strip_tags( $order['transport'] ) . '",
        "delivery_price": "' . $order['transport_cost'] . '",
        "delivery_fullname": "' . $order['client_name'] . ' ' . $order['client_surname'] . '",
        "delivery_company": "' . $order['client_firm'] . '",
        "delivery_address": "' . $order['client_street'] . '",
        "delivery_city": "' . $order['client_city'] . '",
        "delivery_postcode": "' . $order['client_postal_code'] . '",
        "delivery_country_code": "PL",
        "delivery_point_id": "' . $pickup_name . '",
        "delivery_point_name": "' . ( $pickup_name != '' ? 'Paczkomat ' . $pickup_name : '' ) . '",
        "delivery_point_address": "' . $pickup_address . '",
        "delivery_point_postcode": "",
        "delivery_point_city": "",
        "invoice_fullname": "",
        "invoice_company": "",
        "invoice_nip": "",
        "invoice_address": "",
        "invoice_city": "",
        "invoice_postcode": "",
        "invoice_country_code": "",
        "want_invoice": "0",
        "extra_field_1": "",
        "extra_field_2": "",
        "products": [';
          $products = $mdb ->  select( 'pp_shop_order_products', '*', [ 'order_id' => $order['id'] ] );
          foreach ( $products as $product )
          {
            $methodParams .= '{
                "storage": "db",
                "storage_id": 0,
                "product_id": "' .\shop\Product::get_baselinker_product_id( (int)$product['product_id'] ) . '",
                "variant_id": "",
                "name": "' . htmlspecialchars( $product['name'] ) . '",
                "sku": "' . \shop\Product::get_product_sku( (int)$product['product_id'] ) . '",
                "ean": "",
                "attributes": "' . strip_tags( $product['attributes'] ) . ' | ' . strip_tags( str_replace( '<br>', ' | ', preg_replace( '/\s+/', ' ', $product['custom_fields'] ) ) ) . ' | ' . preg_replace( '/\s+/', ' ', htmlspecialchars( $product['message'] ) ) . '",
                "price_brutto": ' . ( $product['price_brutto_promo'] ? $product['price_brutto_promo'] : $product['price_brutto']  ) . ',
                "tax_rate": ' . $product['vat'] . ',
                "quantity": ' . $product['quantity'] . ',
                "weight": 1
            }';

            if ( $product != end( $products ) )
              $methodParams .= ',';
          }
      $methodParams .= ']
    }';

    $apiParams = [
        "token" => $baselinker_settings['api_code'],
        "method" => "addOrder",
        "parameters" => $methodParams
    ];

    $curl = curl_init( "https://api.baselinker.com/connector.php" );
    curl_setopt( $curl, CURLOPT_POST, 1 );
    curl_setopt( $curl, CURLOPT_POSTFIELDS, http_build_query( $apiParams ) );
    curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
    $response = json_decode( curl_exec( $curl ), true );

    if ( $response['status'] == 'SUCCESS' )
    {
      $mdb -> update( 'pp_shop_orders', [ 'baselinker_order_id' => $response['order_id'] ], [ 'id' => $order['id'] ] );
      echo '<p>Wysłałem zamówienie do Baselinker <b>' . $order['number'] . '</b></p>';
    }
  }
}

/* zapisywanie historii cen produktów */
$results = $mdb ->  select( 'pp_shop_products', [ 'id', 'price_brutto', 'price_brutto_promo' ], [ 'OR' => [ 'price_history_date[!]' => date( 'Y-m-d' ), 'price_history_date' => null ], 'ORDER' => [ 'price_history_date' => 'ASC' ], 'LIMIT' => 100 ] );
foreach ( $results as $row )
{
  $mdb -> insert( 'pp_shop_product_price_history', [
    'id_product' => $row['id'],
    'price' => $row['price_brutto_promo'] > 0 ? $row['price_brutto_promo'] : $row['price_brutto'],
    'date' => date( 'Y-m-d' )
  ] );

  $mdb -> update( 'pp_shop_products', [ 'price_history_date' => date( 'Y-m-d' ) ], [ 'id' => $row['id'] ] );

  $mdb -> delete( 'pp_shop_product_price_history', [ 'date[<=]' => date( 'Y-m-d', strtotime( '-31 days', time() ) ) ] );
  echo '<p>Zapisuję historyczną cenę dla produktu <b>#' . $row['id'] . '</b></p>';
}

/* parsowanie zamówień m.in. pod kątem najczęściej sprzedawanych razem produktów */
$orders = $mdb -> select( 'pp_shop_orders', 'id', [ 'parsed' => 0, 'LIMIT' => 1 ] );
foreach ( $orders as $order )
{
  $products = $mdb -> select( 'pp_shop_order_products', 'product_id', [ 'order_id' => $order ] );
  foreach ( $products as $product1 )
  {
    if ( $parent_id = $mdb -> get( 'pp_shop_products', 'parent_id', [ 'id' => $product1 ] ) )
      $product1 = $parent_id;

    foreach ( $products as $product2 )
    {
      if ( $parent_id = $mdb -> get( 'pp_shop_products', 'parent_id', [ 'id' => $product2 ] ) )
        $product2 = $parent_id;

      if ( $product1 != $product2 )
      {
        $intersection_id = $mdb -> query( 'SELECT * FROM pp_shop_orders_products_intersection WHERE product_1_id = :product_1_id AND product_2_id = :product_2_id OR product_1_id = :product_2_id AND product_2_id = :product_1_id', [ 'product_1_id' => (int)$product1, 'product_2_id' => (int)$product2 ] ) -> fetch( \PDO::FETCH_ASSOC );
        if ( $intersection_id )
        {
          $mdb -> update( 'pp_shop_orders_products_intersection', [ 'count' => $intersection_id['count'] + 1 ], [ 'id' => $intersection_id['id'] ] );
        }
        else
        {
          $mdb -> insert( 'pp_shop_orders_products_intersection', [ 'product_1_id' => (int)$product1, 'product_2_id' => (int)$product2, 'count' => 1 ] );
        }
      }
    }
  }
  $mdb -> update( 'pp_shop_orders', [ 'parsed' => 1 ], [ 'id' => $order ] );
  echo '<p>Parsuję zamówienie <b>#' . $order . '</b></p>';
}