<?php

namespace App\Service;

use App\Entity\User;
use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler;

class UserCleanupService
{
    private $userRepository;
    private $entityManager;
    private $logger;
    private $cookieLifetime;
    private $sessionHandler;

    public function __construct(
        UserRepository $userRepository,
        EntityManagerInterface $entityManager,
        LoggerInterface $logger,
        array $sessionStorageOptions = [],
        $sessionHandler = null
    ) {
        $this->userRepository = $userRepository;
        $this->entityManager = $entityManager;
        $this->logger = $logger;
        $this->cookieLifetime = $sessionStorageOptions['cookie_lifetime'] ?? 0;
        $this->sessionHandler = $sessionHandler;
    }

    /**
     * Удаляет неактивных пользователей с только ROLE_USER после истечения времени жизни кукисов
     *
     * @return int Количество удаленных пользователей
     */
    public function cleanupExpiredUsers(): int
    {
        // Если cookie_lifetime = 0, используем время жизни сессии по умолчанию (1 час)
        // Иначе используем указанное время жизни кукисов
        $sessionLifetime = $this->cookieLifetime > 0 
            ? $this->cookieLifetime 
            : 3600; // 1 час по умолчанию для session cookies

        // Вычисляем дату, до которой должна быть последняя активность пользователя
        $beforeDate = new \DateTime();
        $beforeDate->modify("-{$sessionLifetime} seconds");

        $usersToDelete = $this->userRepository->findUsersToCleanup($beforeDate);
        $deletedCount = 0;

        $this->logger->info(sprintf(
            'Найдено пользователей для проверки: %d (время жизни сессии: %d секунд, дата отсечки: %s)',
            count($usersToDelete),
            $sessionLifetime,
            $beforeDate->format('Y-m-d H:i:s')
        ));

        foreach ($usersToDelete as $user) {
            // Проверяем, что у пользователя только ROLE_USER
            // Если массив ролей пуст, значит у пользователя только ROLE_USER (который добавляется автоматически)
            $actualRoles = $user->getActualRoles();
            $hasOnlyUserRole = empty($actualRoles);

            $this->logger->debug(sprintf(
                'Проверка пользователя ID %d: роли=%s, только ROLE_USER=%s',
                $user->getId(),
                json_encode($actualRoles),
                $hasOnlyUserRole ? 'да' : 'нет'
            ));

            if ($hasOnlyUserRole) {
                // Определяем дату для логирования
                $activityDate = $user->getLastActivityAt() ?? $user->getCreatedAt();
                $activityDateStr = $activityDate ? $activityDate->format('Y-m-d H:i:s') : 'N/A';

                try {
                    // Удаляем сессии пользователя из Redis перед удалением пользователя
                    $this->cleanupUserSessions($user);
                    
                    $this->deleteUserAndRelatedData($user);
                    $deletedCount++;
                    $this->logger->info(sprintf(
                        'Удален неактивный пользователь с ID %d (uid: %d, email: %s, последняя активность: %s)',
                        $user->getId(),
                        $user->getUid(),
                        $user->getEmail() ?? 'N/A',
                        $activityDateStr
                    ));
                } catch (\Exception $e) {
                    $this->logger->error(sprintf(
                        'Ошибка при удалении пользователя с ID %d: %s',
                        $user->getId(),
                        $e->getMessage()
                    ));
                }
            }
        }

        // Очищаем старые сессии из Redis
        $this->cleanupOldRedisSessions($sessionLifetime);

        return $deletedCount;
    }

    /**
     * Удаляет пользователя и все связанные данные
     *
     * @param User $user
     */
    private function deleteUserAndRelatedData(User $user): void
    {
        // Удаляем связанные данные Calltouch (если есть)
        $this->deleteCalltouchData($user);

        // Удаляем сам пользователя
        $this->entityManager->remove($user);
        $this->entityManager->flush();
    }

    /**
     * Очищает сессии пользователя из Redis
     *
     * @param User $user
     */
    private function cleanupUserSessions(User $user): void
    {
        if (!$this->sessionHandler) {
            return;
        }

        try {
            $redis = $this->getRedisClient();
            if (!$redis) {
                return;
            }

            // Ищем все сессии, которые могут принадлежать пользователю
            // В Symfony сессии обычно хранятся с префиксом PHPSESSID или sess_
            $patterns = ['PHPSESSID_*', 'sess_*', '*_symfony_*'];
            $deletedSessions = 0;

            foreach ($patterns as $pattern) {
                $keys = $redis->keys($pattern);
                if (!$keys) {
                    continue;
                }

                foreach ($keys as $key) {
                    try {
                        $sessionData = $redis->get($key);
                        if ($sessionData) {
                            // Пытаемся найти user_id в данных сессии
                            // Сессии Symfony обычно содержат сериализованные данные
                            if (strpos($sessionData, '_security_main') !== false || 
                                strpos($sessionData, (string)$user->getId()) !== false ||
                                strpos($sessionData, (string)$user->getUid()) !== false) {
                                $redis->del($key);
                                $deletedSessions++;
                            }
                        }
                    } catch (\Exception $e) {
                        // Игнорируем ошибки при проверке отдельных ключей
                    }
                }
            }

            if ($deletedSessions > 0) {
                $this->logger->info(sprintf(
                    'Удалено %d сессий из Redis для пользователя %d',
                    $deletedSessions,
                    $user->getId()
                ));
            }
        } catch (\Exception $e) {
            $this->logger->warning(sprintf(
                'Ошибка при очистке сессий пользователя %d из Redis: %s',
                $user->getId(),
                $e->getMessage()
            ));
        }
    }

    /**
     * Очищает старые сессии из Redis
     *
     * @param int $sessionLifetime Время жизни сессии в секундах
     */
    private function cleanupOldRedisSessions(int $sessionLifetime): void
    {
        if (!$this->sessionHandler) {
            return;
        }

        try {
            $redis = $this->getRedisClient();
            if (!$redis) {
                return;
            }

            $patterns = ['PHPSESSID_*', 'sess_*', '*_symfony_*'];
            $deletedCount = 0;
            $now = time();

            foreach ($patterns as $pattern) {
                $keys = $redis->keys($pattern);
                if (!$keys) {
                    continue;
                }

                foreach ($keys as $key) {
                    try {
                        $ttl = $redis->ttl($key);
                        // Если TTL отрицательный (ключ не имеет TTL) или больше времени жизни сессии, пропускаем
                        if ($ttl < 0 || $ttl > $sessionLifetime) {
                            continue;
                        }

                        // Проверяем, не истекла ли сессия
                        $lastAccess = $now - $ttl;
                        if ($lastAccess > $sessionLifetime) {
                            $redis->del($key);
                            $deletedCount++;
                        }
                    } catch (\Exception $e) {
                        // Игнорируем ошибки при проверке отдельных ключей
                    }
                }
            }

            if ($deletedCount > 0) {
                $this->logger->info(sprintf(
                    'Удалено %d старых сессий из Redis',
                    $deletedCount
                ));
            }
        } catch (\Exception $e) {
            $this->logger->warning(sprintf(
                'Ошибка при очистке старых сессий из Redis: %s',
                $e->getMessage()
            ));
        }
    }

    /**
     * Получает Redis клиент из session handler
     *
     * @return \Redis|\Predis\Client|null
     */
    private function getRedisClient()
    {
        if (!$this->sessionHandler) {
            return null;
        }

        try {
            // Пытаемся получить Redis клиент из различных типов session handlers
            if ($this->sessionHandler instanceof \Redis) {
                return $this->sessionHandler;
            }

            if ($this->sessionHandler instanceof \Predis\Client) {
                return $this->sessionHandler;
            }

            // Для RedisSessionHandler из Symfony
            if ($this->sessionHandler instanceof RedisSessionHandler) {
                $reflection = new \ReflectionClass($this->sessionHandler);
                if ($reflection->hasProperty('redis')) {
                    $property = $reflection->getProperty('redis');
                    $property->setAccessible(true);
                    return $property->getValue($this->sessionHandler);
                }
            }

            // Пытаемся вызвать метод getRedis, если он существует
            if (method_exists($this->sessionHandler, 'getRedis')) {
                return $this->sessionHandler->getRedis();
            }

            return null;
        } catch (\Exception $e) {
            $this->logger->warning(sprintf(
                'Не удалось получить Redis клиент: %s',
                $e->getMessage()
            ));
            return null;
        }
    }

    /**
     * Удаляет данные Calltouch, связанные с пользователем
     *
     * @param User $user
     */
    private function deleteCalltouchData(User $user): void
    {
        try {
            // Проверяем наличие связи с Calltouch через миграции
            // Если есть таблица calltouch с внешним ключом на users.id
            $connection = $this->entityManager->getConnection();
            
            // Удаляем записи calltouch, связанные с пользователем
            $connection->executeStatement(
                'DELETE FROM calltouch WHERE user_id = :userId',
                ['userId' => $user->getId()]
            );
        } catch (\Exception $e) {
            // Игнорируем ошибки, если таблица не существует или нет связи
            $this->logger->warning(sprintf(
                'Не удалось удалить данные Calltouch для пользователя %d: %s',
                $user->getId(),
                $e->getMessage()
            ));
        }
    }
}
