<?php

/*
 * This file is part of the VITICORE2 package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Tests\Provider\Carrier;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Provider\Carrier\CarrierTableTruncator;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use PHPUnit\Framework\TestCase;

if (!class_exists(
    CarrierTableTruncator::class,
    false,
)) {
    require_once \dirname(__DIR__, 3).'/src/Bridge/Symfony/Bundle/Provider/Carrier/CarrierTableTruncator.php';
}

final class CarrierTableTruncatorTest extends TestCase
{
    public function testItUsesCascadeWithoutMySqlConfigurationOnPostgreSql(): void
    {
        $statements = [];
        $connection = $this->createMock(Connection::class);
        $connection->method('getDatabasePlatform')->willReturn(new PostgreSQLPlatform());
        $connection->method('executeStatement')->willReturnCallback(
            static function (string $sql) use (&$statements): int {
                $statements[] = $sql;

                return 0;
            },
        );

        (new CarrierTableTruncator())->truncate($connection, 'shipping_auto');

        self::assertSame(['TRUNCATE shipping_auto CASCADE'], $statements);
    }

    public function testItTemporarilyDisablesForeignKeysOnMySql(): void
    {
        $statements = [];
        $connection = $this->createMock(Connection::class);
        $connection->method('getDatabasePlatform')->willReturn(new MySQLPlatform());
        $connection->method('executeStatement')->willReturnCallback(
            static function (string $sql) use (&$statements): int {
                $statements[] = $sql;

                return 0;
            },
        );

        (new CarrierTableTruncator())->truncate($connection, 'shipping_auto');

        self::assertSame([
            'SET FOREIGN_KEY_CHECKS=0',
            'TRUNCATE shipping_auto',
            'SET FOREIGN_KEY_CHECKS=1',
        ], $statements);
    }

    public function testItRestoresForeignKeysWhenMySqlTruncateFails(): void
    {
        $statements = [];
        $connection = $this->createMock(Connection::class);
        $connection->method('getDatabasePlatform')->willReturn(new MySQLPlatform());
        $connection->method('executeStatement')->willReturnCallback(
            static function (string $sql) use (&$statements): int {
                $statements[] = $sql;

                if (str_starts_with($sql, 'TRUNCATE')) {
                    throw new \RuntimeException('Truncate failed');
                }

                return 0;
            },
        );

        try {
            (new CarrierTableTruncator())->truncate($connection, 'shipping_auto');
            self::fail('The truncate exception should have been propagated.');
        } catch (\RuntimeException $exception) {
            self::assertSame('Truncate failed', $exception->getMessage());
        }

        self::assertSame([
            'SET FOREIGN_KEY_CHECKS=0',
            'TRUNCATE shipping_auto',
            'SET FOREIGN_KEY_CHECKS=1',
        ], $statements);
    }

    public function testCarrierProvidersDoNotExecuteDatabaseSpecificForeignKeyStatements(): void
    {
        $providerDirectory = \dirname(__DIR__, 3).'/src/Bridge/Symfony/Bundle/Provider/Carrier';
        $providerFiles = glob($providerDirectory.'/*Provider.php');

        self::assertNotFalse($providerFiles);
        self::assertNotEmpty($providerFiles);

        foreach ($providerFiles as $providerFile) {
            $source = file_get_contents($providerFile);

            self::assertNotFalse($source);
            self::assertStringNotContainsString(
                'FOREIGN_KEY_CHECKS',
                $source,
                basename($providerFile).' must use CarrierTableTruncator.',
            );
        }
    }
}
