<?php

declare(strict_types=1);

namespace Cin\Quickpurchase\service;

use Cin\Quickpurchase\classes\PurchaseArticle;
use Cin\Quickpurchase\Plugin;
use JTL\Catalog\Product\Artikel;
use PDO;

final class PurchaseStorageService
{
  private static $pdo;

  public static function add(PurchaseArticle $article): void
  {
    $_SESSION['cin_ankauf_storage'][] = $article->__toString();
  }

  /**
   * Counts total amount of kArtikel in storage.
   */
  public static function count(int $kArtikel): int
  {
    $count = 0;

    PurchaseArticle::foreach($_SESSION['cin_ankauf_storage'], static function (PurchaseArticle $pArticle) use (&$count, $kArtikel): void {
      if ($kArtikel === $pArticle->kArtikel) {
        $count += $pArticle->quantity;
      }
    });

    return $count;
  }

  /**
   * Removes Artikel ID from storage (SESSION).
   *
   * @return bool FALSE if Artikel not in SESSION | TRUE if Article removed
   */
  public static function delete(int $kArtikel): bool
  {
    if ( ! isset($_SESSION['cin_ankauf_storage']) || ! \count($_SESSION['cin_ankauf_storage'])) {
      return false;
    }

    // @var PurchaseArticle $pArticle
    foreach ($_SESSION['cin_ankauf_storage'] as $idx => $articleJson) {
      $pArticle = new PurchaseArticle($articleJson);
      if ($pArticle->kArtikel === $kArtikel) {
        unset($_SESSION['cin_ankauf_storage'][$idx]);

        return true;
      }
    }

    return false;
  }

  public static function getItemsForQueue(): array
  {
    return array_filter(PurchaseArticle::storage(), static fn (PurchaseArticle $pArticle) => 0 === $pArticle->direct_purchase);
  }

  public static function hasItems(): bool
  {
    return isset($_SESSION['cin_ankauf_storage']) && \count($_SESSION['cin_ankauf_storage']);
  }

  /**
   * Check if storage only contains items without direct purchase.
   */
  public static function hasOnlyQueueItems(): bool
  {
    if (0 === \count($_SESSION['cin_ankauf_storage'])) {
      return false;
    }

    foreach ($_SESSION['cin_ankauf_storage'] as $articleJson) {
      $pArticle = new PurchaseArticle($articleJson);

      if (1 === $pArticle->direct_purchase) {
        return false;
      }
    }

    return true;
  }

  /**
   * Load specific article from database.
   *
   * @return Artikel returns an empty article if kArtikel 0
   */
  public static function load(int $kArtikel = 0): Artikel
  {
    $Article = new Artikel();
    if ($kArtikel > 0) {
      $Article->fuelleArtikel($kArtikel);
    }

    return $Article;
  }

  public static function queueItemsFromDatabase(int $filterFlags = 0x0)
  {
    $join  = [];
    $where = [];

    if ($filterFlags & QueueService::FILTER_EXPIRED) {
      $join[] = 'JOIN ( SELECT DISTINCT `kKunde`
      FROM xplugin_cin_purchaseportal_queue q
      WHERE q.refresh_counter = 0
      ORDER BY q.created ASC
      LIMIT 0,10) q2 ON q2.kKunde = q.kKunde';
    } else {
      $where[] = '(q.expired = 0 OR q.expired IS NULL)';
    }

    if ($filterFlags & QueueService::FILTER_PREMIUM) {
      $join[]  = 'LEFT JOIN xplugin_cin_purchaseportal_condition_history h ON h.id = q.condition_history_id';
      $where[] = "(h.key = 'condition_4' OR h.key = 'condition_5') AND a.cName NOT LIKE '%NEU%'";
    }

    if ($filterFlags & QueueService::FILTER_LOW_STOCK) {
      $where[] = 'a.fLagerbestand < CAST(IFNULL(aa.cWert, 0) AS UNSIGNED)';
    }

    if ($filterFlags & QueueService::FILTER_NON_STOCK) {
      $where[] = 'fLagerbestand = 0';
    }

    $select = "SELECT
    q.*,
    a.fLagerbestand,
    CAST(IFNULL(aa.cWert, 0) AS UNSIGNED) AS mindestbestand,
    a.cName articlename,
    pd.fVKNetto AS price,
    IF(DATEDIFF(CURRENT_TIMESTAMP, q.offer_sent) IS NOT NULL, DATEDIFF(CURRENT_TIMESTAMP, q.offer_sent), 0) AS days
    FROM `xplugin_cin_purchaseportal_queue` q
    LEFT JOIN tartikel a ON q.kArtikel = a.kArtikel {repl.joins}
    LEFT JOIN `tpreis` p ON q.`kArtikel` = p.`kArtikel`
    LEFT JOIN tpreisdetail pd ON pd.kPreis = p.kPreis
    LEFT JOIN `tkunde` k ON p.`kKundengruppe` = k.`kKundengruppe`
    LEFT JOIN tartikelattribut aa ON aa.kArtikel = a.kArtikel AND aa.cName = 'mindestbestand global'
    WHERE q.deleted IS NULL
    AND q.kKunde = k.`kKunde`
    AND ".implode(' AND ', $where).(empty($where) ? '1 = 1' : '');

    $select = str_replace('{repl.joins}', implode(' ', $join), $select);

    $prep = self::PDO()->prepare($select);
    $prep->execute();

    return $prep->fetchAll();
  }

  /**
   * Storage information about an article.
   * - mindestbestand
   * - fLagerbestand
   * - direct_purchase.
   */
  public static function state(int $kArtikel): object
  {
    $prep = self::PDO()->prepare("SELECT
    CAST(IFNULL(aa.cWert, 0) AS UNSIGNED) AS mindestbestand,
    a.fLagerbestand,
    (
        (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
    FROM tartikel a
    LEFT JOIN tartikelattribut aa ON aa.kArtikel = a.kArtikel AND aa.cName = 'mindestbestand global'
    WHERE a.kArtikel = :kArtikel");

    $prep->execute([':kArtikel' => $kArtikel]);

    return $prep->fetch();
  }

  /**
   * Calculates the total amount of items for direct purchase.
   */
  public static function total(array $pluginConfig, ?array $conditions = null): float
  {
    $total = 0;

    PurchaseArticle::foreach($_SESSION['cin_ankauf_storage'], static function (PurchaseArticle $pArticle) use (&$total): void {
      if (0 === $pArticle->direct_purchase) {
        return;
      }

      $conditions = ConditionService::buildConditionArray();
      $calculated = PurchaseService::calcSumByFormula($pArticle, $conditions[$pArticle->condition]->formula);

      $calculated = PurchaseService::calcSumByThreshold($calculated, $pArticle);

      $total += (round($calculated, \PHP_ROUND_HALF_DOWN) * $pArticle->quantity);
    });

    return $total;
  }

  private static function PDO(): PDO
  {
    return self::$pdo ?? (self::$pdo = (new Plugin())->getPDO());
  }
}
