<?php

namespace KupShop\OrderingBundle\EventListener;

use KupShop\KupShopBundle\Config;
use KupShop\KupShopBundle\Email\LatePackageEmail;
use KupShop\KupShopBundle\Email\OrderNotPaidEmail;
use KupShop\KupShopBundle\Email\WaitingPackageEmail;
use KupShop\KupShopBundle\Event\CronEvent;
use Query\Operator;
use Query\QueryBuilder;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class CronListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        $listeners = [];
        if (findModule(\Modules::ORDERS, \Modules::SUB_NOTIFY_NOT_PAID)) {
            $listeners[] = ['handleNotifyNotPaid', 200];
        }
        if (findModule(\Modules::ORDERS, \Modules::SUB_NOTIFY_WAITING_PACKAGE)) {
            $listeners[] = ['handleNotifyWaitingPackage', 200];
        }
        if (findModule(\Modules::ORDERS, \Modules::SUB_NOTIFY_LATE_PACKAGE)) {
            $listeners[] = ['handleNotifyLatePackage', 200];
        }

        if (findModule(\Modules::PAYMENTS, \Modules::SUB_THEPAY)) {
            $listeners[] = ['checkThePayPaid', 100];
        }

        if (findModule(\Modules::PAYMENTS, \Modules::SUB_GOPAY)) {
            $listeners[] = ['checkGoPayPaid', 100];
        }

        if (findModule(\Modules::PAYMENTS, \Modules::SUB_ESSOX_SPLATKY)
            || findModule(\Modules::PAYMENTS, \Modules::SUB_ESSOX_ROZDELENI)) {
            $listeners[] = ['checkEssoxPaid', 100];
        }

        if (findModule(\Modules::PAYMENTS, \Modules::SUB_TWISTO_PAY)
            || findModule(\Modules::PAYMENTS, \Modules::SUB_TWISTO_PAYIN3)) {
            $listeners[] = ['checkTwistoPaid', 1000];
        }

        if (findModule(\Modules::PAYMENTS, \Modules::SUB_CSOB)) {
            $listeners[] = ['checkCSOBPaid', 100];
        }

        if (findModule(\Modules::PAYMENTS, \Modules::SUB_PAYPAL)) {
            $listeners[] = ['sendPayPalTrackingInfo', 100];
        }

        if (findModule(\Modules::ORDERS)) {
            $listeners[] = ['cartCleanup', 50];
        }

        //            $listeners[] = ['checkThePay20Paid', 100];

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

        return [
            CronEvent::RUN_NORMAL => $listeners,
        ];
    }

    public function handleNotifyNotPaid()
    {
        $config = findModule(\Modules::ORDERS, \Modules::SUB_NOTIFY_NOT_PAID);
        foreach ((array) ($config ?? []) as $notifySettings) {
            $afterHours = (array) ($notifySettings['afterHours'] ?? []);
            arsort($afterHours);
            foreach ($afterHours as $index => $hours) {
                $orderResultsQb = $this->getNotifyNotPaidQueryBuilder($notifySettings['forPaymentMethods'], $hours, $notifySettings);
                foreach ($orderResultsQb->execute() as $orderResult) {
                    $order = new \Order();
                    $order->createFromDB($orderResult['id']);

                    if ($order->getRemainingPayment() < 0) {
                        continue;
                    }

                    $level = $index + 1;
                    // Send notification only if this level message not sent
                    if ($order->getData('not_paid_notified') < $level) {
                        $this->notifyNotPaidOrder($order, $level, $notifySettings);
                    }
                }
            }

            if (($notifySettings['cancelHours'] ?? 0) > 0) {
                $this->cancelOrders($notifySettings['forPaymentMethods'], $notifySettings['cancelHours'], $notifySettings);
            }
        }
    }

    protected function notifyNotPaidOrder(\Order $order, int $level, array $notifySettings): void
    {
        $order->sendEmail(
            null,
            $notifySettings['orderMessage'] ?? null,
            false,
            isset($notifySettings['orderMessage']) ? null : OrderNotPaidEmail::getType()
        );

        $order->setData('not_paid_notified', $level);
    }

    protected function getNotifyNotPaidQueryBuilder(array $forPaymentMethods, int $hours, array $notifySettings): QueryBuilder
    {
        return sqlQueryBuilder()->select('o.id')
            ->from('orders', 'o')
            ->where('o.status_payed = 0 AND o.status_storno = 0 AND o.date_handle IS NULL')
            ->andWhere(\Query\Order::byPaymentMethods($forPaymentMethods))
            ->andWhere('DATE_ADD(o.date_created, INTERVAL :hours HOUR) <= CURRENT_TIMESTAMP()')
            ->setParameter('hours', $hours)
            ->groupBy('o.id')
            ->orderBy('o.date_accept', 'ASC');
    }

    protected function cancelOrders(array $forPaymentMethods, int $cancelHours, array $notifySettings = [])
    {
        $orderResults = $this->getNotifyNotPaidQueryBuilder($forPaymentMethods, $cancelHours, $notifySettings)
            ->execute();

        foreach ($orderResults as $orderResult) {
            $order = new \Order();
            $order->createFromDB($orderResult['id']);

            // Až budou mít přeplatky/nedoplatky jiný status_payed, můžeme dát tohle pryč
            if ($order->getRemainingPayment() < 0) {
                continue;
            }

            if ($order->getData('disable_autostorno')) {
                continue;
            }
            $order->storno(false, 'orderCancelledAutomatically', true);
        }
    }

    public function handleNotifyWaitingPackage()
    {
        $hourOfDay = (int) date('H');
        if ($hourOfDay < 10 || $hourOfDay > 15) {
            // wrong day hour...
            return;
        }

        $cfg = Config::get();
        $orderResults = sqlQueryBuilder()->select('o.id')->from('orders', 'o')
            ->where('o.status = :order_waiting_package_status')
            ->setParameter('order_waiting_package_status', $cfg['Modules']['orders']['notifyWaitingPackage']['status'])
            ->andWhere(\Query\Order::byInPersonDelivery())
            ->andWhere('o.date_handle IS NOT NULL AND DATE_ADD(o.date_handle, INTERVAL :days DAY) <= CURDATE()')
            ->setParameter('days', $cfg['Modules']['orders']['notifyWaitingPackage']['afterDays'])
            ->groupBy('o.id')
            ->orderBy('o.date_handle', 'ASC');
        foreach ($orderResults->execute() as $orderResult) {
            $order = new \Order();
            $order->createFromDB($orderResult['id']);
            if (!$order->getData('waiting_package_notified')) {
                $order->changeStatus($order->status, null, true, null, WaitingPackageEmail::getType());
                $order->setData('waiting_package_notified', true);
            }
        }
    }

    public function handleNotifyLatePackage()
    {
        $hourOfDay = (int) date('H');
        if ($hourOfDay < 18) {
            // wrong day hour...
            return;
        }

        $config = array_merge(
            ['statuses' => [], 'afterDays' => 1],
            Config::get()['Modules']['orders']['notifyLatePackage']
        );
        if ($days = intval(\Settings::getDefault()->custom['notifyLatePackage'] ?? null)) {
            $config['afterDays'] = $days;
        }

        // Pouzivam date_accept, abych mel jistotu, ze objednavak byla zaplacena a pracuje se s ni
        $orderResults = sqlQueryBuilder()->select('o.id')->from('orders', 'o')
            ->where(Operator::inIntArray($config['statuses'], 'o.status'))
            ->andWhere('o.date_accept < DATE_SUB(CURDATE(), INTERVAL (:days - 1) DAY)')
            ->setParameter('days', $config['afterDays'])
            ->groupBy('o.id')
            ->orderBy('o.date_accept', 'ASC');
        foreach ($orderResults->execute() as $orderResult) {
            $order = new \Order();
            $order->createFromDB($orderResult['id']);
            if (!$order->getData('late_package_notified')) {
                $order->changeStatus(
                    $order->status,
                    translate_shop('notificationLatePackage', 'order_msg'),
                    true,
                    null,
                    LatePackageEmail::getType()
                );
                $order->setData('late_package_notified', true);
            }
        }
    }

    public function checkThePayPaid()
    {
        \Payment::includeClass('ThePay');

        $thePay = new \ThePay();
        try {
            $thePay->checkPaidOrders();
        } catch (\TpSoapException $e) {
            // do nothing
        }
    }

    public function checkGoPayPaid()
    {
        \Payment::includeClass('GoPay');

        $payment = new \GoPay();
        $payment->checkPaidOrders();
    }

    public function checkThePay20Paid()
    {
        $payment = new \ThePay20();
        $payment->checkPaidOrders();
    }

    public function checkEssoxPaid()
    {
        \Payment::includeClass('Essox2Splatky');
        $payment = new \Essox2Splatky();
        $payment->checkPaidOrders();

        \Payment::includeClass('Essox2Rozdeleni');
        $payment = new \Essox2Rozdeleni();
        $payment->checkPaidOrders();
    }

    public function checkTwistoPaid()
    {
        \Payment::includeClass('TwistoPay');
        $payment = new \TwistoPay();
        $payment->checkPaidOrders();
    }

    public function checkCSOBPaid()
    {
        \Payment::includeClass('CSOB');
        $payment = new \CSOB();
        $payment->checkPaidOrders();
    }

    public function sendPayPalTrackingInfo(): void
    {
        /** @var \PayPal $payment */
        $payment = \Payment::getClass('PayPal');
        $payment->sendOrderTrackings();
    }

    public function cartCleanup()
    {
        sqlQueryBuilder()
            ->delete(getTableName('cart'))
            ->where('id_user IS NULL AND date < DATE_SUB(NOW(), INTERVAL :notlogged DAY)')
            ->setParameter('notlogged', 90)
            ->execute();
    }
}
