<?php

declare(strict_types=1);

namespace Gls\GlsPoland\PrestaShop\ObjectModel\Repository;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Query\QueryBuilder;
use Gls\GlsPoland\PrestaShop\ObjectModel\HydratorInterface;

/**
 * @template T of \ObjectModel
 *
 * @template-extends AbstractObjectModelRepository<T>
 */
abstract class AbstractMultiLangObjectModelRepository extends AbstractObjectModelRepository
{
    /**
     * @param class-string<T> $modelClass
     */
    public function __construct(string $modelClass, Connection $connection, string $dbPrefix, ?HydratorInterface $hydrator = null)
    {
        parent::__construct($modelClass, $connection, $dbPrefix, $hydrator);

        if (false === ($this->definition['multilang'] ?? false)) {
            throw new \LogicException(sprintf('%s is not a multi language entity.', $modelClass));
        }
    }

    /**
     * @return T[]
     */
    public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
    {
        $data = $this->getRawData($criteria, $orderBy, $limit, $offset);
        $languageId = isset($criteria['id_lang']) ? (int) $criteria['id_lang'] : null;

        return $this->hydrateCollection($data, $languageId);
    }

    protected function createLangQueryBuilder(string $alias, ?int $languageId = null, bool $innerJoin = false): QueryBuilder
    {
        $langAlias = $alias . 'l';
        $langTable = $this->definition['table'] . '_lang';
        $identifier = $this->definition['primary'];

        $qb = $this
            ->createQueryBuilder($alias)
            ->select($langAlias . '.*')
            ->addSelect($alias . '.*');

        $joinConditions = [sprintf('%s.%s = %s.%s', $langAlias, $identifier, $alias, $identifier)];

        if (null !== $languageId) {
            $joinConditions[] = $langAlias . '.id_lang = :languageId';
            $qb->setParameter('languageId', $languageId);
        }

        return $innerJoin
            ? $qb->innerJoin($alias, $langTable, $langAlias, implode(' AND ', $joinConditions))
            : $qb->leftJoin($alias, $langTable, $langAlias, implode(' AND ', $joinConditions));
    }

    protected function createFindQueryBuilder(string $alias): QueryBuilder
    {
        return $this->createLangQueryBuilder($alias);
    }

    /**
     * @return T|null
     */
    protected function hydrate(array $data, ?int $languageId = null): ?\ObjectModel
    {
        if ([] === $data) {
            return null;
        }

        return $this->hydrator->hydrate($data, $this->modelClass, $languageId);
    }

    /**
     * @return T[]
     */
    protected function hydrateCollection(array $data, ?int $languageId = null): array
    {
        return $this->hydrator->hydrateCollection($data, $this->modelClass, $languageId);
    }

    protected function getFieldType($field): int
    {
        if ('id_lang' === $field) {
            return \ObjectModel::TYPE_INT;
        }

        return parent::getFieldType($field);
    }

    protected function generateAlias(string $field, string $alias): string
    {
        return 'id_lang' !== $field && empty($this->definition['fields'][$field]['lang'])
            ? $alias
            : $alias . 'l';
    }
}
