<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Services\Import;

use Chill\MainBundle\Repository\CountryRepository;
use Chill\MainBundle\Repository\PostalCodeRepository;
use Chill\MainBundle\Service\Import\PostalCodeBaseImporter;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

/**
 * @internal
 *
 * @coversNothing
 */
final class PostalCodeBaseImporterTest extends KernelTestCase
{
    private CountryRepository $countryRepository;

    private EntityManagerInterface $entityManager;

    private PostalCodeBaseImporter $importer;

    private PostalCodeRepository $postalCodeRepository;

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

        self::bootKernel();

        $this->entityManager = self::getContainer()->get(EntityManagerInterface::class);
        $this->importer = self::getContainer()->get(PostalCodeBaseImporter::class);
        $this->postalCodeRepository = self::getContainer()->get(PostalCodeRepository::class);
        $this->countryRepository = self::getContainer()->get(CountryRepository::class);
    }

    public function testImportPostalCode(): void
    {
        $this->importer->importCode(
            'BE',
            'tested with pattern '.($uniqid = uniqid()),
            '12345',
            $refPostalCodeId = 'test'.uniqid(),
            'test',
            50.0,
            5.0,
            4326
        );

        $this->importer->finalize();

        $postalCodes = $this->postalCodeRepository->findByPattern(
            'with pattern '.$uniqid,
            $this->countryRepository->findOneBy(['countryCode' => 'BE'])
        );

        $this->assertCount(1, $postalCodes);
        $this->assertStringStartsWith('tested with pattern', $postalCodes[0]->getName());

        $previousId = $postalCodes[0]->getId();

        $this->entityManager->clear();

        $this->importer->importCode(
            'BE',
            'tested with adapted pattern '.($uniqid = uniqid()),
            '12345',
            $refPostalCodeId,
            'test',
            50.0,
            5.0,
            4326
        );

        $this->importer->finalize();

        $postalCodes = $this->postalCodeRepository->findByPattern(
            'with pattern '.$uniqid,
            $this->countryRepository->findOneBy(['countryCode' => 'BE'])
        );

        $this->assertCount(1, $postalCodes);
        $this->assertStringStartsWith('tested with adapted pattern', $postalCodes[0]->getName());
        $this->assertEquals($previousId, $postalCodes[0]->getId());
    }

    public function testPostalCodeRemoval(): void
    {
        $source = 'removal_test_'.uniqid();
        $refId1 = 'ref1_'.uniqid();
        $refId2 = 'ref2_'.uniqid();

        // 1. Import two postal codes
        $this->importer->importCode('BE', 'Label 1', '1000', $refId1, $source, 50.0, 5.0, 4326);
        $this->importer->importCode('BE', 'Label 2', '2000', $refId2, $source, 50.0, 5.0, 4326);
        $this->importer->finalize();

        $pc1 = $this->postalCodeRepository->findOneBy(['refPostalCodeId' => $refId1, 'postalCodeSource' => $source]);
        $pc2 = $this->postalCodeRepository->findOneBy(['refPostalCodeId' => $refId2, 'postalCodeSource' => $source]);

        $this->assertNotNull($pc1);
        $this->assertNotNull($pc2);

        // 2. Import only the first one
        $this->importer->importCode('BE', 'Label 1 updated', '1000', $refId1, $source, 50.0, 5.0, 4326);
        $this->importer->finalize();

        $this->entityManager->clear();

        $pc1 = $this->postalCodeRepository->findOneBy(['refPostalCodeId' => $refId1, 'postalCodeSource' => $source]);
        $pc2 = $this->postalCodeRepository->findOneBy(['refPostalCodeId' => $refId2, 'postalCodeSource' => $source]);

        $this->assertNotNull($pc1);
        $this->assertEquals('Label 1 updated', $pc1->getName());

        $this->assertFalse($pc1->isDeleted(), 'pc1 should NOT be marked as deleted');

        // pc2 should be marked as deleted. Note: findOneBy might still find it if it doesn't filter by deletedAt
        $this->assertNotNull($pc2);

        $this->assertTrue($pc2->isDeleted(), 'Postal code should be marked as deleted (deletedAt is not null)');

        // 3. Reactivate pc2 by re-importing it
        $this->importer->importCode('BE', 'Label 2 restored', '2000', $refId2, $source, 50.0, 5.0, 4326);
        $this->importer->finalize();

        $this->entityManager->clear();
        $pc2 = $this->postalCodeRepository->findOneBy(['refPostalCodeId' => $refId2, 'postalCodeSource' => $source]);
        $this->assertFalse($pc2->isDeleted(), 'Postal code should NOT be marked as deleted after restoration');
        $this->assertEquals('Label 2 restored', $pc2->getName());
    }

    public function testNoInterferenceBetweenSources(): void
    {
        $source1 = 'source1_'.uniqid();
        $source2 = 'source2_'.uniqid();
        $refId1 = 'ref1_'.uniqid();
        $refId2 = 'ref2_'.uniqid();

        // 1. Import from source1
        $this->importer->importCode('BE', 'Label 1', '1000', $refId1, $source1, 50.0, 5.0, 4326);
        $this->importer->finalize();

        $pc1 = $this->postalCodeRepository->findOneBy(['refPostalCodeId' => $refId1, 'postalCodeSource' => $source1]);
        $this->assertNotNull($pc1);
        $this->assertFalse($pc1->isDeleted());

        // 2. Import from source2
        $this->importer->importCode('BE', 'Label 2', '2000', $refId2, $source2, 50.0, 5.0, 4326);
        $this->importer->finalize();

        $this->entityManager->clear();

        $pc1 = $this->postalCodeRepository->findOneBy(['refPostalCodeId' => $refId1, 'postalCodeSource' => $source1]);
        $pc2 = $this->postalCodeRepository->findOneBy(['refPostalCodeId' => $refId2, 'postalCodeSource' => $source2]);

        $this->assertNotNull($pc1);
        $this->assertNotNull($pc2);
        $this->assertFalse($pc1->isDeleted(), 'pc1 from source1 should NOT be deleted after import from source2');
        $this->assertFalse($pc2->isDeleted(), 'pc2 from source2 should NOT be deleted');
    }
}
