<?php

declare(strict_types=1);

namespace Cin\Quickpurchase\service;

use Cin\Quickpurchase\classes\PurchaseArticle;
use Cin\Quickpurchase\Plugin;
use JTL\Catalog\Product\Artikel;
use JTL\Shop;
use JTL\Smarty\JTLSmarty;
use PDO;
use Smarty;
use stdClass;
use Webit\Util\EvalMath\EvalMath;

final class PurchaseService
{
  public const MAIL_HEADER_IMAGE_NAME = 'MailHeaderLogo';

  public const MAIL_HEADER_IMAGE_PATH = '/mediafiles/cin_quick_purchase_5/';

  private static $plugin;

  public static function calcSumByFormula(Artikel|PurchaseArticle $article, string $formula): float
  {
    if ('JTL\Catalog\Product\Artikel' === \get_class($article)) {
      $formula = str_replace('VKN', (string) $article->Preise->fVKNetto, $formula);
      $formula = str_replace('VKB', (string) $article->Preise->fVKBrutto, $formula);
    } else {
      $formula = str_replace('VKN', (string) $article->fVKNetto, $formula);
      $formula = str_replace('VKB', (string) $article->fVKBrutto, $formula);
    }

    return (new EvalMath())->evaluate($formula);
  }

  public static function calcSumByThreshold(float $sum, PurchaseArticle $purchaseArticle): float
  {
    $pluginConfig = self::Plugin()->cfg();

    if ( ! $purchaseArticle->isInStock() && ! $purchaseArticle->hasMinOrderQuantity()) {
      $sum = $sum * (float) $pluginConfig['cin_ankauf_no_stock_bonus_factor'];
    } elseif ($purchaseArticle->hasMinOrderQuantity() && ($purchaseArticle->fLagerbestand / $purchaseArticle->mindestbestand) > $pluginConfig['cin_ankauf_upper_stock_threshold']) {
      $sum = $sum * (float) $pluginConfig['cin_ankauf_upper_stock_threshold_factor'];
    } elseif ($purchaseArticle->hasMinOrderQuantity() && ($purchaseArticle->fLagerbestand / $purchaseArticle->mindestbestand) < $pluginConfig['cin_ankauf_lower_stock_threshold']) {
      $sum = $sum * (float) $pluginConfig['cin_ankauf_lower_stock_threshold_factor'];
    }

    return $sum;
  }

  public static function ensureStorage(): void
  {
    if ( ! isset($_SESSION['cin_ankauf_storage'])) {
      $_SESSION['cin_ankauf_storage'] = [];
    }
  }

  public static function getArticles(string $needle)
  {
    $P                = new Plugin();
    $cfg              = $P->cfg();
    $articles         = self::fetchArticles($needle);
    $manufacturerList = (string) ($cfg['cin_ankauf_manufacturers_ext'] ?? '');
    $cfg['mList']     = '' === trim($manufacturerList)
      ? []
      : array_values(
        array_filter(
          array_map(static fn ($v): string => trim((string) $v), explode(',', $manufacturerList)),
          static fn (string $v): bool => '' !== $v
        )
      );

    array_walk($articles, static function (&$row) use ($cfg): void {
      $row['Artikel']    = (new Artikel())->fuelleArtikel((int) $row['kArtikel']);
      $row['maxAnkauf']  = (0 === (int) $row['mindestbestand'] && 0 === (int) $row['Artikel']->fLagerbestand) ? 1 : (int) $row['mindestbestand'] - (int) $row['Artikel']->fLagerbestand;
      $row['maxAnkauf']  = max(min($row['maxAnkauf'], 30), 1);
      $row['queryimage'] = isset($row['Artikel']->Bilder[0]) ? ('tile' === $cfg['cin_ankauf_style_queryresults'] ? $row['Artikel']->Bilder[0]->cURLKlein : $row['Artikel']->Bilder[0]->cURLMini) : '/gfx/keinBild.gif';
      // $row['queryimage'] = 'tile' === $cfg['cin_ankauf_style_queryresults'] ? $row['Artikel']->Bilder[0]->cURLKlein : $row['Artikel']->Bilder[0]->cURLMini;

      $tmpDesc = htmlspecialchars_decode($row['Artikel']->cBeschreibung ?? '');
      $tmpDesc = preg_replace('/(hersteller:|fabricant|manufacturer:)\s*/i', '<strong>'.__('manufacturer').':</strong> ', $tmpDesc);
      $tmpDesc = preg_replace('/(typ:|type:|Art\.-Nr\.:)\s*/i', '<br /><strong>$1</strong> ', $tmpDesc);
      $tmpDesc = preg_replace('/(inkl\.:)\s*/i', '<br />$1 ', $tmpDesc);

      $row['Artikel']->cBeschreibung = '<strong>'.__('manufacturer').':</strong> '.$row['Artikel']->cHersteller.'<br />'.$tmpDesc;

      // find extern manufacturer
      if (null === $row['Artikel']->cArtNr) {
        $row['x'] = 'keine Artikelnummer';
      } else {
        preg_match('/^[a-zA-Z]{1,2}/', $row['Artikel']->cArtNr, $extManf_Matches);
        $extManf = $extManf_Matches[0] ?? '';
        if ('' !== $extManf && \in_array($extManf, $cfg['mList'], true) && (float) $row['Artikel']->Preise->fVKBrutto > (float) $cfg['cin_ankauf_manufacturers_max']) {
          $row['direct_purchase'] = 0;
        }
      }

      if (0 === $row['direct_purchase']) {
        $row['maxAnkauf'] = 10;
      }
    });

    return $articles;
  }

  public static function getDefaultResponse(): stdClass
  {
    $response   = new stdClass();
    $conditions = ConditionService::buildConditionArray();
    $total      = PurchaseStorageService::total(self::Plugin()->cfg(), $conditions);

    $response->data                    = new stdClass();
    $response->data->minsumreached     = $total >= self::Plugin()->cfg()['cin_ankauf_min_sum'];
    $response->data->onlyQueueItems    = PurchaseStorageService::hasOnlyQueueItems();
    $response->data->storage           = PurchaseArticle::storage();
    $response->data->cin_ankauf_access = (isset($_SESSION['Kunde']) && true === (bool) $_SESSION['Kunde']->nRegistriert);

    $response->status  = 'success';
    $response->message = '';

    return $response;
  }

  public static function getTemplates(string $type, $smarty = null): object
  {
    $tpls = new stdClass();
    $P    = new Plugin();
    $jP   = $P->getJTlPlugin();
    $cfg  = $P->cfg();

    switch ($type) {
      case 'admin':

        // $adminPath = $jP->getPaths()->getAdminPath().'/template/';
        break;

      case 'queue':

        $queuePath = $jP->getPaths()->getAdminPath().'/template/queue/';

        $tpls->tableHeader     = file_get_contents($queuePath.'header.tpl');
        $tpls->tableHeaderLast = file_get_contents($queuePath.'header_last.tpl');
        $tpls->tableImage      = file_get_contents($queuePath.'td_image.tpl');
        $tpls->tableActions    = file_get_contents($queuePath.'td_action.tpl');

        $tpls->notify = self::prepareTemplate(file_get_contents($jP->getPaths()->getBasePath().'/common/template/notify.tpl'), $P);

        break;

      case 'purchaseportal':

        $frontendPath = $jP->getPaths()->getFrontendPath().'/template/';
        $name         = 'article_item_'.$cfg['cin_ankauf_style_queryresults'].'.tpl';

        $tpls->article = file_get_contents($frontendPath.$name);
        // $tpls->storageAdded    = self::prepareTemplate(file_get_contents($frontendPath.'pp_storage_added_item.tpl'), $P);
        // $tpls->storageReserved = self::prepareTemplate(file_get_contents($frontendPath.'pp_storage_reserved_item.tpl'), $P);

        $tpls->notify = self::prepareTemplate(file_get_contents($jP->getPaths()->getBasePath().'/common/template/notify.tpl'), $P);

        break;

      default:
    }

    return $tpls;
  }

  public static function Plugin(): Plugin
  {
    return self::$plugin ?? (self::$plugin = new Plugin());
  }

  // public static function weBuyNthArticles(): string
  // {
  //   $db = Shop::Container()->getDB();

  //   $getPurchaseArticle = $db->getPDO()->prepare("SELECT
  //     COUNT(a.kArtikel) AS directPurchaseArticles
  //     FROM tartikel a
  //     LEFT JOIN tartikelattribut aa_mb ON aa_mb.kArtikel = a.kArtikel AND aa_mb.cName = 'mindestbestand global'
  //     LEFT JOIN (
  //       SELECT sa.kArtikel
  //       FROM tartikel sa
  //       LEFT JOIN tartikelattribut aa ON aa.kArtikel = sa.kArtikel
  //       WHERE aa.cName = 'AKP'
  //       AND (aa.cWert = '1' OR aa.cWert = 'true')
  //     ) ss ON ss.kArtikel = a.kArtikel
  //     WHERE a.kArtikel = ss.kArtikel
  //     AND aa_mb.cName = 'mindestbestand global'
  //     AND ((CAST(IFNULL(aa_mb.cWert, 0) AS UNSIGNED) - a.fLagerbestand ) > 0
  //     OR (CAST(IFNULL(aa_mb.cWert, 0) AS UNSIGNED) = 0 AND a.fLagerbestand = 0))");
  //   $getPurchaseArticle->execute();

  //   $getAllAKPArticle = $db->getPDO()->prepare('SELECT
  //     COUNT(a.kArtikel) AS directPurchaseArticles
  //     FROM tartikel a
  //     LEFT JOIN tartikelattribut aa ON aa.kArtikel = a.kArtikel
  //     WHERE aa.cName = \'AKP\'
  //     AND (aa.cWert = \'1\' OR aa.cWert = \'true\')');
  //   $getAllAKPArticle->execute();

  //   $getLastChange = $db->getPDO()->prepare('SELECT
  //     `dLetzteAktualisierung` AS datum
  //     FROM `tartikel`
  //     ORDER BY `dLetzteAktualisierung` DESC LIMIT 1');
  //   $getLastChange->execute();

  //   $purchaseArticles = $getPurchaseArticle->fetch(PDO::FETCH_COLUMN);
  //   $allArticles      = $getAllAKPArticle->fetch(PDO::FETCH_COLUMN);
  //   $date             = $getLastChange->fetch(PDO::FETCH_COLUMN);

  //   // msgstr "<p>Wir kaufen heute %1$d verschiedene Artikel zum Festpreis an!</p><p>FÃ¼r %2$d Artikel kÃ¶nnen Sie sich in unsere automatische Ankauf-Warteschlange eintragen - Stand: %3$s</p>"
  //   return \sprintf(__('we_buy_n_article'), $allArticles, $allArticles - $purchaseArticles, $date);
  // }

  /**
   * Cut the string at the first occurrence of one of the search terms.
   */
  private static function cutStringDefensive(string $text, array $needles): string
  {
    foreach ($needles as $needle) {
      $position = mb_stripos($text, $needle);
      $text     = mb_substr($text, 0, (false === $position ? mb_strlen($text) : $position));
    }

    return $text;
  }

  /**
   * Fetch matching articles based on indexed search results.
   *
   * @param string $needle can be name snippet or article number
   */
  /**
   * Fetch matching articles from database.
   *
   * @param string $needle can be name snippet or article number
   */
  private static function fetchArticles(string $needle): array
  {
    $needle = trim($needle);
    $limit  = max(0, (int) self::Plugin()->cfg()['query_result_limit']);

    if ('' === $needle || 0 === $limit) {
      return [];
    }

    $executeParams = [
      ':limit' => $limit,
      ':equal' => $needle
    ];

    $parts = array_values(array_filter(array_map('trim', explode(' ', $needle)), static fn ($v): bool => '' !== $v));
    if (\count($parts) > 1) {
      $i          = 0;
      $conditions = [];
      foreach ($parts as $part) {
        ++$i;
        $executeParams[':like_'.$i] = '%'.$part.'%';
        $conditions[]               = 'a.cName LIKE :like_'.$i;
      }
      $where = '('.implode(' AND ', $conditions).')';
    } else {
      $executeParams[':like'] = '%'.$needle.'%';
      $where                  = 'a.cName LIKE :like';
    }

    $stmt = "SELECT
      subselect.kArtikel,
      subselect.herstellerName AS herstellerName,
      subselect.cName,
      subselect.direct_purchase,
      subselect.mindestbestand,
      (art2.kArtikel IS NOT NULL) AS newArticlePresent,
      art2.cArtNr as newArticleNo,
      art2.cName AS newArticleName
      FROM (
        SELECT DISTINCT
        a.kArtikel,
        a.cName,
        (
            (CAST(IFNULL(aa.cWert, 0) AS UNSIGNED) - a.fLagerbestand ) > 0
            OR
            (CAST(IFNULL(aa.cWert, 0) AS UNSIGNED) = 0 AND a.fLagerbestand = 0)
        ) AS direct_purchase,
        CAST(IFNULL(aa.cWert, 0) AS UNSIGNED) AS mindestbestand,
        h.cName AS herstellerName
        FROM tartikel a
        LEFT JOIN thersteller h ON h.kHersteller = a.kHersteller
        LEFT JOIN tartikelattribut aa ON aa.kArtikel = a.kArtikel AND aa.cName = 'mindestbestand global'
        LEFT JOIN tartikelattribut aaAKP ON aaAKP.kArtikel = a.kArtikel AND aaAKP.cName = 'AKP'
        WHERE (aaAKP.cWert = '1' OR aaAKP.cWert = 'true')
        AND ( {$where} OR a.cArtNr = :equal )
        ORDER BY a.cName ASC
        LIMIT :limit) AS subselect
      LEFT JOIN tartikel art2 ON CONCAT(subselect.cName, ' neu') LIKE art2.cName";

    $prep = self::PDO()->prepare($stmt);
    foreach ($executeParams as $key => $value) {
      $prep->bindValue($key, $value, ':limit' === $key ? PDO::PARAM_INT : PDO::PARAM_STR);
    }
    $prep->execute();

    return $prep->fetchAll(PDO::FETCH_ASSOC) ?: [];
  }
  /**
   * prepare smarty templates for ajax requests. Replace params and language params will be set. Includes like {{include ...}} are possible.
   */
  private static function prepareTemplate(string $snippet, Plugin $P): string
  {
    // fetch includes
    $snippet = preg_replace_callback("/{include\\s*'(.+)'}/", static function ($replace) use (&$count, $P) {
      return \is_array($replace) && \count($replace) >= 2 ? file_get_contents($P->getJTlPlugin()->getPaths()->getFrontendPath().'/template/'.$replace[1]) : '';
    }, $snippet);

    // line break fix for patterns
    $snippet = preg_replace('/(\r\n|\r|\n)/', '', $snippet);
    $snippet = preg_replace('/{\*\/repl\*}/', '{*/repl*}'."\n", $snippet);

    $count   = 0;
    $snippet = preg_replace('/{[^?_*][^{]*(?<!translate)}/', '<!-- replace -->', preg_replace('/{\*repl\*}.*{\*\/repl\*}/', '<!-- replace -->', $snippet));

    $smarty = new JTLSmarty();
    $smarty->registerPlugin(
      Smarty::PLUGIN_MODIFIER,
      'cin_quick_purchase_translate',
      [$P, 'translate']
    );
    $snippet = $smarty->fetch('string:'.$snippet);

    return preg_replace_callback('/<!-- replace -->/', static function () use (&$count) {
      return '{'.$count++.'}';
    }, $snippet);
  }

  private static function PDO(): PDO
  {
    return self::Plugin()->getPDO();
  }
}