<?php

declare(strict_types=1);

namespace OCA\Mitgliederverwaltung\Tests\Unit;

use OCA\Mitgliederverwaltung\BackgroundJob\ContactsFullSyncJob;
use OCA\Mitgliederverwaltung\Service\ContactsSyncService;
use OCP\AppFramework\Utility\ITimeFactory;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;

class ContactsFullSyncJobTest extends TestCase {

    private ContactsSyncService&MockObject $contactsSyncService;
    private LoggerInterface&MockObject $logger;
    private ITimeFactory&MockObject $timeFactory;
    private ContactsFullSyncJob $job;

    protected function setUp(): void {
        parent::setUp();

        $this->contactsSyncService = $this->createMock(ContactsSyncService::class);
        $this->logger = $this->createMock(LoggerInterface::class);
        $this->timeFactory = $this->createMock(ITimeFactory::class);

        $this->job = new ContactsFullSyncJob(
            $this->timeFactory,
            $this->contactsSyncService,
            $this->logger
        );
    }

    public function testConstruction(): void {
        $this->assertInstanceOf(ContactsFullSyncJob::class, $this->job);
    }

    public function testRunCallsFullSync(): void {
        $this->contactsSyncService->expects($this->once())
            ->method('fullSync')
            ->willReturn(['created' => 10, 'updated' => 5, 'deleted' => 2]);

        $this->logger->expects($this->once())
            ->method('info')
            ->with(
                'Contacts full sync completed',
                $this->callback(function (array $context) {
                    return $context['created'] === 10
                        && $context['updated'] === 5
                        && $context['deleted'] === 2
                        && $context['app'] === 'mitgliederverwaltung';
                })
            );

        $reflection = new \ReflectionMethod($this->job, 'run');
        $reflection->setAccessible(true);
        $reflection->invoke($this->job, []);
    }

    public function testRunLogsErrorOnException(): void {
        $exception = new \RuntimeException('Contacts sync failed');

        $this->contactsSyncService->expects($this->once())
            ->method('fullSync')
            ->willThrowException($exception);

        $this->logger->expects($this->once())
            ->method('error')
            ->with(
                'Contacts full sync failed',
                $this->callback(function (array $context) use ($exception) {
                    return $context['exception'] === $exception
                        && $context['app'] === 'mitgliederverwaltung';
                })
            );

        $reflection = new \ReflectionMethod($this->job, 'run');
        $reflection->setAccessible(true);
        $reflection->invoke($this->job, []);
    }

    public function testRunHandlesZeroChanges(): void {
        $this->contactsSyncService->expects($this->once())
            ->method('fullSync')
            ->willReturn(['created' => 0, 'updated' => 0, 'deleted' => 0]);

        $this->logger->expects($this->once())
            ->method('info');

        $reflection = new \ReflectionMethod($this->job, 'run');
        $reflection->setAccessible(true);
        $reflection->invoke($this->job, []);
    }
}
