<?php

namespace KupShop\GraphQLBundle\ApiAdmin\Util;

use KupShop\GraphQLBundle\ApiAdmin\Types\Coupon\Coupon;
use KupShop\GraphQLBundle\ApiAdmin\Types\Coupon\Response\CouponMutateResponse;
use KupShop\GraphQLBundle\ApiAdmin\Types\OrderDiscount\DiscountCoupon;
use KupShop\GraphQLBundle\Exception\GraphQLNotFoundException;
use KupShop\GraphQLBundle\Exception\GraphQLValidationException;
use KupShop\OrderDiscountBundle\Util\DiscountManager;
use KupShop\OrderingBundle\Util\Purchase\PurchaseUtil;
use Query\Operator;
use Query\QueryBuilder;

class CouponUtil
{
    public function __construct(
        private readonly DiscountManager $discountManager,
        private readonly PurchaseUtil $purchaseUtil,
    ) {
    }

    public function checkCodeExists(string $code): bool
    {
        if (!findModule(\Modules::ORDER_DISCOUNT)) {
            return false;
        }

        return $this->discountManager->couponNumberExists($code, $order_discounts);
    }

    public function checkCouponExists(string $coupon): ?Coupon
    {
        if (findModule(\Modules::ORDER_DISCOUNT)) {
            $order_discounts = [];
            if ($this->discountManager->couponNumberExists($coupon, $order_discounts)) {
                $coupon_data = $order_discounts[array_key_first($order_discounts)];
                $coupon_data['code'] = $coupon;

                return new Coupon($coupon_data);
            }
        }

        return null;
    }

    public function getCoupon(string $coupon): ?DiscountCoupon
    {
        if (!findModule(\Modules::ORDER_DISCOUNT)) {
            return null;
        }

        if (!($couponData = $this->getCouponData($coupon))) {
            throw new GraphQLNotFoundException(sprintf('Coupon "%s" was not found!', $coupon));
        }

        return new DiscountCoupon($couponData);
    }

    public function useCoupon(string $coupon): CouponMutateResponse
    {
        if (!findModule(\Modules::ORDER_DISCOUNT)) {
            return new CouponMutateResponse(false);
        }

        // nactu si kupon - tim overim, zda vubec existuje
        $discountCoupon = $this->getCoupon($coupon);

        // pokud kupon nelze pouzit, tak hodim chybu
        if (!$discountCoupon->getIsUsable()) {
            throw new GraphQLValidationException(
                sprintf('Coupon "%s" is not usable because is not active or was used already!', $coupon)
            );
        }

        return new CouponMutateResponse(
            $this->updateCouponState($coupon, true)
        );
    }

    public function reactivateCoupon(string $coupon): CouponMutateResponse
    {
        // nactu si kupon - tim overim, zda vubec existuje
        $this->getCoupon($coupon);

        // reactivuju ho
        return new CouponMutateResponse(
            $this->updateCouponState($coupon, false)
        );
    }

    public function getCouponQueryBuilder(): QueryBuilder
    {
        return sqlQueryBuilder()
            ->select('dc.*, d.descr as name')
            ->from('discounts_coupons', 'dc')
            ->join('dc', 'discounts', 'd', 'd.id = dc.id_discount');
    }

    protected function getCouponData(string $code): ?array
    {
        // hledam v generovanych kodech
        $data = $this->getCouponQueryBuilder()
            ->where(Operator::equals(['code' => $code]))
            ->execute()->fetchAssociative();

        return $data ?: null;
    }

    private function updateCouponState(string $coupon, bool $used): bool
    {
        // zjistim, zda se jedna o generovany poukaz
        $generatedCouponId = sqlQueryBuilder()
            ->select('id')
            ->from('discounts_coupons')
            ->where(Operator::equals(['code' => $coupon]))
            ->execute()->fetchOne();

        if ($generatedCouponId) {
            // aktualizace stavu poukazu - pokud se jedna o generovany poukaz, tak musim aktualizovat jeho stav
            sqlQueryBuilder()
                ->update('discounts_coupons')
                ->directValues(
                    [
                        'used' => $used ? 'Y' : 'N',
                    ]
                )
                ->where(Operator::equals(['id' => $generatedCouponId]))
                ->execute();

            return true;
        }

        return false;
    }
}
