<?php

namespace App\Repository;

use App\Entity\SpecialistView;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\ORM\QueryBuilder;

class SpecialistViewRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, SpecialistView::class);
    }

    public function createFilteredQueryBuilder(array $filters): QueryBuilder
    {
        $qb = $this->createQueryBuilder('s')
            ->andWhere('s.active = true');

        $this->applyFilters($qb, $filters);
        $this->applyOrderBy($qb, $filters['order_by'] ?? null);

        return $qb;
    }

    private function applyFilters(QueryBuilder $qb, array $filters): void
    {
        foreach ($filters as $key => $value) {
            if (empty($value)) {
                continue;
            }

            match ($key) {
                'onlineMode' => $this->applyOnlineModeFilter($qb, $value),
                'regionId' => $this->applyRegionFilter($qb, $value),
                'alias' => $this->applyAliasFilter($qb, $value),
                'filial' => $this->applyFilialFilter($qb, $value),
                'dcode' => $this->applyDcodeFilter($qb, $value),
                'kinder' => $this->applyKinderFilter($qb, $value),
                'category' => $this->applyCategoryFilter($qb, $value),
                'department' => $this->applyDepartmentFilter($qb, $value),
                'depAlias' => $this->applyDepAliasFilter($qb, $value),
                'name' => $this->applyNameFilter($qb, $value),
                'current_date' => $this->applyCurrentDateFilter($qb, $value),
                default => null // Игнорируем неизвестные фильтры
            };
        }
    }

    private function applyKinderFilter(QueryBuilder $qb, $sType): void
    {
        $sType = match (true) {
            is_array($sType) => array_map('intval', $sType),
            is_string($sType) => [(int) $sType],
            is_numeric($sType) => [$sType],
            default => []
        };

        $qb->andWhere('s.sType in (:sType)')
            ->setParameter('sType', $sType);
    }

    private function applyOnlineModeFilter(QueryBuilder $qb, bool $onlineMode): void
    {
        $qb->andWhere('EXISTS (
            SELECT 1 FROM App\Entity\LocationView l 
            WHERE l.specialistId = s.id AND l.onlineMode = :onlineMode
        )')->setParameter('onlineMode', $onlineMode);
    }

    private function applyRegionFilter(QueryBuilder $qb, $regionId): void
    {
        $qb->andWhere('s.regionId in (:regionId)')
            ->setParameter('regionId', $regionId);
    }

    private function applyAliasFilter(QueryBuilder $qb, string $alias): void
    {
        $qb->andWhere('s.alias = :alias')
            ->setParameter('alias', $alias);
    }

    private function applyFilialFilter(QueryBuilder $qb, $filial): void
    {
        $filialIds = array_map('intval', (array)$filial);
        
        $qb->andWhere('EXISTS (
            SELECT 1 FROM App\Entity\LocationView l2 
            WHERE l2.specialistId = s.id AND l2.filial in (:filial)
        )')->setParameter('filial', $filialIds);
    }

    private function applyDcodeFilter(QueryBuilder $qb, $dcode): void
    {
        $dcode = match (true) {
            is_array($dcode) => array_map('intval', $dcode),
            is_string($dcode) => [(int) $dcode],
            is_numeric($dcode) => [$dcode],
            default => []
        };

        if (!empty($dcode)) {
            $qb->andWhere('EXISTS (
                SELECT 1 FROM App\Entity\LocationView l3 
                WHERE l3.specialistId = s.id AND l3.dcode in (:dcode)
            )')->setParameter('dcode', $dcode);
        }
    }

    private function applyCategoryFilter(QueryBuilder $qb, $category): void
    {
        $qb->andWhere('s.category = :category')
            ->setParameter('category', $category);
    }

    private function applyDepartmentFilter(QueryBuilder $qb, $department): void
    {
        $qb->andWhere('EXISTS (
            SELECT 1 FROM App\Entity\LocationView l4 
            WHERE l4.specialistId = s.id AND l4.department = :department
        )')->setParameter('department', (int) $department);
    }

    private function applyDepAliasFilter(QueryBuilder $qb, string $depAlias): void
    {
        $qb->andWhere('EXISTS (
            SELECT 1 FROM App\Entity\LocationView l5 
            JOIN App\Entity\Department d WITH d.did = l5.department
            WHERE l5.specialistId = s.id AND d.alias = :depAlias
        )')->setParameter('depAlias', $depAlias);
    }

    private function applyNameFilter(QueryBuilder $qb, string $name): void
    {
        $qb->andWhere($qb->expr()->like('LOWER(s.name)', 'LOWER(:search)'))
           ->setParameter('search', '%' . trim($name) . '%');
    }

    private function applyCurrentDateFilter(QueryBuilder $qb, $currentDate): void
    {
        if (empty($currentDate)) {
            return;
        }

        // Парсим дату: формат "13.01.2026 - 20.01.2026" (с пробелами вокруг дефиса)
        // Также поддерживаем варианты: "13.01.2026+-+20.01.2026" или "13.01.2026-20.01.2026"
        $dateString = trim($currentDate);
        
        // Разделяем по дефису с пробелами или без
        // Используем регулярное выражение для более точного парсинга
        if (preg_match('/^(.+?)\s*[-+]\s*(.+)$/', $dateString, $matches)) {
            $startDateStr = trim($matches[1]);
            $endDateStr = trim($matches[2]);
        } else {
            // Если не удалось распарсить, пробуем просто разделить по дефису
            $parts = preg_split('/\s*[-+]\s*/', $dateString, 2);
            if (count($parts) < 2) {
                return;
            }
            $startDateStr = trim($parts[0]);
            $endDateStr = trim($parts[1]);
        }

        // Парсим дату из формата "dd.mm.yyyy" или "yyyy-mm-dd"
        $startDate = $this->parseDate($startDateStr);
        $endDate = $this->parseDate($endDateStr);

        if (!$startDate || !$endDate) {
            return;
        }

        // Фильтруем специалистов, у которых есть локации с nearestDate в указанном диапазоне
        $qb->andWhere('EXISTS (
            SELECT 1 FROM App\Entity\LocationView l_date 
            WHERE l_date.specialistId = s.id 
            AND l_date.active = true
            AND l_date.nearestDate >= :startDate 
            AND l_date.nearestDate <= :endDate
        )')
        ->setParameter('startDate', $startDate)
        ->setParameter('endDate', $endDate);
    }

    private function parseDate(string $dateStr): ?\DateTime
    {
        $dateStr = trim($dateStr);
        
        // Пробуем формат "dd.mm.yyyy"
        if (preg_match('/^(\d{1,2})\.(\d{1,2})\.(\d{4})$/', $dateStr, $matches)) {
            $day = (int)$matches[1];
            $month = (int)$matches[2];
            $year = (int)$matches[3];
            try {
                return new \DateTime(sprintf('%04d-%02d-%02d', $year, $month, $day));
            } catch (\Exception $e) {
                return null;
            }
        }
        
        // Пробуем формат "yyyy-mm-dd"
        if (preg_match('/^(\d{4})-(\d{1,2})-(\d{1,2})$/', $dateStr, $matches)) {
            $year = (int)$matches[1];
            $month = (int)$matches[2];
            $day = (int)$matches[3];
            try {
                return new \DateTime(sprintf('%04d-%02d-%02d', $year, $month, $day));
            } catch (\Exception $e) {
                return null;
            }
        }
        
        // Пробуем стандартный формат
        try {
            return new \DateTime($dateStr);
        } catch (\Exception $e) {
            return null;
        }
    }

    private function applyOrderBy(QueryBuilder $qb, ?string $orderBy): void
    {
        $orderConfig = match ($orderBy) {
            'sort-abc.asc' => ['field' => 's.name', 'direction' => 'ASC'],
            'sort-abc.desc' => ['field' => 's.name', 'direction' => 'DESC'],
            'sort-time.asc' => $this->createTimeOrderConfig('ASC'),
            'sort-time.desc' => $this->createTimeOrderConfig('DESC'),
            default => ['field' => 's.name', 'direction' => 'ASC']
        };

        if (isset($orderConfig['join'])) {
            $qb->leftJoin('App\Entity\LocationView', 'l_sort', 'WITH', 'l_sort.specialistId = s.id');
        }

        $qb->addOrderBy($orderConfig['field'], $orderConfig['direction']);
    }

    private function createTimeOrderConfig(string $direction): array
    {
        return [
            'field' => 'l_sort.nearestDate',
            'direction' => $direction,
            'join' => true
        ];
    }
}