<?php

namespace KupShop\GlobalDiscountsBundle\Util;

use KupShop\CatalogBundle\Util\ProductsFilterSpecs;
use Query\Operator;

class GlobalDiscounts
{
    private $filterSpecs;

    public function __construct(ProductsFilterSpecs $filterSpecs)
    {
        $this->filterSpecs = $filterSpecs;
    }

    public function activateDiscounts()
    {
        foreach ($this->getActivatableDiscounts() as $discount) {
            $this->activateDiscount($discount);
        }
    }

    public function deactivateDiscounts()
    {
        $deactivableDiscounts = $this->getDeactivableDiscounts();

        foreach ($deactivableDiscounts as $discount) {
            $this->deactivateDiscount($discount);
        }

        if (!empty($deactivableDiscounts)) {
            $this->recalculateDiscounts();
        }
    }

    public function recalculateDiscounts()
    {
        foreach ($this->getActiveDiscounts() as $discount) {
            $this->updateProductsDiscount($discount['discount'], $discount['filter']);
        }
    }

    public function activateDiscount(array $discount): bool
    {
        $this->updateProductsDiscount($discount['discount'], $discount['filter']);
        $this->updateDiscount($discount['id'], ['active' => 1]);

        return true;
    }

    public function deactivateDiscount(array $discount): bool
    {
        $this->updateProductsDiscount(0, $discount['filter']);
        $this->updateDiscount($discount['id'], ['active' => 0]);

        return true;
    }

    public function applyDiscountsOnProducts(array $productIds)
    {
        foreach ($this->getActiveDiscounts() as $discount) {
            $this->updateProductsDiscount($discount['discount'], $discount['filter'], $productIds);
        }
    }

    public function updateProductsDiscount($discountValue, $filter, ?array $productIds = null): void
    {
        $specs = $this->filterSpecs->getSpecs($filter);

        $update = sqlQueryBuilder()->update('products', 'p')
            ->set('p.discount', ':discount')
            ->setParameter(':discount', $discountValue)
            ->andWhere($specs)
            ->andWhere(Operator::not(Operator::findInSet(['FS'], 'p.campaign')));

        if ($productIds !== null) {
            $update->andWhere(Operator::inIntArray($productIds, 'p.id'));
        }

        $update->execute();
    }

    public function updateDiscount(int $discountId, array $values)
    {
        sqlQueryBuilder()->update('global_discounts')
            ->directValues($values)
            ->where(Operator::equals(['id' => $discountId]))
            ->execute();
    }

    public function getDiscount(int $discountId)
    {
        $discount = $this->getQueryBuilder()->select('*')
            ->where(Operator::equals(['id' => $discountId]))
            ->execute()->fetch();

        if (!$discount) {
            return false;
        }

        return $this->prepareDiscount($discount);
    }

    public function getActiveDiscounts(): array
    {
        $discounts = $this->getQueryBuilder(true)
            ->execute()->fetchAll();

        if (!$discounts) {
            return [];
        }

        return array_map(function ($x) { return $this->prepareDiscount($x); }, $discounts);
    }

    public function getActivatableDiscounts(): array
    {
        $discounts = $this->getQueryBuilder(false)
            ->andWhere('(date_from <= :date AND date_to >= :date) OR (date_from IS NULL AND date_to IS NULL) OR (date_from <= :date AND date_to IS NULL) OR (date_from IS NULL AND date_to >= :date)')
            ->setParameter('date', new \DateTime(), 'datetime')
            ->execute()->fetchAll();

        if (!$discounts) {
            return [];
        }

        return array_map(function ($x) { return $this->prepareDiscount($x); }, $discounts);
    }

    public function getDeactivableDiscounts(): array
    {
        $discounts = $this->getQueryBuilder(true)
            ->andWhere('date_from >= :date OR date_to <= :date')
            ->orderBy('position')
            ->setParameter('date', new \DateTime(), 'datetime')
            ->execute()->fetchAll();

        if (!$discounts) {
            return [];
        }

        return array_map(function ($x) { return $this->prepareDiscount($x); }, $discounts);
    }

    protected function getQueryBuilder($active = null)
    {
        $qb = sqlQueryBuilder()->from('global_discounts')
            ->select('*')
            ->orderBy('position')
            ->sendToMaster();

        if ($active !== null) {
            $qb->andWhere(Operator::equals(['active' => $active ? 1 : 0]));
        }

        return $qb;
    }

    private function prepareDiscount($discount)
    {
        $discount['filter'] = json_decode($discount['filter'], true);

        return $discount;
    }
}
