<?php
declare(strict_types=1);

namespace Autogedal\StockLog\Cron;

use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\ResourceConnection;
use Psr\Log\LoggerInterface;

class Cleanup
{
    private const XML_ENABLE         = 'autogedal_stocklog/cleanup/enable_cleanup';
    private const XML_RETENTION      = 'autogedal_stocklog/cleanup/retention_months';
    private const XML_PURGE_ARCHIVE  = 'autogedal_stocklog/cleanup/enable_archive_purge';

    private const MAIN_TABLE    = 'autogedal_stock_log';
    private const ARCHIVE_TABLE = 'autogedal_stock_log_archive';

    private const BATCH_SIZE = 5000;

    public function __construct(
        private readonly ScopeConfigInterface $scopeConfig,
        private readonly ResourceConnection   $resource,
        private readonly LoggerInterface      $logger
    ) {
    }

    public function execute(): void
    {
        if (!$this->scopeConfig->isSetFlag(self::XML_ENABLE)) {
            return;
        }

        $months = max(1, (int)$this->scopeConfig->getValue(self::XML_RETENTION));
        $cutoff = (new \DateTimeImmutable("-{$months} months", new \DateTimeZone('UTC')))->format('Y-m-d H:i:s');

        $this->archiveAndDelete($cutoff);

        if ($this->scopeConfig->isSetFlag(self::XML_PURGE_ARCHIVE)) {
            $purgeMonths = $months * 2;
            $purgeCutoff = (new \DateTimeImmutable("-{$purgeMonths} months", new \DateTimeZone('UTC')))->format('Y-m-d H:i:s');
            $this->purgeArchive($purgeCutoff);
        }
    }

    private function archiveAndDelete(string $cutoff): void
    {
        $connection   = $this->resource->getConnection();
        $mainTable    = $this->resource->getTableName(self::MAIN_TABLE);
        $archiveTable = $this->resource->getTableName(self::ARCHIVE_TABLE);
        $archivedAt   = (new \DateTimeImmutable('now', new \DateTimeZone('UTC')))->format('Y-m-d H:i:s');

        $totalArchived = 0;
        $totalDeleted  = 0;

        do {
            $select = $connection->select()
                ->from($mainTable)
                ->where('created_at < ?', $cutoff)
                ->limit(self::BATCH_SIZE);

            $rows = $connection->fetchAll($select);

            if (empty($rows)) {
                break;
            }

            $toInsert = array_map(static function (array $row) use ($archivedAt): array {
                $row['archived_at'] = $archivedAt;
                return $row;
            }, $rows);

            $connection->insertOnDuplicate($archiveTable, $toInsert, ['archived_at']);

            $ids = array_column($rows, 'log_id');
            $deleted = $connection->delete($mainTable, ['log_id IN (?)' => $ids]);

            $totalArchived += count($rows);
            $totalDeleted  += $deleted;

        } while (count($rows) === self::BATCH_SIZE);

        if ($totalArchived > 0) {
            $this->logger->info(
                sprintf('StockLog Cleanup: archived %d rows, deleted %d rows (cutoff: %s)', $totalArchived, $totalDeleted, $cutoff)
            );
        }
    }

    private function purgeArchive(string $purgeCutoff): void
    {
        $connection   = $this->resource->getConnection();
        $archiveTable = $this->resource->getTableName(self::ARCHIVE_TABLE);

        $purged = $connection->delete($archiveTable, ['archived_at < ?' => $purgeCutoff]);

        if ($purged > 0) {
            $this->logger->info(
                sprintf('StockLog Cleanup: purged %d rows from archive (cutoff: %s)', $purged, $purgeCutoff)
            );
        }
    }
}
