<?php

declare(strict_types=1);

namespace App\Tests\Unit\Service;

use App\Module\Mail\Application\Dto\MailFolderDto;
use App\Module\Mail\Application\Service\MailSyncService;
use App\Module\Mail\Domain\Entity\MailConfiguration;
use App\Module\Mail\Domain\Entity\MailFolder;
use App\Module\Mail\Domain\Provider\MailProviderInterface;
use App\Module\Mail\Domain\Repository\MailConfigurationRepositoryInterface;
use App\Module\Mail\Domain\Repository\MailFolderRepositoryInterface;
use App\Module\Mail\Domain\Repository\MailMessageRepositoryInterface;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry;
use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\SharedLockInterface;

/**
 * @internal
 */
class MailSyncServiceTest extends TestCase
{
    public function testSyncAllReturnsEmptyReportWhenConfigDisabled(): void
    {
        $config = new MailConfiguration();
        $config->setEnabled(false);

        $configRepo = $this->createMock(MailConfigurationRepositoryInterface::class);
        $configRepo->method('findSingleton')->willReturn($config);

        $provider    = $this->createMock(MailProviderInterface::class);
        $folderRepo  = $this->createMock(MailFolderRepositoryInterface::class);
        $messageRepo = $this->createMock(MailMessageRepositoryInterface::class);
        $em          = $this->createMock(EntityManagerInterface::class);
        $lockFactory = $this->makeLockFactory();

        $service = new MailSyncService(
            provider: $provider,
            configRepository: $configRepo,
            folderRepository: $folderRepo,
            messageRepository: $messageRepo,
            entityManager: $em,
            lockFactory: $lockFactory,
            logger: new NullLogger(),
            managerRegistry: $this->createMock(ManagerRegistry::class),
        );

        $report = $service->syncAll();

        self::assertSame(0, $report->createdCount);
        self::assertSame(0, $report->updatedCount);
        self::assertSame(0, $report->deletedCount);
        self::assertSame(0, $report->foldersScanned);
    }

    public function testSyncAllReturnsEmptyReportWhenLockNotAcquired(): void
    {
        $config = new MailConfiguration();
        $config->setEnabled(true);

        $configRepo = $this->createMock(MailConfigurationRepositoryInterface::class);
        $configRepo->method('findSingleton')->willReturn($config);

        $provider    = $this->createMock(MailProviderInterface::class);
        $folderRepo  = $this->createMock(MailFolderRepositoryInterface::class);
        $messageRepo = $this->createMock(MailMessageRepositoryInterface::class);
        $em          = $this->createMock(EntityManagerInterface::class);
        $lockFactory = $this->makeLockFactory(false);

        $service = new MailSyncService(
            provider: $provider,
            configRepository: $configRepo,
            folderRepository: $folderRepo,
            messageRepository: $messageRepo,
            entityManager: $em,
            lockFactory: $lockFactory,
            logger: new NullLogger(),
            managerRegistry: $this->createMock(ManagerRegistry::class),
        );

        $report = $service->syncAll();

        self::assertSame(0, $report->createdCount);
        self::assertContains('lock_not_acquired', $report->errors);
    }

    public function testSyncFolderStructureCreatesNewFolders(): void
    {
        $config = new MailConfiguration();
        $config->setEnabled(true);

        $configRepo = $this->createMock(MailConfigurationRepositoryInterface::class);
        $configRepo->method('findSingleton')->willReturn($config);

        $folderDto = new MailFolderDto(
            path: 'INBOX',
            displayName: 'Inbox',
            parentPath: null,
            unreadCount: 5,
            totalCount: 42,
        );

        $provider = $this->createMock(MailProviderInterface::class);
        $provider->method('listFolders')->willReturn([$folderDto]);

        $folderRepo = $this->createMock(MailFolderRepositoryInterface::class);
        $folderRepo->method('findByPath')->willReturn(null);
        $folderRepo->method('findAllOrderedByPath')->willReturn([]);

        $messageRepo = $this->createMock(MailMessageRepositoryInterface::class);
        $em          = $this->createMock(EntityManagerInterface::class);
        $em->expects(self::once())->method('persist');
        $em->expects(self::once())->method('flush');

        $lockFactory = $this->makeLockFactory();

        $service = new MailSyncService(
            provider: $provider,
            configRepository: $configRepo,
            folderRepository: $folderRepo,
            messageRepository: $messageRepo,
            entityManager: $em,
            lockFactory: $lockFactory,
            logger: new NullLogger(),
            managerRegistry: $this->createMock(ManagerRegistry::class),
        );

        $service->syncFolderStructure();
    }

    public function testSyncFolderAbortsSuppressionWhenOver50Percent(): void
    {
        $config = new MailConfiguration();
        $config->setEnabled(true);

        $configRepo = $this->createMock(MailConfigurationRepositoryInterface::class);
        $configRepo->method('findSingleton')->willReturn($config);

        $folder = new MailFolder();
        $folder->setPath('INBOX');

        $messageRepo = $this->createMock(MailMessageRepositoryInterface::class);
        $messageRepo->method('findMaxUidInFolder')->willReturn(10);
        $messageRepo->method('findAllUidsByFolder')->willReturn([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
        $messageRepo->method('findLastNByFolder')->willReturn([]);

        $provider = $this->createMock(MailProviderInterface::class);
        $provider->method('listMessages')->willReturn([]);

        $folderRepo = $this->createMock(MailFolderRepositoryInterface::class);
        $em         = $this->createMock(EntityManagerInterface::class);
        $em->expects(self::never())->method('remove');

        $lockFactory = $this->makeLockFactory();

        $service = new MailSyncService(
            provider: $provider,
            configRepository: $configRepo,
            folderRepository: $folderRepo,
            messageRepository: $messageRepo,
            entityManager: $em,
            lockFactory: $lockFactory,
            logger: new NullLogger(),
            managerRegistry: $this->createMock(ManagerRegistry::class),
        );

        $report = $service->syncFolder($folder);

        self::assertSame(0, $report->deletedCount);
        self::assertNotEmpty($report->errors);
    }

    private function makeLockFactory(bool $acquired = true): LockFactory
    {
        $lock = $this->createMock(SharedLockInterface::class);
        $lock->method('acquire')->willReturn($acquired);

        $factory = $this->createMock(LockFactory::class);
        $factory->method('createLock')->willReturn($lock);

        return $factory;
    }
}
