<?php
namespace admin\factory;
use shop\Product;
class ShopProduct
{
  private static function seoLinkUsedByOtherProduct( int $product_id, string $lang_id, string $seo_link ): bool
  {
    global $mdb;

    if ( !$seo_link )
      return false;

    return (bool) $mdb -> count( 'pp_shop_products_langs', [
      'AND' => [
        'lang_id' => $lang_id,
        'seo_link' => $seo_link,
        'product_id[!]' => $product_id,
      ],
    ] );
  }

  private static function removeConflictingRedirectSources( int $product_id, string $lang_id, string $from ): void
  {
    global $mdb;

    if ( !$from )
      return;

    $mdb -> delete( 'pp_redirects', [
      'AND' => [
        'from' => $from,
        'lang_id' => $lang_id,
        'product_id[!]' => $product_id,
      ],
    ] );
  }

  // count_product
  static public function count_product( $where = null )
  {
    global $mdb;

    if ( $where )
      return $mdb -> count( 'pp_shop_products', $where );
    else
      return $mdb -> count( 'pp_shop_products', [ 'archive' => 0 ] );
  }

  static public function update_product_combinations_prices( int $product_id, $price_brutto, $vat, $price_brutto_promo )
  {
    global $mdb;

    $products = $mdb -> query( 'SELECT psp.id, parent_id '
      . 'FROM pp_shop_products AS psp '
      . 'INNER JOIN pp_shop_products_attributes AS pspa ON psp.id = pspa.product_id '
      . 'INNER JOIN pp_shop_attributes_values AS psav ON pspa.value_id = psav.id '
      . 'WHERE psav.impact_on_the_price > 0 AND psp.parent_id = :product_id', [ ':product_id' => $product_id ] ) -> fetchAll( \PDO::FETCH_ASSOC );
    foreach ( $products as $product )
    {
      $price_brutto_combination = $price_brutto;
      $price_brutto_promo_combination = $price_brutto_promo;

      $values = $mdb -> query( 'SELECT impact_on_the_price FROM pp_shop_attributes_values AS psav INNER JOIN pp_shop_products_attributes AS pspa ON pspa.value_id = psav.id WHERE impact_on_the_price IS NOT NULL AND product_id = :product_id', [ ':product_id' => $product['id'] ] ) -> fetchAll( \PDO::FETCH_ASSOC );
      foreach ( $values as $value )
      {
        $price_brutto_combination += $value['impact_on_the_price'];
        if ( $price_brutto_promo )
          $price_brutto_promo_combination += $value['impact_on_the_price'];
        else
          $price_brutto_promo_combination = null;
      }

      $price_netto_combination = \S::normalize_decimal( $price_brutto_combination / ( 100 + $vat ) * 100, 2 );
      if ( $price_brutto_promo_combination )
        $price_netto_promo_combination = \S::normalize_decimal( $price_brutto_promo_combination / ( 100 + $vat ) * 100, 2 );
      else
        $price_netto_promo_combination = null;

      $mdb -> update( 'pp_shop_products', [ 'price_netto' => $price_netto_combination, 'price_brutto' => $price_brutto_combination, 'price_netto_promo' => $price_netto_promo_combination, 'price_brutto_promo' => $price_brutto_promo_combination ], [ 'id' => $product['id'] ] );
    }
  }

  // szybka zmiana statusu produktu
  static public function change_product_status( int $product_id ) {
    global $mdb;

    $status = $mdb -> get( 'pp_shop_products', 'status', [ 'id' => $product_id ] );
    $status = $status == 1 ? 0 : 1;
    return $mdb -> update( 'pp_shop_products', [ 'status' => $status ], [ 'id' => $product_id ] );
  }

  // domyślna nazwa produktu
  static public function product_default_name( int $product_id ) {
    global $mdb;

    $default_lang = $mdb -> get( 'pp_langs', 'id', [ 'start' => 1 ] );
    return $mdb -> get( 'pp_shop_products_langs', 'name', [ 'AND' => [ 'product_id' => $product_id, 'lang_id' => $default_lang ] ] );
  }

  // szybka zmiana google xml label
  static public function product_change_custom_label( int $product_id, $custom_label, $value )
  {
    global $mdb;
    return $mdb -> update( 'pp_shop_products', [ 'custom_label_' . $custom_label => $value ? $value : null ], [ 'id' => $product_id ] );
  }

  // szybka zmiana ceny promocyjnej
  static public function product_change_price_brutto_promo( int $product_id, $price )
  {
    global $mdb;

    $vat = $mdb -> get( 'pp_shop_products', 'vat', [ 'id' => $product_id ] );
    $price_netto = \S::normalize_decimal( (float)$price / ( 100 + (float)$vat ) * 100, 2 );

    return $mdb -> update( 'pp_shop_products', [ 'price_brutto_promo' => $price != 0.00 ? $price : null, 'price_netto_promo' => $price_netto != 0.00 ? $price : null ], [ 'id' => $product_id ] );
  }

  // szybka zmiana ceny
  static public function product_change_price_brutto( int $product_id, $price )
  {
    global $mdb;

    $vat = $mdb -> get( 'pp_shop_products', 'vat', [ 'id' => $product_id ] );
    $price_netto = \S::normalize_decimal( (float)$price / ( 100 + (float)$vat ) * 100, 2 );

    return $mdb -> update( 'pp_shop_products', [ 'price_brutto' => $price != 0.00 ? $price : null, 'price_netto' => $price_netto != 0.00 ? $price : null ], [ 'id' => $product_id ] );
  }

  // pobierz id produktu głównego
  static public function get_product_parent_id( int $product_id )
  {
    global $mdb;
    return $mdb -> get( 'pp_shop_products', 'parent_id', [ 'id' => $product_id ] );
  }

  // usunięcie kombinacji produktu
  static public function delete_combination( int $combination_id )
  {
    global $mdb;

    $mdb -> delete( 'pp_shop_products', [ 'id' => $combination_id ] );
    $mdb -> delete( 'pp_shop_products_attributes', [ 'product_id' => $combination_id ] );

    return true;
  }

  // pobranie permutacji produktu
  static public function get_product_permutations( int $product_id )
  {
    global $mdb;

    $results = $mdb -> select( 'pp_shop_products', 'id', [ 'parent_id' => $product_id ] );
    if ( \S::is_array_fix( $results ) ) foreach ( $results as $row )
      $products[] = \admin\factory\ShopProduct::product_details( $row );

    return $products;
  }

  // generowanie kombinacji produktu
  static public function generate_permutation( int $product_id, $attributes )
  {
    global $mdb;

    $vat = $mdb -> get( 'pp_shop_products', 'vat', [ 'id' => $product_id ] );
    $attributeRepository = new \Domain\Attribute\AttributeRepository( $mdb );

    $permutations = \shop\Product::array_cartesian( $attributes );
    if ( \S::is_array_fix( $permutations ) ) foreach ( $permutations as $permutation )
    {
      $product = null;
      ksort( $permutation );

      $permutation_hash = '';

      if ( \S::is_array_fix( $permutation ) ) foreach ( $permutation as $key => $val )
      {
        if ( $permutation_hash )
          $permutation_hash .= '|';

        $permutation_hash .= $key . '-' . $val;

        // sprawdzenie czy atrybut ma wpływ na cenę
        $value_details = $attributeRepository -> valueDetails( (int)$val );
        $impact_on_the_price = $value_details[ 'impact_on_the_price' ];

        if ( $impact_on_the_price > 0 )
        {
          if ( !$product )
            $product = \admin\factory\ShopProduct::product_details( $product_id );

          $product_price_brutto = $product['price_brutto'] + $impact_on_the_price;
          $product_price_netto = $product_price_brutto / ( 1 + ( $product['vat'] / 100 ) );

          if ( $product['price_brutto_promo'] )
          {
            $product_price_brutto_promo = $product['price_brutto_promo'] + $impact_on_the_price;
            $product_price_netto_promo = $product_price_brutto_promo / ( 1 + ( $product['vat'] / 100 ) );
          }
          else
          {
            $product_price_brutto_promo = null;
            $product_price_netto_promo = null;
          }
        }

        if ( $permutation_hash and !$mdb -> count( 'pp_shop_products', [ 'AND' => [ 'parent_id' => $product_id, 'permutation_hash' => $permutation_hash ] ] ) )
        {
          if ( $mdb -> insert( 'pp_shop_products', [ 'parent_id' => $product_id, 'permutation_hash' => $permutation_hash, 'vat' => $vat ] ) )
          {
            $combination_id = $mdb -> id();
            if ( $product )
            {
              $mdb -> update( 'pp_shop_products', [ 'price_netto' => $product_price_netto, 'vat' => $product['vat'], 'price_brutto' => $product_price_brutto, 'price_netto_promo' => $product_price_netto_promo, 'price_brutto_promo' => $product_price_brutto_promo ], [ 'id' => $combination_id ] );
            }

            $permutation_hash_rev_rows = explode( '|', $permutation_hash );
            foreach ( $permutation_hash_rev_rows as $permutation_hash_rev )
            {
              $attribute_rev = explode( '-', $permutation_hash_rev );
              $mdb -> insert( 'pp_shop_products_attributes', [ 'product_id' => $combination_id, 'attribute_id' => $attribute_rev[0], 'value_id' => $attribute_rev[1] ] );
            }
          }
        }
      }
    }
    return true;
  }

  public static function product_name($product_id)
  {
    global $mdb;

    $results = $mdb -> query("SELECT pspl.name FROM pp_shop_products_langs AS pspl, pp_langs AS pl WHERE lang_id = pl.id AND product_id = :product_id AND pspl.name != '' ORDER BY o ASC LIMIT 1", [':product_id' => $product_id]) -> fetchAll();

    return $results[0]['name'];
  }

  public static function get_product_images($product_id)
  {
    global $mdb;

    return $mdb -> select('pp_shop_products_images', 'src', ['product_id' => (int) $product_id, 'ORDER' => ['o' => 'ASC', 'id' => 'ASC']]);
  }

  static public function generate_EAN( $number )
  {
    $code = '200' . str_pad($number, 9, '0');
    $weightflag = true;
    $sum = 0;

    for ($i = strlen($code) - 1; $i >= 0; $i--)
    {
      $sum += (int)$code[$i] * ($weightflag?3:1);
      $weightflag = !$weightflag;
    }

    $code .= (10 - ($sum % 10)) % 10;
    return $code;
  }

  static public function generate_google_feed_xml()
  {
    global $mdb, $lang_id;

    $settings = \front\factory\Settings::settings_details(true);

    $domain_prefix = 'https';
    $url = preg_replace('#^(http(s)?://)?w{3}\.#', '$1', $_SERVER['SERVER_NAME']);

    $main_language = \front\factory\Languages::default_language();

    $doc = new \DOMDocument('1.0', 'UTF-8');
    $xmlRoot = $doc -> createElement('rss');
    $xmlRoot = $doc -> appendChild($xmlRoot);
    $xmlRoot -> setAttribute('version', '2.0');
    $xmlRoot -> setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:g', 'http://base.google.com/ns/1.0');
    $channelNode = $xmlRoot -> appendChild($doc -> createElement('channel'));
    $channelNode -> appendChild( $doc -> createElement( 'title', $settings['firm_name']));
    $channelNode -> appendChild( $doc -> createElement( 'link', $domain_prefix . '://' . $url ) );

    $rows = $mdb -> select( 'pp_shop_products', 'id', [ 'AND' => [ 'status' => '1', 'archive' => 0, 'parent_id' => null ] ] );
    if ( \S::is_array_fix( $rows ) ) foreach ( $rows as $product_id )
    {
      $product = \shop\Product::getFromCache( $product_id, $lang_id );

      if ( is_array( $product -> product_combinations ) and count( $product -> product_combinations ) )
      {
        foreach ( $product -> product_combinations as $product_combination )
        {
          if ( $product_combination -> quantity !== null or $product_combination -> stock_0_buy )
          {
            $itemNode = $channelNode -> appendChild( $doc -> createElement( 'item' ) );
            $p_gid = $itemNode -> appendChild( $doc -> createElement('g:id', $product_combination -> id ) );
            $p_groupid = $itemNode -> appendChild( $doc -> createElement( 'g:item_group_id', $product -> id ) );

            if ( $product -> custom_label_0 )
              $p_label = $itemNode -> appendChild( $doc -> createElement('g:custom_label_0', $product -> custom_label_0 ) );

            if ( $product -> custom_label_1 )
              $p_label = $itemNode -> appendChild( $doc -> createElement('g:custom_label_1', $product -> custom_label_1 ) );

            if ( $product -> custom_label_2 )
              $p_label = $itemNode -> appendChild( $doc -> createElement('g:custom_label_2', $product -> custom_label_2 ) );

            if ( $product -> custom_label_3 )
              $p_label = $itemNode -> appendChild( $doc -> createElement('g:custom_label_3', $product -> custom_label_3 ) );

            if ( $product -> custom_label_4 )
              $p_label = $itemNode -> appendChild( $doc -> createElement('g:custom_label_4', $product -> custom_label_4 ) );

            if ( $product -> language['xml_name'] )
              $p_title = $itemNode -> appendChild( $doc -> createElement( 'title', str_replace( '&', '&amp;', $product -> language['xml_name'] ) . ' - ' . $product -> generateSubtitleFromAttributes( $product_combination -> permutation_hash ) ) );
            else
              $p_title = $itemNode -> appendChild( $doc -> createElement( 'title', str_replace( '&', '&amp;', $product -> language['name'] ) . ' - ' . $product -> generateSubtitleFromAttributes( $product_combination -> permutation_hash ) ) );

            if ( $product -> ean )
              $p_gtin = $itemNode -> appendChild( $doc -> createElement( 'g:gtin', $product -> ean ) );
            else
              $p_gtin = $itemNode -> appendChild( $doc -> createElement( 'g:gtin', self::generate_EAN( $product -> id ) ) );

            // opis produktu
            if ( $product -> language['short_description'] )
              $p_description = $itemNode -> appendChild( $doc -> createElement( 'g:description', html_entity_decode( strip_tags( $product -> language['short_description'] ) ) ) );
            else
              $p_description = $itemNode -> appendChild( $doc -> createElement( 'g:description', html_entity_decode( strip_tags( $product -> language['name'] ) ) ) );

            if ( $product -> language['seo_link'] )
              $link = $domain_prefix . '://' . $url . '/' . \S::seo( $product -> language['seo_link']  ) . '/' . str_replace( '|', '/', $product_combination -> permutation_hash );
            else
              $link = $domain_prefix . '://' . $url . '/' . 'p-' . $product -> id . '-' . \S::seo( $product -> language['name'] ) . '/' . str_replace( '|', '/', $product_combination -> permutation_hash );

            $p_link = $itemNode -> appendChild( $doc -> createElement( 'link', $link ) );

            if ( $product -> images[0] )
              $p_gimage_link = $itemNode -> appendChild( $doc -> createElement( 'g:image_link', $domain_prefix . '://' . $url . $product -> images[0]['src'] ) );

            if (  count( $product -> images ) > 1 )
            {
              for ( $i = 1; $i < count( $product -> images ); ++$i )
                $p_gimage_link = $itemNode -> appendChild( $doc -> createElement( 'g:additional_image_link', $domain_prefix . '://' . $url . $product -> images[$i]['src'] ) );
            }

            $p_gcondition = $itemNode -> appendChild( $doc -> createElement( 'g:condition', 'new' ) );

            if ( $product_combination -> quantity !== null )
            {
              if ( $product_combination -> quantity > 0 )
              {
                $p_gavailability = $itemNode -> appendChild( $doc -> createElement( 'g:availability', 'in stock' ) );
                $p_gquantity = $itemNode -> appendChild( $doc -> createElement( 'g:quantity', $product_combination -> quantity ) );
              }
              else
              {
                if ( $product_combination -> stock_0_buy )
                  $p_availability = $itemNode -> appendChild( $doc -> createElement( 'g:availability', 'in stock' ) );
                else
                  $p_availability = $itemNode -> appendChild( $doc -> createElement( 'g:availability', 'out of stock' ) );
              }
            }
            else
            {
              if ( $product -> quantity > 0 )
              {
                $p_gavailability = $itemNode -> appendChild( $doc -> createElement( 'g:availability', 'in stock' ) );
                $p_gquantity = $itemNode -> appendChild( $doc -> createElement( 'g:quantity', $product -> quantity ) );
              }
              else
              {
                if ( $product -> stock_0_buy )
                {
                  $p_availability = $itemNode -> appendChild( $doc -> createElement( 'g:availability', 'in stock' ) );
                  $p_gquantity = $itemNode -> appendChild( $doc -> createElement( 'g:quantity', 999 ) );
                }
                else
                {
                  $p_availability = $itemNode -> appendChild( $doc -> createElement( 'g:availability', 'out of stock' ) );
                  $p_gquantity = $itemNode -> appendChild( $doc -> createElement( 'g:quantity', 0 ) );
                }
              }
            }

            if ( $product_combination -> price_brutto )
            {
              $p_gprice = $itemNode -> appendChild( $doc -> createElement( 'g:price', $product_combination -> price_brutto . ' PLN' ) );

              if ( $product_combination -> price_brutto_promo )
                $p_gsale_price = $itemNode -> appendChild( $doc -> createElement( 'g:sale_price', $product_combination -> price_brutto_promo . ' PLN' ) );
            }
            else
            {
              $p_gprice = $itemNode -> appendChild( $doc -> createElement( 'g:price', $product -> price_brutto . ' PLN' ) );

              if ( $product -> price_brutto_promo )
                $p_gsale_price = $itemNode -> appendChild( $doc -> createElement( 'g:sale_price', $product -> price_brutto_promo . ' PLN' ) );
            }

            $p_gshipping = $itemNode -> appendChild( $doc -> createElement( 'g:shipping' ) );
            $p_gcountry = $p_gshipping -> appendChild( $doc -> createElement( 'g:country', 'PL' ) );
            $p_gservice = $p_gshipping -> appendChild( $doc -> createElement( 'g:service', '1 dzień roboczy' ) );
            $p_gprice = $p_gshipping -> appendChild( $doc -> createElement( 'g:price', ( new \Domain\Transport\TransportRepository( $mdb ) )->lowestTransportPrice( (int) $product -> wp ) . ' PLN' ) );
          }
        }
      }
      else
      {
        $itemNode = $channelNode -> appendChild( $doc -> createElement( 'item' ) );
        $p_gid = $itemNode -> appendChild( $doc -> createElement('g:id', $product -> id ) );
        $p_groupid = $itemNode -> appendChild( $doc -> createElement( 'g:item_group_id', $product -> id ) );

        if ( $product -> google_xml_label )
          $p_label = $itemNode -> appendChild($doc -> createElement('g:custom_label_0', $product -> google_xml_label ) );

        if ( $product -> language['xml_name'] )
          $p_title = $itemNode -> appendChild( $doc -> createElement( 'title', str_replace( '&', '&amp;', $product -> language['xml_name'] ) ) );
        else
          $p_title = $itemNode -> appendChild( $doc -> createElement( 'title', str_replace( '&', '&amp;', $product -> language['name'] ) ) );

        if ( $product -> ean )
          $p_gtin = $itemNode -> appendChild( $doc -> createElement( 'g:gtin', $product -> ean ) );
        else
          $p_gtin = $itemNode -> appendChild( $doc -> createElement( 'g:gtin', self::generate_EAN( $product -> id ) ) );

        // opis produktu
        if ( $product -> language['short_description'] )
          $p_description = $itemNode -> appendChild( $doc -> createElement( 'g:description', html_entity_decode( strip_tags( $product -> language['short_description'] ) ) ) );
        else
          $p_description = $itemNode -> appendChild( $doc -> createElement( 'g:description', html_entity_decode( strip_tags( $product -> language['name'] ) ) ) );

        if ( $product -> language['seo_link'] )
          $link = $domain_prefix . '://' . $url . '/' . \S::seo( $product -> language['seo_link'] );
        else
          $link = $domain_prefix . '://' . $url . '/' . 'p-' . $product -> id . '-' . \S::seo( $product -> language['name'] );

        $p_link = $itemNode -> appendChild( $doc -> createElement( 'link', $link ) );

        if ( $product -> custom_label_0 )
          $p_label = $itemNode -> appendChild( $doc -> createElement('g:custom_label_0', $product -> custom_label_0 ) );

        if ( $product -> custom_label_1 )
          $p_label = $itemNode -> appendChild( $doc -> createElement('g:custom_label_1', $product -> custom_label_1 ) );

        if ( $product -> custom_label_2 )
          $p_label = $itemNode -> appendChild( $doc -> createElement('g:custom_label_2', $product -> custom_label_2 ) );

        if ( $product -> custom_label_3 )
          $p_label = $itemNode -> appendChild( $doc -> createElement('g:custom_label_3', $product -> custom_label_3 ) );

        if ( $product -> custom_label_4 )
          $p_label = $itemNode -> appendChild( $doc -> createElement('g:custom_label_4', $product -> custom_label_4 ) );

        if ( $product -> images[0] )
          $p_gimage_link = $itemNode -> appendChild( $doc -> createElement( 'g:image_link', $domain_prefix . '://' . $url . $product -> images[0]['src'] ) );

        if (  count( $product -> images ) > 1 )
        {
          for ( $i = 1; $i < count( $product -> images ); ++$i )
            $p_gimage_link = $itemNode -> appendChild( $doc -> createElement( 'g:additional_image_link', $domain_prefix . '://' . $url . $product -> images[$i]['src'] ) );
        }

        $p_gcondition = $itemNode -> appendChild( $doc -> createElement( 'g:condition', 'new' ) );

        if ( $product -> quantity )
        {
          $p_gavailability = $itemNode -> appendChild( $doc -> createElement( 'g:availability', 'in stock' ) );
          $p_gquantity = $itemNode -> appendChild( $doc -> createElement( 'g:quantity', $product -> quantity ) );
        }
        else
        {
          if ( $product -> stock_0_buy ) {
            $p_availability = $itemNode -> appendChild( $doc -> createElement( 'g:availability', 'in stock' ) );
            $p_gquantity = $itemNode -> appendChild( $doc -> createElement( 'g:quantity', 999 ) );
          }
          else {
            $p_availability = $itemNode -> appendChild( $doc -> createElement( 'g:availability', 'out of stock' ) );
            $p_gquantity = $itemNode -> appendChild( $doc -> createElement( 'g:quantity', 0 ) );
          }
        }

        $p_gprice = $itemNode -> appendChild( $doc -> createElement( 'g:price', $product -> price_brutto . ' PLN' ) );

        if ( $product -> price_brutto_promo )
          $p_gsale_price = $itemNode -> appendChild( $doc -> createElement( 'g:sale_price', $product -> price_brutto_promo . ' PLN' ) );

        $p_gshipping = $itemNode -> appendChild( $doc -> createElement( 'g:shipping' ) );
        $p_gcountry = $p_gshipping -> appendChild( $doc -> createElement( 'g:country', 'PL' ) );
        $p_gservice = $p_gshipping -> appendChild( $doc -> createElement( 'g:service', '1 dzień roboczy' ) );
        $p_gprice = $p_gshipping -> appendChild( $doc -> createElement( 'g:price', ( new \Domain\Transport\TransportRepository( $mdb ) )->lowestTransportPrice( (int) $product -> wp ) . ' PLN' ) );
      }
    }
    file_put_contents('../google-feed.xml', $doc -> saveXML());
  }

  static public function count_product_combinations( int $product_id )
  {
    global $mdb;

    return $mdb -> count( 'pp_shop_products', [ 'parent_id' => $product_id ] );
  }

  // ajax_products_list
  static public function ajax_products_list_archive( $current_page = null, $query = null )
  {
    global $mdb;

    $search = '';

    if ( $query )
    {
      $query_array = [];
      parse_str( $query, $query_array );

      foreach ( $query_array as $key => $val ) {
        if ( $val !== '' )
          $search .= ' AND ' . $key . ' LIKE \'%' . $val . '%\'';
      }
    }

    $results = $mdb -> query( 'SELECT '
                                . 'DISTINCT( psp.id )'
                              . 'FROM '
                                . 'pp_shop_products AS psp '
                              . 'INNER JOIN pp_shop_products_langs AS pspl ON pspl.product_id = psp.id '
                              . 'WHERE archive = 1 AND parent_id IS NULL ' . $search . ' ORDER BY id DESC LIMIT ' . ( $current_page - 1 ) * 10 . ', 10' ) -> fetchAll( \PDO::FETCH_ASSOC );
    $results2 = $mdb -> query( 'SELECT '
                                . 'COUNT( DISTINCT( psp.id ) ) AS products_count '
                              . 'FROM '
                                . 'pp_shop_products AS psp '
                              . 'INNER JOIN pp_shop_products_langs AS pspl ON pspl.product_id = psp.id '
                              . 'WHERE archive = 1 AND parent_id IS NULL ' . $search ) -> fetchAll( \PDO::FETCH_ASSOC );

    if ( is_array( $results ) ) foreach ( $results as $row ) {
      $products[] = \admin\factory\ShopProduct::product_details( $row['id'] );
    }

    return [ 'products' => $products, 'products_count' => $results2[0]['products_count'] ];
  }

  // ajax_products_list
  static public function ajax_products_list( $current_page = null, $query = null )
  {
    global $mdb;

    if ( $query )
    {
      $search = '';
      $query_array = [];

      parse_str( $query, $query_array );

      foreach ( $query_array as $key => $val ) {
        if ( strpos( $key, '|' ) !== false )
        {
          $keys_tmp = explode( '|', $key );
          $search .= ' AND ( ';
          foreach ( $keys_tmp as $key_tmp )
          {
            if ( $key_tmp != reset( $keys_tmp ) )
              $search .= ' OR ' . $key_tmp . ' LIKE \'%' . $val . '%\'';
            else
              $search .= ' ' . $key_tmp . ' LIKE \'%' . $val . '%\'';
          }
          $search .= ' )';
        }
        else
        {
          $search .= ' AND ' . $key . ' LIKE \'%' . $val . '%\'';
        }
      }

      $results = $mdb -> query( 'SELECT '
                                  . 'DISTINCT( psp.id )'
                                . 'FROM '
                                  . 'pp_shop_products AS psp '
                                . 'INNER JOIN pp_shop_products_langs AS pspl ON pspl.product_id = psp.id '
                                . 'WHERE archive = 0 AND parent_id IS NULL ' . $search . ' ORDER BY id DESC LIMIT ' . ( $current_page - 1 ) * 10 . ', 10' ) -> fetchAll( \PDO::FETCH_ASSOC );
      $results2 = $mdb -> query( 'SELECT '
                                  . 'COUNT( DISTINCT( psp.id ) ) AS products_count '
                                . 'FROM '
                                  . 'pp_shop_products AS psp '
                                . 'INNER JOIN pp_shop_products_langs AS pspl ON pspl.product_id = psp.id '
                                . 'WHERE archive = 0 AND parent_id IS NULL ' . $search ) -> fetchAll( \PDO::FETCH_ASSOC );
    } else {
      $results = $mdb -> query( 'SELECT id FROM pp_shop_products WHERE parent_id IS NULL ORDER BY id DESC LIMIT ' . ( $current_page - 1 ) * 10 . ', 10' ) -> fetchAll( \PDO::FETCH_ASSOC );
      $results2 = $mdb -> query( 'SELECT COUNT( id ) AS products_count FROM pp_shop_products WHERE parent_id IS NULL' ) -> fetchAll( \PDO::FETCH_ASSOC );
    }

    if ( is_array( $results ) ) foreach ( $results as $row ) {
      $products[] = \admin\factory\ShopProduct::product_details( $row['id'] );
    }

    return [ 'products' => $products, 'products_count' => $results2[0]['products_count'] ];
  }

  public static function products_list()
  {
    global $mdb;

    $results = $mdb -> select( 'pp_shop_products', 'id', [ 'parent_id' => null ] );
    if ( is_array( $results ) ) foreach ( $results as $row )
    {
      $products[ $row ] = $mdb -> get ('pp_shop_products_langs', 'name', ['AND' => [ 'product_id' => $row, 'lang_id' => 'pl' ] ] );
    }

    return $products;
  }

  public static function images_order_save($product_id, $order)
  {
    global $mdb;

    $order = explode(';', $order);
    if (\is_array($order) && !empty($order))
    {
      foreach ($order as $image_id)
      {
        $mdb -> update('pp_shop_products_images', [
          'o' => $i++,
        ], [
          'AND' => [
            'product_id' => $product_id,
            'id' => $image_id,
          ],
              ]);
      }
    }

    return true;
  }

  public static function image_alt_change($image_id, $image_alt)
  {
    global $mdb;
    $result = $mdb -> update('pp_shop_products_images', [
      'alt' => $image_alt,
    ], [
      'id' => $image_id,
    ]);
    \S::delete_cache();

    return $result;
  }

  // product_unarchive
  static public function product_unarchive( int $product_id )
  {
    global $mdb;

    $mdb -> update( 'pp_shop_products', [ 'status' => 1, 'archive' => 0 ], [ 'id' => $product_id ] );
    $mdb -> update( 'pp_shop_products', [ 'status' => 1, 'archive' => 0 ], [ 'parent_id' => $product_id ] );

    return true;
  }

  static public function product_archive( int $product_id )
  {
    global $mdb;

    $mdb -> update( 'pp_shop_products', [ 'status' => 0, 'archive' => 1 ], [ 'id' => $product_id ] );
    $mdb -> update( 'pp_shop_products', [ 'status' => 0, 'archive' => 1 ], [ 'parent_id' => $product_id ] );

    return true;
  }

  public static function product_delete( int $product_id)
  {
    global $mdb;

    $mdb -> delete( 'pp_shop_products_categories', ['product_id' => $product_id ] );
    $mdb -> delete( 'pp_shop_products_langs', ['product_id' => $product_id ] );
    $mdb -> delete( 'pp_shop_products_images', ['product_id' => $product_id ] );
    $mdb -> delete( 'pp_shop_products_files', ['product_id' => $product_id ] );
    $mdb -> delete( 'pp_shop_products_attributes', ['product_id' => $product_id ] );
    $mdb -> delete( 'pp_shop_products', ['id' => $product_id ] );
    $mdb -> delete( 'pp_shop_product_sets_products', [ 'product_id' => $product_id ] );
    // pp_routes
    $mdb -> delete( 'pp_routes', [ 'product_id' => $product_id ] );
    // pp_redirects
    $mdb -> delete( 'pp_redirects', [ 'product_id' => $product_id ] );

    \S::delete_dir( '../upload/product_images/product_' . $product_id . '/' );
    \S::delete_dir( '../upload/product_files/product_' . $product_id . '/' );

    return true;
  }

  public static function product_categories($product_id)
  {
    global $mdb;

    $results = $mdb -> query('SELECT category_id FROM pp_shop_products_categories WHERE product_id = '.(int) $product_id) -> fetchAll();
    if (\is_array($results) && !empty($results))
    {
      foreach ($results as $row)
      {
        if ('' === $out)
        {
          $out .= ' - ';
        }

        $out .= ( new \Domain\Category\CategoryRepository( $mdb ) )->categoryTitle( (int) $row['category_id'] );

        if (end($results) !== $row)
        {
          $out .= ' / ';
        }
      }
    }

    return $out;
  }

  public static function max_order()
  {
    global $mdb;

    return $mdb -> max('pp_shop_products_categories', 'o');
  }

  public static function delete_img($image_id)
  {
    global $mdb;
    $mdb -> update('pp_shop_products_images', ['to_delete' => 1], ['id' => (int) $image_id]);

    return true;
  }

  public static function delete_nonassigned_images()
  {
    global $mdb;

    $results = $mdb -> select('pp_shop_products_images', '*', ['product_id' => null]);
    if (\is_array($results))
    {
      foreach ($results as $row)
      {
        if (file_exists('../'.$row['src']))
        {
          unlink('../'.$row['src']);
        }
      }
    }

    $mdb -> delete('pp_shop_products_images', ['product_id' => null]);
  }

  public static function file_name_change($file_id, $file_name)
  {
    global $mdb;
    $mdb -> update('pp_shop_products_files', ['name' => $file_name], ['id' => (int) $file_id]);

    return true;
  }

  public static function delete_file($file_id)
  {
    global $mdb;
    $mdb -> update('pp_shop_products_files', ['to_delete' => 1], ['id' => (int) $file_id]);

    return true;
  }

  public static function delete_nonassigned_files()
  {
    global $mdb;

    $results = $mdb -> select('pp_shop_products_files', '*', ['product_id' => null]);
    if (\is_array($results))
    {
      foreach ($results as $row)
      {
        if (file_exists('../'.$row['src']))
        {
          unlink('../'.$row['src']);
        }
      }
    }

    $mdb -> delete('pp_shop_products_files', ['product_id' => null]);
  }

  public static function save(
      $product_id, $name, $short_description, $description, $status, $meta_description, $meta_keywords, $seo_link, $copy_from, $categories, $price_netto, $price_brutto, $vat, $promoted, $warehouse_message_zero, $warehouse_message_nonzero, $tab_name_1, $tab_description_1, $tab_name_2, $tab_description_2, $layout_id, $products_related, int $set_id, $price_netto_promo, $price_brutto_promo, $new_to_date, $stock_0_buy, $wp, $custom_label_0, $custom_label_1, $custom_label_2, $custom_label_3, $custom_label_4, $additional_message, int $quantity, $additional_message_text, int $additional_message_required, $canonical, $meta_title, $producer_id, $sku, $ean, $product_unit, $weight, $xml_name, $custom_field_name, $custom_field_required, $security_information, $custom_field_type
    )
  {
    global $mdb, $user;

    if ( !$product_id )
    {
      $mdb -> insert('pp_shop_products', [
        'date_add' => date('Y-m-d H:i:s'),
        'date_modify' => date('Y-m-d H:i:s'),
        'modify_by' => $user['id'],
        'status' => 'on' === $status ? 1 : 0,
        'price_netto' => ($price_netto && 0.00 !== $price_netto) ? $price_netto : null,
        'price_brutto' => ($price_brutto && 0.00 !== $price_brutto) ? $price_brutto : null,
        'vat' => $vat,
        'promoted' => 'on' === $promoted ? 1 : 0,
        'layout_id' => $layout_id ? $layout_id : null,
        'price_netto_promo' => ($price_netto_promo && 0.00 !== $price_netto_promo) ? $price_netto_promo : null,
        'price_brutto_promo' => ($price_brutto_promo && 0.00 !== $price_brutto_promo) ? $price_brutto_promo : null,
        'new_to_date' => $new_to_date ? $new_to_date : null,
        'stock_0_buy' => 'on' === $stock_0_buy ? 1 : 0,
        'wp' => $wp ? $wp : null,
        'sku' => $sku ? $sku : null,
        'ean' => $ean ? $ean : null,
        'custom_label_0' => $custom_label_0 ? $custom_label_0 : null,
        'custom_label_1' => $custom_label_1 ? $custom_label_1 : null,
        'custom_label_2' => $custom_label_2 ? $custom_label_2 : null,
        'custom_label_3' => $custom_label_3 ? $custom_label_3 : null,
        'custom_label_4' => $custom_label_4 ? $custom_label_4 : null,
        'additional_message' => $additional_message == 'on' ? 1 : 0,
        'set_id' => $set_id ? $set_id : null,
        'quantity' => $quantity,
        'additional_message_text' => $additional_message_text ? $additional_message_text : null,
        'additional_message_required' => $additional_message_required,
        'producer_id' => !empty( $producer_id ) ? $producer_id : null,
        'product_unit_id' => !empty( $product_unit ) ? $product_unit : null,
        'weight' => !empty( $weight ) ? $weight : null,
      ] );

      $id = $mdb -> id();

      if ( $id )
      {
        $langs = ( new \Domain\Languages\LanguagesRepository( $mdb ) )->languagesList( true );
        foreach ( $langs as $lg )
        {
          $mdb -> insert( 'pp_shop_products_langs', [
            'product_id' => (int) $id,
            'lang_id' => $lg['id'],
            'name' => $name[$lg['id']] ? $name[$lg['id']] : null,
            'short_description' => $short_description[$lg['id']] ? $short_description[$lg['id']] : null,
            'description' => $description[$lg['id']] ? $description[$lg['id']] : null,
            'meta_description' => $meta_description[$lg['id']] ? $meta_description[$lg['id']] : null,
            'meta_keywords' => $meta_keywords[$lg['id']] ? $meta_keywords[$lg['id']] : null,
            'seo_link' => $seo_link[$lg['id']] ? \S::seo($seo_link[$lg['id']]) : null,
            'copy_from' => $copy_from[$lg['id']] ? $copy_from[$lg['id']] : null,
            'warehouse_message_zero' => $warehouse_message_zero[$lg['id']] ? $warehouse_message_zero[$lg['id']] : null,
            'warehouse_message_nonzero' => $warehouse_message_nonzero[$lg['id']] ? $warehouse_message_nonzero[$lg['id']] : null,
            'tab_name_1' => $tab_name_1[$lg['id']] ? $tab_name_1[$lg['id']] : null,
            'tab_description_1' => $tab_description_1[$lg['id']] ? $tab_description_1[$lg['id']] : null,
            'tab_name_2' => $tab_name_2[$lg['id']] ? $tab_name_2[$lg['id']] : null,
            'tab_description_2' => $tab_description_2[$lg['id']] ? $tab_description_2[$lg['id']] : null,
            'canonical' => $canonical[$lg['id']] ? $canonical[$lg['id']] : null,
            'meta_title' => $meta_title[$lg['id']] ? $meta_title[$lg['id']] : null,
            'xml_name' => $xml_name[$lg['id']] ? $xml_name[$lg['id']] : null,
            'security_information' => $security_information[$lg['id']] ? $security_information[$lg['id']] : null,
          ] );
        }

        if ( is_array($categories))
        {
          foreach ($categories as $category)
          {
            $order = self::max_order() + 1;

            $mdb -> insert('pp_shop_products_categories', [
              'product_id' => (int) $id,
              'category_id' => (int) $category,
              'o' => (int) $order,
            ]);
          }
        }
        elseif ($categories)
        {
          $order = self::max_order() + 1;

          $mdb -> insert('pp_shop_products_categories', [
            'product_id' => (int) $id,
            'category_id' => (int) $categories,
            'o' => (int) $order,
          ]);
        }

        if (\is_array($products_related))
        {
          foreach ($products_related as $product_related )
          {
            $mdb -> insert('pp_shop_products_related', [
              'product_id' => (int) $id,
              'product_related_id' => (int) $product_related,
            ]);
          }
        }
        elseif ( $products_related )
        {
          $mdb -> insert('pp_shop_products_related', [
            'product_id' => (int) $id,
            'product_related_id' => (int) $products_related,
          ]);
        }

        $created = false;

        $results = $mdb -> select('pp_shop_products_files', '*', ['product_id' => null]);
        if (\is_array($results))
        {
          foreach ($results as $row)
          {
            $dir = '/upload/product_files/product_'.$id;

            $new_file_name = str_replace('/upload/product_files/tmp', $dir, $row['src']);

            if (file_exists('..'.$row['src']))
            {
              if (!is_dir('../'.$dir) && true !== $created)
              {
                if (mkdir('../'.$dir, 0755, true))
                {
                  $created = true;
                }
              }
              rename('..'.$row['src'], '..'.$new_file_name);
            }

            $mdb -> update('pp_shop_products_files', ['src' => $new_file_name, 'product_id' => $id], ['id' => $row['id']]);
          }
        }

        $created = false;

        $results = $mdb -> select('pp_shop_products_images', '*', ['product_id' => null]);
        if (\is_array($results))
        {
          foreach ($results as $row)
          {
            $dir = '/upload/product_images/product_'.$id;

            $new_file_name = str_replace('/upload/product_images/tmp', $dir, $row['src']);

            if (file_exists('../'.$new_file_name))
            {
              $ext = strrpos($new_file_name, '.');
              $fileName_a = substr($new_file_name, 0, $ext);
              $fileName_b = substr($new_file_name, $ext);

              $count = 1;

              while (file_exists('../'.$fileName_a.'_'.$count.$fileName_b))
              {
                ++$count;
              }

              $new_file_name = $fileName_a.'_'.$count.$fileName_b;
            }

            if (file_exists('..'.$row['src']))
            {
              if (!is_dir('../'.$dir) && true !== $created)
              {
                if (mkdir('../'.$dir, 0755, true))
                {
                  $created = true;
                }
              }
              rename('..'.$row['src'], '..'.$new_file_name);
            }

            $mdb -> update('pp_shop_products_images', ['src' => $new_file_name, 'product_id' => (int) $id], ['id' => $row['id']]);
          }
        }

        // dodatkowe pola
        for ( $i = 0; $i < count( $custom_field_name ); ++$i )
        {
          if ( !empty( $custom_field_name[$i] ) )
          {
            $custom_field = $custom_field_name[$i];
            $custom_field_type_data = $custom_field_type[$i];
            $custom_field_required = isset( $custom_field_required[$i] ) ? 1 : 0;

            $mdb -> insert( 'pp_shop_products_custom_fields', [
              'id_product' => (int) $id,
              'name' => $custom_field,
              'type' => $custom_field_type_data,
              'is_required' => $custom_field_required,
            ] );
          }
        }

        \S::htacces();

        \S::delete_dir('../temp/');
        \S::delete_dir('../thumbs/');

        return $id;
      }
    }
    else
    {
      $mdb -> update( 'pp_shop_products', [
        'date_modify' => date('Y-m-d H:i:s'),
        'modify_by' => $user['id'],
        'status' => 'on' === $status ? 1 : 0,
        'price_netto' => ($price_netto && 0.00 !== $price_netto) ? $price_netto : null,
        'price_brutto' => ($price_brutto && 0.00 !== $price_brutto) ? $price_brutto : null,
        'vat' => $vat,
        'promoted' => 'on' === $promoted ? 1 : 0,
        'layout_id' => $layout_id ? $layout_id : null,
        'price_netto_promo' => ($price_netto_promo && 0.00 !== $price_netto_promo) ? $price_netto_promo : null,
        'price_brutto_promo' => ($price_brutto_promo && 0.00 !== $price_brutto_promo) ? $price_brutto_promo : null,
        'new_to_date' => $new_to_date ? $new_to_date : null,
        'stock_0_buy' => 'on' === $stock_0_buy ? 1 : 0,
        'wp' => $wp ? $wp : null,
        'sku' => $sku ? $sku : null,
        'ean' => $ean ? $ean : null,
        'custom_label_0' => $custom_label_0 ? $custom_label_0 : null,
        'custom_label_1' => $custom_label_1 ? $custom_label_1 : null,
        'custom_label_2' => $custom_label_2 ? $custom_label_2 : null,
        'custom_label_3' => $custom_label_3 ? $custom_label_3 : null,
        'custom_label_4' => $custom_label_4 ? $custom_label_4 : null,
        'additional_message' => $additional_message == 'on' ? 1 : 0,
        'set_id' => $set_id ? $set_id : null,
        'quantity' => $quantity,
        'additional_message_text' => $additional_message_text ? $additional_message_text : null,
        'additional_message_required' => $additional_message_required,
        'producer_id' => !empty( $producer_id ) ? $producer_id : null,
        'product_unit_id' => !empty( $product_unit ) ? $product_unit : null,
        'weight' => !empty( $weight ) ? $weight : null,
      ], [
        'id' => (int) $product_id,
      ] );

      $mdb -> update( 'pp_shop_products', [
        'additional_message' => $additional_message == 'on' ? 1 : 0,
      ], [
        'parent_id' => (int) $product_id,
      ] );

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

      $langs = ( new \Domain\Languages\LanguagesRepository( $mdb ) )->languagesList( true );
      foreach ( $langs as $lg )
      {
        if ( $translation_id = $mdb -> get( 'pp_shop_products_langs', 'id', [ 'AND' => [ 'product_id' => $product_id, 'lang_id' => $lg['id'] ] ] ) )
        {
          $current_seo_link = $mdb -> get( 'pp_shop_products_langs', 'seo_link', [ 'id' => $translation_id ] );

          if ( $seo_link[$lg['id']] )
            $new_seo_link = \S::seo( $seo_link[$lg['id']] );
          else
            $new_seo_link = \S::seo( 'p-' . $product_id . '-' . $name[$lg['id']] );

          if ( $new_seo_link !== $current_seo_link and $current_seo_link != '' )
          {
            if ( $mdb -> count( 'pp_redirects', [ 'from' => $new_seo_link, 'to' => $current_seo_link, 'lang_id' => $lg['id'], 'product_id' => $product_id ] ) )
              $mdb -> delete( 'pp_redirects', [ 'from' => $new_seo_link, 'to' => $current_seo_link, 'lang_id' => $lg['id'], 'product_id' => $product_id ] );

            $mdb -> delete( 'pp_redirects', [
              'AND' => [
                'product_id' => $product_id,
                'lang_id' => $lg['id'],
                'from' => $current_seo_link,
                'to[!]' => $new_seo_link,
              ],
            ] );

            if ( !self::seoLinkUsedByOtherProduct( (int) $product_id, (string) $lg['id'], (string) $current_seo_link ) )
            {
              self::removeConflictingRedirectSources( (int) $product_id, (string) $lg['id'], (string) $current_seo_link );

              if ( !$mdb -> count( 'pp_redirects', [ 'from' => $current_seo_link, 'to' => $new_seo_link, 'lang_id' => $lg['id'], 'product_id' => $product_id ] ) )
              {
                if ( \S::canAddRedirect( $current_seo_link, $new_seo_link, $lg['id'] ) )
                  $mdb -> insert( 'pp_redirects', [ 'from' => $current_seo_link, 'to' => $new_seo_link, 'lang_id' => $lg['id'], 'product_id' => $product_id ] );
              }
            }
            else
              $mdb -> delete( 'pp_redirects', [ 'AND' => [ 'product_id' => $product_id, 'lang_id' => $lg['id'], 'from' => $current_seo_link ] ] );
          }

          $mdb -> update( 'pp_shop_products_langs', [
            'name' => $name[$lg['id']] ? $name[$lg['id']] : null,
            'short_description' => $short_description[$lg['id']] ? $short_description[$lg['id']] : null,
            'description' => $description[$lg['id']] ? $description[$lg['id']] : null,
            'meta_description' => $meta_description[$lg['id']] ? $meta_description[$lg['id']] : null,
            'meta_keywords' => $meta_keywords[$lg['id']] ? $meta_keywords[$lg['id']] : null,
            'seo_link' => $seo_link[$lg['id']] ? \S::seo($seo_link[$lg['id']]) : null,
            'copy_from' => $copy_from[$lg['id']] ? $copy_from[$lg['id']] : null,
            'warehouse_message_zero' => $warehouse_message_zero[$lg['id']] ? $warehouse_message_zero[$lg['id']] : null,
            'warehouse_message_nonzero' => $warehouse_message_nonzero[$lg['id']] ? $warehouse_message_nonzero[$lg['id']] : null,
            'tab_name_1' => $tab_name_1[$lg['id']] ? $tab_name_1[$lg['id']] : null,
            'tab_description_1' => $tab_description_1[$lg['id']] ? $tab_description_1[$lg['id']] : null,
            'tab_name_2' => $tab_name_2[$lg['id']] ? $tab_name_2[$lg['id']] : null,
            'tab_description_2' => $tab_description_2[$lg['id']] ? $tab_description_2[$lg['id']] : null,
            'canonical' => $canonical[$lg['id']] ? $canonical[$lg['id']] : null,
            'meta_title' => $meta_title[$lg['id']] ? $meta_title[$lg['id']] : null,
            'xml_name' => $xml_name[$lg['id']] ? $xml_name[$lg['id']] : null,
            'security_information' => $security_information[$lg['id']] ? $security_information[$lg['id']] : null,
          ], [
            'id' => $translation_id
          ] );
        }
        else
        {
          $mdb -> insert( 'pp_shop_products_langs', [
            'product_id' => (int) $product_id,
            'lang_id' => $lg['id'],
            'name' => $name[$lg['id']] ? $name[$lg['id']] : null,
            'short_description' => $short_description[$lg['id']] ? $short_description[$lg['id']] : null,
            'description' => $description[$lg['id']] ? $description[$lg['id']] : null,
            'meta_description' => $meta_description[$lg['id']] ? $meta_description[$lg['id']] : null,
            'meta_keywords' => $meta_keywords[$lg['id']] ? $meta_keywords[$lg['id']] : null,
            'seo_link' => $seo_link[$lg['id']] ? \S::seo($seo_link[$lg['id']]) : null,
            'copy_from' => $copy_from[$lg['id']] ? $copy_from[$lg['id']] : null,
            'warehouse_message_zero' => $warehouse_message_zero[$lg['id']] ? $warehouse_message_zero[$lg['id']] : null,
            'warehouse_message_nonzero' => $warehouse_message_nonzero[$lg['id']] ? $warehouse_message_nonzero[$lg['id']] : null,
            'tab_name_1' => $tab_name_1[$lg['id']] ? $tab_name_1[$lg['id']] : null,
            'tab_description_1' => $tab_description_1[$lg['id']] ? $tab_description_1[$lg['id']] : null,
            'tab_name_2' => $tab_name_2[$lg['id']] ? $tab_name_2[$lg['id']] : null,
            'tab_description_2' => $tab_description_2[$lg['id']] ? $tab_description_2[$lg['id']] : null,
            'canonical' => $canonical[$lg['id']] ? $canonical[$lg['id']] : null,
            'meta_title' => $meta_title[$lg['id']] ? $meta_title[$lg['id']] : null,
            'xml_name' => $xml_name[$lg['id']] ? $xml_name[$lg['id']] : null,
            'security_information' => $security_information[$lg['id']] ? $security_information[$lg['id']] : null,
          ] );
        }
      }

      $not_in = [0];

      if (\is_array($categories))
      {
        foreach ($categories as $category)
        {
          $not_in[] = $category;
        }
      }
      elseif ($categories)
      {
        $not_in[] = $categories;
      }

      $mdb -> delete('pp_shop_products_categories', ['AND' => ['product_id' => (int) $product_id, 'category_id[!]' => $not_in]]);

      $categories_tmp = $mdb -> select('pp_shop_products_categories', 'category_id', ['product_id' => (int) $product_id]);

      if (!\is_array($categories))
      {
        $categories = [$categories];
      }

      $categories = array_diff($categories, $categories_tmp);

      if (\is_array($categories))
      {
        foreach ($categories as $category)
        {
          $order = self::max_order() + 1;

          if ( $product_id and $category )
            $mdb -> insert( 'pp_shop_products_categories', [
              'product_id' => (int)$product_id,
              'category_id' => (int)$category,
              'o' => (int) $order,
            ] );
        }
      }

      // produkty powiązane
      $not_in = [0];

      if (\is_array($products_related))
      {
        foreach ($products_related as $product_related)
        {
          $not_in[] = $product_related;
        }
      }
      elseif ($products_related)
      {
        $not_in[] = $products_related;
      }

      $mdb -> delete('pp_shop_products_related', ['AND' => ['product_id' => (int) $product_id, 'product_related_id[!]' => $not_in]]);

      $products_related_tmp = $mdb -> select('pp_shop_products_related', 'product_related_id', ['product_id' => (int) $product_id]);

      if (!\is_array($products_related))
      {
        $products_related = [$products_related];
      }

      $products_related = array_diff($products_related, $products_related_tmp);

      if (\is_array($products_related))
      {
        foreach ($products_related as $product_related)
        {
          if ($product_id && $product_related)
          {
            $mdb -> insert('pp_shop_products_related', [
              'product_id' => (int) $product_id,
              'product_related_id' => (int) $product_related,
            ]);
          }
        }
      }

      $created = false;

      $results = $mdb -> select('pp_shop_products_files', '*', ['product_id' => null]);
      if (\is_array($results))
      {
        foreach ($results as $row)
        {
          $dir = '/upload/product_files/product_'.$product_id;

          $new_file_name = str_replace('/upload/product_files/tmp', $dir, $row['src']);

          if (file_exists('..'.$row['src']))
          {
            if (!is_dir('../'.$dir) && true !== $created)
            {
              if (mkdir('../'.$dir, 0755, true))
              {
                $created = true;
              }
            }
            rename('..'.$row['src'], '..'.$new_file_name);
          }

          $mdb -> update('pp_shop_products_files', ['src' => $new_file_name, 'product_id' => (int) $product_id], ['id' => $row['id']]);
        }
      }

      $results = $mdb -> select('pp_shop_products_files', '*', ['AND' => ['product_id' => (int) $product_id, 'to_delete' => 1]]);
      if (\is_array($results))
      {
        foreach ($results as $row)
        {
          if (file_exists('../'.$row['src']))
          {
            unlink('../'.$row['src']);
          }
        }
      }

      $mdb -> delete('pp_shop_products_files', ['AND' => ['product_id' => (int) $product_id, 'to_delete' => 1]]);

      $created = false;

      // zdjęcia
      $results = $mdb -> select('pp_shop_products_images', '*', ['product_id' => null]);
      if (\is_array($results))
      {
        foreach ($results as $row)
        {
          $dir = '/upload/product_images/product_'.$product_id;

          $new_file_name = str_replace('/upload/product_images/tmp', $dir, $row['src']);

          if (file_exists('../'.$new_file_name))
          {
            $ext = strrpos($new_file_name, '.');
            $fileName_a = substr($new_file_name, 0, $ext);
            $fileName_b = substr($new_file_name, $ext);

            $count = 1;

            while (file_exists('../'.$fileName_a.'_'.$count.$fileName_b))
            {
              ++$count;
            }

            $new_file_name = $fileName_a.'_'.$count.$fileName_b;
          }

          if (file_exists('..'.$row['src']))
          {
            if (!is_dir('../'.$dir) && true !== $created)
            {
              if (mkdir('../'.$dir, 0755, true))
              {
                $created = true;
              }
            }
            rename('..'.$row['src'], '..'.$new_file_name);
          }

          $mdb -> update('pp_shop_products_images', ['src' => $new_file_name, 'product_id' => (int) $product_id], ['id' => $row['id']]);
        }
      }

      $results = $mdb -> select('pp_shop_products_images', '*', ['AND' => ['product_id' => (int) $product_id, 'to_delete' => 1]]);
      if (\is_array($results))
      {
        foreach ($results as $row)
        {
          if (file_exists('../'.$row['src']))
          {
            unlink('../'.$row['src']);
          }
        }
      }

      $mdb -> delete('pp_shop_products_images', ['AND' => ['product_id' => (int) $product_id, 'to_delete' => 1]]);

      // dodatkowe pola
      // delete only custom fields that are not in the new list
      foreach ( $custom_field_name as $custom_field )
      {
        if ( !empty( $custom_field ) )
        {
          $exits_custom_ids[] = (int)$mdb -> get( 'pp_shop_products_custom_fields', 'id_additional_field', [ 'AND' => [ 'id_product' => $product_id, 'name' => $custom_field ] ] );
        }
      }

      $mdb -> delete( 'pp_shop_products_custom_fields', [ 'AND' => [ 'id_product' => $product_id, 'id_additional_field[!]' => $exits_custom_ids ] ] );

      // $custom_field_name i $custom_field_required
      foreach ( $custom_field_name as $i => $custom_field )
      {
        if ( !empty( $custom_field ) )
        {
          $custom_field_type_data = $custom_field_type[$i];
          $is_required = !empty( $custom_field_required[$i] ) ? 1 : 0;

          if ( !$mdb -> count( 'pp_shop_products_custom_fields', [ 'AND' => [ 'id_product' => $product_id, 'name' => $custom_field ] ] ) )
          {
            $mdb -> insert( 'pp_shop_products_custom_fields', [
              'id_product' => $product_id,
              'name' => $custom_field,
              'type' => $custom_field_type_data,
              'is_required' => $is_required
            ]);
          }
          else
          {
            $mdb -> update( 'pp_shop_products_custom_fields',
            [
              'type' => $custom_field_type_data,
              'is_required' => $is_required
            ],
            [ 'AND' => [ 'id_product' => $product_id, 'name' => $custom_field ] ]);
          }
        }
      }

      \S::htacces();

      \S::delete_dir( '../temp/' );
      \S::delete_dir( '../thumbs/' );

      $redis = \RedisConnection::getInstance() -> getConnection();
      if ( $redis )
        $redis -> flushAll();

      return $product_id;
    }
  }

  // pobierz prostą listę z ilościami produktu
  static public function get_product_quantity_list( int $product_id )
  {
    global $mdb;

    return $mdb -> get( 'pp_shop_products', 'quantity', [ 'id' => $product_id ] );
  }

  // ADMIN - szczególy produktu
  static public function product_details( int $product_id )
  {
    global $mdb;

    if ( $product = $mdb -> get( 'pp_shop_products', '*', [ 'id' => $product_id ] ) )
    {
      $results = $mdb -> select( 'pp_shop_products_langs', '*', [ 'product_id' => $product_id ] );
      if ( is_array( $results ) ) foreach ($results as $row)
        $product['languages'][ $row['lang_id'] ] = $row;

      $product['images'] = $mdb -> select( 'pp_shop_products_images', '*', [ 'product_id' => $product_id, 'ORDER' => [ 'o' => 'ASC', 'id' => 'ASC' ] ] );
      $product['files'] = $mdb -> select( 'pp_shop_products_files', '*', [ 'product_id' => $product_id ] );
      $product['categories'] = $mdb -> select( 'pp_shop_products_categories', 'category_id', [ 'product_id' => $product_id ] );
      $product['attributes'] = $mdb -> select( 'pp_shop_products_attributes', [ 'attribute_id', 'value_id' ], [ 'product_id' => $product_id ] );
      $product['products_related'] = $mdb -> select( 'pp_shop_products_related', 'product_related_id', [ 'product_id' => $product_id ] );
      $product['custom_fields'] = $mdb -> select( 'pp_shop_products_custom_fields', '*', [ 'id_product' => $product_id ] );
    }

    return $product;
  }

  // duplikowanie produktu w panelu administratora
  static public function duplicate_product( int $product_id, int $with_combinations = 0 )
  {
    global $mdb;

    $product = $mdb -> get( 'pp_shop_products', '*', [ 'id' => $product_id ] );
    if ( $product )
    {
      $mdb -> insert( 'pp_shop_products', [
        'price_netto' => $product['price_netto'],
        'price_brutto' => $product['price_brutto'],
        'price_netto_promo' => $product['price_netto_promo'],
        'price_brutto_promo' => $product['price_brutto_promo'],
        'vat' => $product['vat'],
        'promoted' => $product['promoted'],
        'layout_id' => $product['layout_id'],
        'new_to_date' => $product['new_to_date'],
        'stock_0_buy' => $product['stock_0_buy'],
        'wp' => $product['wp'],
        'custom_label_0' => $product['custom_label_0'],
        'custom_label_1' => $product['custom_label_1'],
        'custom_label_2' => $product['custom_label_2'],
        'custom_label_3' => $product['custom_label_3'],
        'custom_label_4' => $product['custom_label_4'],
        'additional_message' => $product['additional_message']
      ] );

      $new_product_id = $mdb -> id();
      if ( $new_product_id )
      {
        $attributes = $mdb -> select( 'pp_shop_products_attributes', '*', [ 'product_id' => $product_id ] );
        if ( \S::is_array_fix( $attributes ) ) foreach ( $attributes as $row )
        {
          $mdb -> insert( 'pp_shop_products_attributes', [
            'product_id' => $new_product_id,
            'attribute_id' => $row['attribute_id'],
            'value_id' => $row['value_id']
          ] );
        }

        $categories = $mdb -> select( 'pp_shop_products_categories', '*', [ 'product_id' => $product_id ] );
        if ( \S::is_array_fix( $categories ) ) foreach ( $categories as $row )
        {
          $mdb -> insert( 'pp_shop_products_categories', [
            'product_id' => $new_product_id,
            'category_id' => $row['category_id'],
            'o' => $row['o']
          ] );
        }

        $langs = $mdb -> select( 'pp_shop_products_langs', '*', [ 'product_id' => $product_id ] );
        if ( \S::is_array_fix( $langs ) ) foreach ( $langs as $row )
        {
          $mdb -> insert( 'pp_shop_products_langs', [
            'product_id' => $new_product_id,
            'lang_id' => $row['lang_id'],
            'name' => $row['name'] . ' - kopia',
            'short_description' => $row['short_description'],
            'description' => $row['description'],
            'tab_name_1' => $row['tab_name_1'],
            'tab_description_1' => $row['tab_description_1'],
            'tab_name_2' => $row['tab_name_2'],
            'tab_description_2' => $row['tab_description_2'],
            'meta_description' => $row['meta_description'],
            'meta_keywords' => $row['meta_keywords'],
            'copy_from' => $row['copy_from'],
            'warehouse_message_zero' => $row['warehouse_message_zero'],
            'warehouse_message_nonzero' => $row['warehouse_message_nonzero']
          ] );
        }

        // custom fields
        $custom_fields = $mdb -> select( 'pp_shop_products_custom_fields', '*', [ 'id_product' => $product_id ] );
        if ( \S::is_array_fix( $custom_fields ) ) foreach ( $custom_fields as $row )
        {
          $mdb -> insert( 'pp_shop_products_custom_fields', [
            'id_product' => $new_product_id,
            'name' => $row['name']
          ] );
        }
      }

      // duplikowanie kombinacji produktu
      if ( $with_combinations )
      {
        $product_combinations = $mdb -> select( 'pp_shop_products', '*', [ 'parent_id' => $product_id ] );
        foreach ( $product_combinations as $product_combination )
        {
          $mdb -> insert( 'pp_shop_products', [
            'parent_id' => $new_product_id,
            'permutation_hash' => $product_combination['permutation_hash'],
            'price_netto' => $product_combination['price_netto'],
            'price_brutto' => $product_combination['price_brutto'],
            'price_netto_promo' => $product_combination['price_netto_promo'],
            'price_brutto_promo' => $product_combination['price_brutto_promo'],
            'vat' => $product_combination['vat'],
            'stock_0_buy' => $product_combination['stock_0_buy'],
            'quantity' => $product_combination['quantity'],
            'wp' => $product_combination['wp'],
            'additional_message' => $product_combination['additional_message'],
            'additional_message_text' => $product_combination['additional_message_text'],
            'additional_message_required' => $product_combination['additional_message_required']
          ] );

          $combination_id = $mdb -> id();
          if ( $combination_id )
          {
            $pp_shop_products_attributes = $mdb -> select( 'pp_shop_products_attributes', '*', [ 'product_id' => $product_combination['id'] ] );
            foreach ( $pp_shop_products_attributes as $pp_shop_products_attribute )
            {
              $mdb -> insert( 'pp_shop_products_attributes', [
                'product_id' => $combination_id,
                'attribute_id' => $pp_shop_products_attribute['attribute_id'],
                'value_id' => $pp_shop_products_attribute['value_id']
              ] );
            }
          }
        }
      }

      return true;
    }
    return  false;
  }

  //
  // KOMBINACJE PRODUKTU
  //

  static public function product_combination_stock_0_buy_save( int $product_id, $stock_0_buy )
  {
    global $mdb;
    return $mdb -> update( 'pp_shop_products', [ 'stock_0_buy' => $stock_0_buy == 'true' ? 1 : 0 ], [ 'id' => $product_id ] );
  }

  static public function product_combination_sku_save( int $product_id, $sku )
  {
    global $mdb;
    return $mdb -> update( 'pp_shop_products', [ 'sku' => $sku ], [ 'id' => $product_id ] );
  }

  static public function product_combination_quantity_save( int $product_id, $quantity )
  {
    global $mdb;
    return $mdb -> update( 'pp_shop_products', [ 'quantity' => $quantity == '' ? $quantity = null : $quantity = (int) $quantity ], [ 'id' => $product_id ] );
  }

  static public function product_combination_price_save( int $product_id, $price_netto )
  {
    global $mdb;

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

    $price_brutto = $price_netto * ( 1 + ( $vat / 100 ) );

    return $mdb -> update( 'pp_shop_products', [ 'price_netto' => $price_netto == '' ? $price_netto = null : $price_netto = (float) $price_netto, 'price_brutto' => $price_brutto == '' ? $price_brutto = null : $price_brutto = (float) $price_brutto ], [ 'id' => $product_id ] );
  }

  // aktualizacja ceny produktu pod wpływem zmiany ceny wartości atrybutu
  static public function update_product_price_by_attribute_value_impact( $value_id, $impact_on_the_price )
  {
    global $mdb;

    $products = $mdb -> select( 'pp_shop_products_attributes', [ 'product_id' ], [ 'value_id' => $value_id ] );
    if ( is_array( $products ) ) foreach ( $products as $row )
    {
      $parent_id = $mdb -> get( 'pp_shop_products', 'parent_id', [ 'id' => $row['product_id'] ] );
      $product = $mdb -> get( 'pp_shop_products', '*', [ 'id' => $parent_id ] );
      if ( $product )
      {
        $price_brutto = $product['price_brutto'] + \S::normalize_decimal( $impact_on_the_price );
        $price_netto = \S::normalize_decimal( $price_brutto / ( 1 + ( $product['vat'] / 100 ) ) );

        if ( $product['price_netto_promo'] )
        {
          $price_brutto_promo = $product['price_brutto_promo'] + \S::normalize_decimal( $impact_on_the_price );
          $price_netto_promo = \S::normalize_decimal( $price_brutto_promo / ( 1 + ( $product['vat'] / 100 ) ) );
        }
        else
        {
          $price_netto_promo = null;
          $price_brutto_promo = null;
        }

        if ( $impact_on_the_price > 0 )
        {
          $mdb -> update( 'pp_shop_products', [ 'price_netto' => $price_netto, 'price_brutto' => $price_brutto, 'price_netto_promo' => $price_netto_promo, 'price_brutto_promo' => $price_brutto_promo, 'date_modify' => date( 'Y-m-d H:i:s' ) ], [ 'id' => $row['product_id'] ] );
        }
        else if ( isset( $impact_on_the_price ) && $impact_on_the_price == 0 && $impact_on_the_price != '' )
        {
          $mdb -> update( 'pp_shop_products', [ 'price_netto' => null, 'price_brutto' => null, 'price_netto_promo' => null, 'price_brutto_promo' => null, 'quantity' => null, 'stock_0_buy' => null, 'date_modify' => date( 'Y-m-d H:i:s' ) ], [ 'id' => $row['product_id'] ] );
        }
      }
    }
  }
}
