<?php

declare(strict_types=1);

namespace KupShop\KupShopBundle\EventListener;

use KupShop\KupShopBundle\Config;
use KupShop\KupShopBundle\Event\CronEvent;
use Query\Operator as Op;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class CleanupListener implements EventSubscriberInterface
{
    public const INTERVALS = [
        'orders_history' => '3 YEAR',
        'orders_history_emails' => '2 YEAR',

        'discounts_coupons' => '6 MONTH',
        'admins_accesses' => '1 YEAR',

        'warehouse_log' => '2 YEAR',
        'warehouse_orders' => '6 MONTH',

        'balikonos' => '1 YEAR',
    ];

    public static function getSubscribedEvents(): array
    {
        return [
            CronEvent::RUN_EXPENSIVE => [
                ['runCleanupOfOldObjectsFromDatabase', 0],
            ],
        ];
    }

    private function tableExists(string $tableName): bool
    {
        $db = Config::get()['Connection']['database'];

        $count = sqlQueryBuilder()
            ->select('COUNT(*)')
            ->from('information_schema.TABLES')
            ->andWhere(Op::equals([
                'TABLE_SCHEMA' => $db,
                'TABLE_NAME' => $tableName,
            ]))
            ->execute()
            ->fetchOne();

        return $count !== false && $count > 0;
    }

    public function runCleanupOfOldObjectsFromDatabase(): void
    {
        $cleanupMethods = array_filter(get_class_methods($this), fn (string $method) => str_starts_with($method, 'cleanup'));

        foreach ($cleanupMethods as $method) {
            $this->{$method}();
        }
    }

    public function cleanupOrderHistory(): void
    {
        if (findModule(\Modules::ORDERS, \Modules::SUB_KEEP_HISTORY)) {
            return;
        }

        $deleteEmailHistory = sqlQueryBuilder()
            ->delete('orders_history')
            ->andWhere('EXISTS (SELECT * FROM orders o WHERE o.id = id_order AND o.date_updated < (NOW() - INTERVAL '.self::INTERVALS['orders_history'].'))')
            ->andWhere("JSON_EXISTS(custom_data, '$.email_type')")
            ->andWhere('comment IS NULL')
            ->setMaxResults(100_000);

        $deleteEmailHistory->execute();

        $removeEmailContents = sqlQueryBuilder()
            ->update('orders_history', 'oh')
            ->andWhere('EXISTS (SELECT * FROM orders WHERE id = oh.id_order AND date_updated < (NOW() - INTERVAL '.self::INTERVALS['orders_history_emails'].'))')
            ->andWhere("JSON_EXISTS(oh.custom_data, '$.email_type')")
            ->andWhere("JSON_EXTRACT(oh.custom_data, '$.email_type') <> 'ORDER_CREATE'")
            ->set('oh.comment', 'NULL')
            ->setMaxResults(100_000);

        $removeEmailContents->execute();
    }

    public function cleanupDiscountCoupons(): void
    {
        if (!$this->tableExists('discounts_coupons')) {
            return;
        }

        sqlQueryBuilder()->delete('discounts_coupons')
            ->andWhere('date_to < (NOW() - INTERVAL '.self::INTERVALS['discounts_coupons'].')')
            ->andWhere('id_order_purchased IS NULL')
            ->setMaxResults(100_000)
            ->execute();
    }

    public function cleanupAdminsAccesses(): void
    {
        sqlQueryBuilder()->delete('admins_accesses')
            ->andWhere('date_access < (NOW() - INTERVAL '.self::INTERVALS['admins_accesses'].')')
            ->setMaxResults(100_000)
            ->execute();
    }

    public function cleanupWarehouseLog(): void
    {
        if (!$this->tableExists('warehouse_log')) {
            return;
        }

        sqlQueryBuilder()->delete('warehouse_log')
            ->andWhere('update_datetime < (NOW() - INTERVAL '.self::INTERVALS['warehouse_log'].')')
            ->setMaxResults(100_000)
            ->execute();
    }

    public function cleanupWarehouseOrders(): void
    {
        if (!$this->tableExists('warehouse_orders')) {
            return;
        }

        sqlQueryBuilder()->delete('warehouse_orders')
            ->andWhere('date_start < (NOW() - INTERVAL '.self::INTERVALS['warehouse_orders'].')')
            ->andWhere('id_position IS NULL')
            ->setMaxResults(100_000)
            ->execute();
    }

    public function cleanupBalikobot(): void
    {
        if (!$this->tableExists('balikonos')) {
            return;
        }

        sqlQueryBuilder()->delete('balikonos')
            ->andWhere('EXISTS (SELECT * FROM orders o WHERE o.id = id_order AND o.date_updated < (NOW() - INTERVAL '.self::INTERVALS['balikonos'].'))')
            ->setMaxResults(100_000)
            ->execute();
    }
}
