<?php

declare(strict_types=1);

namespace Cin\Quickpurchase\service;

use Cin\Quickpurchase\classes\PurchaseArticle;
use Cin\Quickpurchase\classes\QueueItem;
use Cin\Quickpurchase\classes\QueueList;
use Cin\Quickpurchase\ColorMan;
use FilesystemIterator;
use JTL\Catalog\Product\Artikel;
use JTL\Customer\Customer;
use JTL\Shop;
use PDO;
use stdClass;

final class QueueService
{
  public const FILTER_EXPIRED = 0x1;

  public const FILTER_PREMIUM = 0x2;

  public const FILTER_LOW_STOCK = 0x4;

  public const FILTER_NON_STOCK = 0x8;

  /**
   * Deletes all as deleted marked queue entries older than one day.
   */
  public static function deleteFinally(): void
  {
    $prep = PurchaseService::Plugin()->getPDO()->prepare('DELETE FROM `xplugin_cin_purchaseportal_queue` WHERE `deleted` < (now() - INTERVAL 1 day)');
    $prep->execute();
  }

  /**
   * Return customer name ('name surname').
   */
  public static function findName(int $kKunde): string
  {
    $customer = new Customer($kKunde);

    return $customer->cVorname.' '.$customer->cNachname;
  }

  public static function findArticle(int $kArtikel): Artikel
  {
    $article = new Artikel();

    return $article->fuelleArtikel($kArtikel);
  }

  public static function getCachedArticlePreviewName(int $kArtikel): string
  {
    $cacheDir   = self::getInvalidateCacheDir();
    $cachedFile = $cacheDir.$kArtikel.'.png';

    if ( ! file_exists($cachedFile)) {
      $stmt = 'SELECT cPfad
      FROM tartikelpict
      WHERE kArtikel = :kartikel
      AND nNr = 1';

      $prep = PurchaseService::Plugin()->getPDO()->prepare($stmt);
      $prep->execute([':kartikel' => $kArtikel]);

      $cPfad     = $prep->fetchColumn();
      $cPfadFull = PFAD_ROOT.'media/image/storage/'.$cPfad;

      $gdImage = ColorMan::readimage($cPfadFull);
      $dims    = ColorMan::dims($gdImage);
      $factor  = 120 / max($dims);

      $gdImage = ColorMan::scaleTo($gdImage, $factor * $dims[0], $factor * $dims[1]);

      imagepng($gdImage, $cachedFile); // .'.png'
    }

    return $cachedFile;
  }

  public static function getCustomerItems(int $kKunde): array
  {
    $prep = PurchaseService::Plugin()->getPDO()->prepare(
      'SELECT
      q.*,
      a.cName
      FROM `xplugin_cin_purchaseportal_queue` q
      LEFT JOIN tartikel a ON a.kArtikel = q.kArtikel
      WHERE q.kKunde = :kKunde
      AND deleted IS NULL
      ORDER BY q.created DESC'
    );
    $prep->execute([
      ':kKunde' => $kKunde
    ]);

    return $prep->fetchAll();
  }

  /**
   * Clean image cache for artivle previews.
   */
  public static function getInvalidateCacheDir(int $expirationDays = 14)
  {
    clearstatcache();
    $cacheDir = PurchaseService::Plugin()->getJTlPlugin()->getPaths()->getAdminPath().'cache/';
    if ( ! is_dir($cacheDir)) {
      mkdir($cacheDir);
    }
    $cacheAge           = 86400 * $expirationDays;
    $fileSystemIterator = new FilesystemIterator($cacheDir);
    $now                = time();
    foreach ($fileSystemIterator as $file) {
      if ($now - $file->getCTime() >= $cacheAge) {
        unlink($file->getPath().'/'.$file->getFilename());
      }
    }

    return $cacheDir;
  }

  public static function getPurchasePriceSummary(): float
  {
    $queueListFromDb = PurchaseStorageService::queueItemsFromDatabase(0x0);

    $ekn = 0;

    array_walk($queueListFromDb, static function (&$row) use (&$ekn): void {
      $article   = self::findArticle($row->kArtikel);
      $condition = ConditionService::getConditionFromHistoryByID($row->condition_history_id);

      $ekn += PurchaseService::calcSumByFormula($article, $condition->formula);
    });

    return $ekn;
  }

  public static function getQuantitiesSummary(): int
  {
    $prep = PurchaseService::Plugin()->getPDO()->prepare('SELECT SUM(`quantity`) AS qtySum FROM `xplugin_cin_purchaseportal_queue` WHERE `deleted` IS NULL');
    $prep->execute();

    return (int) $prep->fetchColumn() ?: 0;
  }

  public static function getRetailPriceSummary(): float
  {
    $stmt = "SELECT TRUNCATE(SUM(q.quantity * pd.fVKNetto), 2) AS 'sum'
    FROM `xplugin_cin_purchaseportal_queue` q
    LEFT JOIN `tartikel` a ON q.`kArtikel` = a.`kArtikel`
    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`
    WHERE q.deleted IS NULL
    AND q.kKunde = k.`kKunde`";

    $prep = PurchaseService::Plugin()->getPDO()->prepare($stmt);
    $prep->execute();

    return $prep->fetchColumn() ?: 0.0;
  }

  /**
   * Load customer and items from session storage and submit pdf with queue information to customer. And store the items in queue table.
   */
  public static function handleStorageItems(): bool
  {
    $kKunde                 = (int) $_SESSION['Kunde']->kKunde;
    $data                   = MailingService::getDefaultMailData($kKunde);
    $data->mail_queue_items = PurchaseStorageService::getItemsForQueue();

    if (0 === \count($data->mail_queue_items)) {
      return false;
    }

    $data->conditions        = $conditions = ConditionService::buildConditionArray();
    $data->historyConditions = ConditionService::getConditionHistory(['id', 'key', 'name']);

    array_walk($data->mail_queue_items, static function (PurchaseArticle &$item) use ($conditions): void {
      $item->condition_history_id = ConditionService::getConditionIdFromHistory($item->condition, $conditions[$item->condition]);
    });

    if (MailingService::sendQueueMail($data, $kKunde)) {
      self::writePurchaseArticlesToQueue($data->mail_queue_items, $kKunde);

      return true;
    }

    return false;
  }

  public static function markItemAsDeleted(int $databaseId, bool $fromCustomer = false): bool
  {
    $prep = PurchaseService::Plugin()->getPDO()->prepare('UPDATE `xplugin_cin_purchaseportal_queue` SET `deleted` = CURRENT_TIMESTAMP, `deleted_by_user` = :by WHERE `id` = :databaseId');

    return $prep->execute([
      ':databaseId' => $databaseId,
      ':by'         => (int) $fromCustomer
    ]);
  }

  public static function saveColumnSettings(array $columnSettings): bool
  {
    $isSuccessfully = true;

    $pdo = PurchaseService::Plugin()->getPDO();

    $stmt1 = 'UPDATE tplugineinstellungen
      SET cWert = :newValue1
      WHERE cName = :name1';
    $stmt2 = 'UPDATE tplugineinstellungen
      SET cWert = :newValue2
      WHERE cName = :name2';

    foreach ($columnSettings as $columnObject) {
      $prep           = $pdo->prepare($stmt1);
      $isSuccessfully = $isSuccessfully && $prep->execute([
        ':name1'     => 'cin_ankauf_hidden_1_'.str_replace('_', '-', $columnObject['name']),
        ':newValue1' => $columnObject['enabled']
      ]);

      $prep           = $pdo->prepare($stmt2);
      $isSuccessfully = $isSuccessfully && $prep->execute([
        ':name2'     => 'cin_ankauf_hidden_2_'.str_replace('_', '-', $columnObject['name']),
        ':newValue2' => $columnObject['idx']
      ]);
    }

    if ($isSuccessfully) {
      Shop::Container()->getCache()->flushTags([CACHING_GROUP_PLUGIN.'_'.PurchaseService::Plugin()->getJTlPlugin()->getID()]);
    }

    return $isSuccessfully;
  }

  /**
   * Submit all selected queue items as purchase offer.
   *
   * @param array $idAmount Array with simple key value pairs. Key is the database id for queue entry. Value is the quantity requested for the queue item.
   */
  public static function submitPurchaseOffers(array $idAmount): array
  {
    $successList = [];
    $idList      = array_keys($idAmount);
    $list        = implode(',', $idList);

    $prep = PurchaseService::Plugin()->getPDO()->prepare("SELECT kKunde FROM `xplugin_cin_purchaseportal_queue` WHERE id IN ({$list}) GROUP BY kKunde");
    $prep->execute();

    $customerIdList = array_keys($prep->fetchAll(PDO::FETCH_ASSOC | PDO::FETCH_UNIQUE));

    foreach ($customerIdList as $customerDbId) {
      $selectedItems                = new QueueList();
      $customer                     = new Customer($customerDbId);
      $customerResult               = new stdClass();
      $customerResult->customerId   = $customerDbId;
      $customerResult->customerName = $customer->cVorname.' '.$customer->cNachname;
      $customerResult->message      = 'OK';

      $prep = PurchaseService::Plugin()->getPDO()->prepare("SELECT * FROM `xplugin_cin_purchaseportal_queue` WHERE id IN ({$list}) AND kKunde = :kKunde");
      $prep->execute([
        ':kKunde' => $customerDbId
      ]);

      $customerQueueOffers = new QueueList($prep->fetchAll(PDO::FETCH_OBJ));

      $customerQueueOffers->Foreach(static function (QueueItem $queueItem, int $idx) use (&$selectedItems, $idAmount): void {
        $hCondition = ConditionService::getConditionFromHistoryByID($queueItem->condition_history_id);
        if (null === $hCondition) {
          return;
        }

        $queueItem->shop_article     = self::findArticle((int) $queueItem->kArtikel);
        $queueItem->quantity_offer   = $idAmount[$queueItem->id];
        $queueItem->historyCondition = $hCondition;

        $selectedItems->Add($queueItem);
      });

      $customerResult->items = $selectedItems->ToJson();

      if (self::isMinSumReached($selectedItems)) {
        $customerResult->success = self::handleQueueOffer($customer, $selectedItems);
      } else {
        $customerResult->success = false;
        $customerResult->message = __('min sum not reached');
      }

      $successList[] = $customerResult;
    }

    return $successList;
  }

  public static function writePurchaseArticleToQueue(PurchaseArticle $purchaseArticle, int $kKunde): void
  {
    $prep = PurchaseService::Plugin()->getPDO()->prepare('INSERT INTO `xplugin_cin_purchaseportal_queue`
      (`kKunde`, `cMail`, `kArtikel`, `cArtNr`, `quantity`, `condition_history_id`, `custom_description`)
      VALUES (:kKunde, :cMail, :kArtikel, :cArtNr, :quantity, :condition, :description)');
    $prep->execute([
      ':kKunde'      => $kKunde,
      ':cMail'       => MailingService::encrypt_mail($_SESSION['Kunde']->cMail),
      ':kArtikel'    => $purchaseArticle->kArtikel,
      ':cArtNr'      => $purchaseArticle->cArtNr,
      ':quantity'    => $purchaseArticle->quantity,
      ':condition'   => $purchaseArticle->condition_history_id,
      ':description' => $purchaseArticle->custom_description
    ]);
  }

  /**
   * @param PurchaseArticle[] $purchaseArticles
   */
  public static function writePurchaseArticlesToQueue(array $purchaseArticles, int $kKunde): void
  {
    foreach ($purchaseArticles as $pArticle) {
      self::writePurchaseArticleToQueue($pArticle, $kKunde);
    }
  }

  /**
   * Check if the minimum amount for an offer from seller to one customer reached.
   */
  private static function isMinSumReached(QueueList $queueList): bool
  {
    $totalAmount = 0;

    $queueList->Foreach(static function (QueueItem $queueItem) use (&$totalAmount): void {
      $hCondition = ConditionService::getConditionFromHistoryByID($queueItem->condition_history_id);

      if (null === $hCondition) {
        return;
      }

      $article    = $queueItem->shop_article;
      $calculated = PurchaseService::calcSumByFormula($article, $hCondition->formula);
      $calculated = PurchaseService::calcSumByThreshold($calculated, $queueItem->ToPurchaseArticle());

      $totalAmount += (round($calculated, 2) * $queueItem->quantity_offer);
    });

    return ((float) PurchaseService::Plugin()->cfg()['cin_ankauf_min_sum']) <= $totalAmount;
  }
}
