<?php

declare(strict_types=1);

namespace App\Repository;

use App\Entity\Ecole;
use App\Entity\TenantDomain;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

/**
 * @extends ServiceEntityRepository<Ecole>
 */
final class EcoleRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, Ecole::class);
    }

    public function countActives(): int
    {
        return (int) $this->createQueryBuilder('ecole')
            ->select('COUNT(ecole.id)')
            ->andWhere('ecole.active = :active')
            ->setParameter('active', true)
            ->getQuery()
            ->getSingleScalarResult();
    }

    public function findActiveBySlugOrSubdomain(string $identifier): ?Ecole
    {
        $identifier = mb_strtolower(trim($identifier));

        if ($identifier === '') {
            return null;
        }

        $ecole = $this->createQueryBuilder('ecole')
            ->leftJoin('ecole.themePublic', 'theme')
            ->leftJoin('ecole.tenantDomains', 'domain')
            ->addSelect('theme')
            ->addSelect('domain')
            ->andWhere('ecole.active = :active')
            ->andWhere('ecole.slug = :identifier OR ecole.sousDomaine = :identifier')
            ->setParameter('active', true)
            ->setParameter('identifier', $identifier)
            ->getQuery()
            ->getOneOrNullResult();

        if ($ecole instanceof Ecole) {
            return $ecole;
        }

        $normalizedIdentifier = $this->normalizePublicIdentifier($identifier);
        if ($normalizedIdentifier === '') {
            return null;
        }

        foreach ($this->findAllActiveWithDomains() as $candidate) {
            if (
                $this->normalizePublicIdentifier((string) $candidate->getSlug()) === $normalizedIdentifier
                || $this->normalizePublicIdentifier((string) $candidate->getSousDomaine()) === $normalizedIdentifier
            ) {
                return $candidate;
            }
        }

        return null;
    }

    public function findActiveByHost(string $host, string $publicDomain = ''): ?Ecole
    {
        $host = TenantDomain::normalizeHost($host);

        if ($host === '') {
            return null;
        }

        $ecole = $this->createQueryBuilder('ecole')
            ->leftJoin('ecole.themePublic', 'theme')
            ->innerJoin('ecole.tenantDomains', 'matchedDomain', 'WITH', 'matchedDomain.active = :domainActive AND matchedDomain.host = :host')
            ->leftJoin('ecole.tenantDomains', 'domain')
            ->addSelect('theme')
            ->addSelect('domain')
            ->andWhere('ecole.active = :active')
            ->setParameter('active', true)
            ->setParameter('domainActive', true)
            ->setParameter('host', $host)
            ->getQuery()
            ->getOneOrNullResult();

        if ($ecole instanceof Ecole) {
            return $ecole;
        }

        $subdomain = $this->subdomainFromHost($host, $publicDomain);

        return $subdomain !== null ? $this->findActiveBySlugOrSubdomain($subdomain) : null;
    }

    public function findFirstActiveByThemeCode(string $themeCode): ?Ecole
    {
        $themeCode = mb_strtolower(trim($themeCode));

        if ($themeCode === '') {
            return null;
        }

        return $this->createQueryBuilder('ecole')
            ->leftJoin('ecole.themePublic', 'theme')
            ->leftJoin('ecole.tenantDomains', 'domain')
            ->addSelect('theme')
            ->addSelect('domain')
            ->andWhere('ecole.active = :active')
            ->andWhere('theme.actif = :themeActive')
            ->andWhere('theme.code = :themeCode')
            ->setParameter('active', true)
            ->setParameter('themeActive', true)
            ->setParameter('themeCode', $themeCode)
            ->orderBy('ecole.createdAt', 'ASC')
            ->setMaxResults(1)
            ->getQuery()
            ->getOneOrNullResult();
    }

    /**
     * @return list<Ecole>
     */
    public function findActivePilots(int $limit = 4): array
    {
        return $this->createQueryBuilder('ecole')
            ->leftJoin('ecole.themePublic', 'theme')
            ->leftJoin('ecole.tenantDomains', 'domain')
            ->addSelect('theme')
            ->addSelect('domain')
            ->andWhere('ecole.active = :active')
            ->setParameter('active', true)
            ->orderBy('ecole.createdAt', 'ASC')
            ->setMaxResults($limit)
            ->getQuery()
            ->getResult();
    }

    /**
     * @return list<Ecole>
     */
    public function findAllActiveWithDomains(): array
    {
        return $this->createQueryBuilder('ecole')
            ->leftJoin('ecole.themePublic', 'theme')
            ->leftJoin('ecole.tenantDomains', 'domain')
            ->addSelect('theme')
            ->addSelect('domain')
            ->andWhere('ecole.active = :active')
            ->setParameter('active', true)
            ->orderBy('ecole.createdAt', 'ASC')
            ->getQuery()
            ->getResult();
    }

    /**
     * @return list<Ecole>
     */
    public function findRecent(int $limit = 5): array
    {
        return $this->createQueryBuilder('ecole')
            ->leftJoin('ecole.themePublic', 'theme')
            ->leftJoin('ecole.tenantDomains', 'domain')
            ->addSelect('theme')
            ->addSelect('domain')
            ->orderBy('ecole.createdAt', 'DESC')
            ->setMaxResults($limit)
            ->getQuery()
            ->getResult();
    }

    private function subdomainFromHost(string $host, string $publicDomain): ?string
    {
        $publicDomain = TenantDomain::normalizeHost($publicDomain);

        if ($publicDomain !== '' && $host !== $publicDomain && str_ends_with($host, '.'.$publicDomain)) {
            $subdomain = substr($host, 0, -mb_strlen('.'.$publicDomain));

            return $subdomain !== '' ? $subdomain : null;
        }

        $parts = explode('.', $host);

        if (count($parts) >= 2 && end($parts) === 'localhost' && $parts[0] !== 'www') {
            return $parts[0];
        }

        if (count($parts) > 2 && $parts[0] !== 'www') {
            return $parts[0];
        }

        return null;
    }

    private function normalizePublicIdentifier(string $identifier): string
    {
        return preg_replace('/[^a-z0-9]/', '', mb_strtolower(trim($identifier))) ?? '';
    }
}
