<?php

declare(strict_types=1);

namespace KupShop\SalesBundle\SaleList;

use KupShop\KupShopBundle\Util\Entity\EntityUtil;
use KupShop\SalesBundle\Entity\Sale;
use KupShop\SalesBundle\Util\SalesUtil;
use Query\Operator;
use Query\QueryBuilder;
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;

class SaleList
{
    private array $specs = [];
    private array $resultModifiers = [];

    public function __construct(
        protected readonly EntityUtil $entityUtil,
        protected readonly MultiFetch $multiFetch,
    ) {
    }

    public function getSales(?int &$totalCount = null): SaleCollection
    {
        $useTotalCount = (bool) count(func_get_args());

        $qb = $this->createQueryBuilder($useTotalCount);
        $result = $qb->execute();

        if ($useTotalCount) {
            $totalCount = (int) sqlFetchAssoc(sqlQuery('SELECT FOUND_ROWS() as total_count'))['total_count'];
        }

        $sales = [];
        foreach ($result as $item) {
            $sales[$item['id']] = $this->entityUtil->createEntity(
                SalesUtil::prepareSale($item),
                Sale::class,
                new CamelCaseToSnakeCaseNameConverter()
            );
        }

        $saleCollection = new SaleCollection($sales);

        foreach ($this->resultModifiers as $callback) {
            $callback($saleCollection);
        }

        return $saleCollection;
    }

    public function orderBy(string $sort, ?string $order = null): self
    {
        $this->andSpec(function (QueryBuilder $qb) use ($sort, $order) {
            $qb->orderBy($sort, $order);
        });

        return $this;
    }

    public function limit(int $count, ?int $offset = null): self
    {
        $this->andSpec(function (QueryBuilder $qb) use ($count, $offset) {
            if (isset($offset)) {
                $qb->setFirstResult($offset);
            }
            $qb->setMaxResults($count);
        });

        return $this;
    }

    public function addResultModifiers(callable $resultModifier): self
    {
        $this->resultModifiers[] = $resultModifier;

        return $this;
    }

    public function andSpec(?callable $spec = null): self
    {
        $this->specs[] = $spec;

        return $this;
    }

    public function fetchItems(): self
    {
        $this->addResultModifiers(fn (SaleCollection $sales) => $this->multiFetch->fetchItems($sales));

        return $this;
    }

    public function getQueryBuilder(): QueryBuilder
    {
        return $this->createQueryBuilder(false);
    }

    protected function createQueryBuilder(bool $useTotalCount = true): QueryBuilder
    {
        $qb = sqlQueryBuilder()
            ->select('s.*')
            ->from('sales', 's');

        if ($useTotalCount) {
            $qb->addCalcRows();
        }

        $qb->andWhere(Operator::andX($this->specs));

        return $qb;
    }
}
