<?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\Cart\LegacyOrderImport;

use CpCreation\VitiCore\Cart\LegacyOrderImport\LegacyOrderImporter;
use CpCreation\VitiCore\Cart\LegacyOrderImport\LegacyOrderImportPlan;
use CpCreation\VitiCore\Cart\LegacyOrderImport\LegacyOrderSourceReader;
use CpCreation\VitiCore\Cart\LegacyOrderImport\LegacyPricingSnapshotBuilder;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DriverManager;
use PHPUnit\Framework\TestCase;

final class LegacyOrderImporterApplyTest extends TestCase
{
    private Connection $connection;
    private LegacyOrderImporter $importer;

    protected function setUp(): void
    {
        $this->connection = DriverManager::getConnection([
            'driver' => 'pdo_sqlite',
            'memory' => true,
        ]);
        $this->createSchema();
        $this->importer = new LegacyOrderImporter(
            $this->connection,
            new LegacyOrderSourceReader(),
            new LegacyPricingSnapshotBuilder(),
        );
    }

    protected function tearDown(): void
    {
        $this->connection->close();
    }

    public function testFirstImportIsUniqueAndForceRetriesWithoutDuplicatingTheOrder(): void
    {
        $plan = $this->plan();

        $firstResult = $this->importer->apply($plan, force: false);

        self::assertSame(1, $firstResult['imported']);
        self::assertSame(0, $firstResult['skipped']);
        self::assertSame(4200, $firstResult['total_ttc']);
        self::assertSame(1, $this->countRows('cart_legacy_order_import_run'));
        self::assertSame(1, $this->countRows('cart_legacy_order_import_map'));
        self::assertSame(1, $this->countRows('cart_cart'));
        self::assertSame(1, $this->countRows('cart_cart_item'));
        self::assertSame(1, $this->countRows('cart_cart_checkout'));

        try {
            $this->importer->apply($plan, force: false);
            self::fail('Un second import sans --force aurait du etre refuse.');
        } catch (\RuntimeException $exception) {
            self::assertStringContainsString('Utilisez --force', $exception->getMessage());
        }

        self::assertSame(1, (int) $this->connection->fetchOne(
            'SELECT attempt_count FROM cart_legacy_order_import_run WHERE source_key = :source_key',
            ['source_key' => $plan->sourceKey],
        ));

        $forcedResult = $this->importer->apply($plan, force: true);

        self::assertSame($firstResult['run_id'], $forcedResult['run_id']);
        self::assertSame(0, $forcedResult['imported']);
        self::assertSame(1, $forcedResult['skipped']);
        self::assertSame(2, (int) $this->connection->fetchOne(
            'SELECT attempt_count FROM cart_legacy_order_import_run WHERE source_key = :source_key',
            ['source_key' => $plan->sourceKey],
        ));
        self::assertSame(1, (int) $this->connection->fetchOne(
            'SELECT imported_count FROM cart_legacy_order_import_run WHERE source_key = :source_key',
            ['source_key' => $plan->sourceKey],
        ));
        self::assertSame(1, (int) $this->connection->fetchOne(
            'SELECT skipped_count FROM cart_legacy_order_import_run WHERE source_key = :source_key',
            ['source_key' => $plan->sourceKey],
        ));
        self::assertSame(1, $this->countRows('cart_legacy_order_import_map'));
        self::assertSame(1, $this->countRows('cart_cart'));
        self::assertSame(1, $this->countRows('cart_cart_item'));
        self::assertSame(1, $this->countRows('cart_cart_checkout'));
    }

    public function testForceCannotChangeTheScopeOfAnExistingImport(): void
    {
        $this->importer->apply($this->plan('paid'), force: false);

        try {
            $this->importer->apply($this->plan('all'), force: true);
            self::fail('Une reprise forcee avec un autre scope aurait du etre refusee.');
        } catch (\RuntimeException $exception) {
            self::assertStringContainsString('conserver exactement le meme --scope', $exception->getMessage());
        }

        self::assertSame(1, (int) $this->connection->fetchOne(
            'SELECT attempt_count FROM cart_legacy_order_import_run',
        ));
        self::assertSame('paid', $this->connection->fetchOne(
            'SELECT scope FROM cart_legacy_order_import_run',
        ));
        self::assertSame(1, $this->countRows('cart_legacy_order_import_map'));
    }

    public function testAnotherSourceAliasCannotDuplicateAnAlreadyImportedCart(): void
    {
        $this->importer->apply($this->plan(sourceKey: str_repeat('a', 64)), force: false);

        try {
            $this->importer->apply($this->plan(sourceKey: str_repeat('c', 64)), force: false);
            self::fail('Un autre alias de source aurait du rencontrer les UUID cibles deja importes.');
        } catch (\RuntimeException $exception) {
            self::assertStringContainsString('Collision avec une commande cible non tracee', $exception->getMessage());
        }

        self::assertSame(1, $this->countRows('cart_legacy_order_import_run'));
        self::assertSame(1, $this->countRows('cart_legacy_order_import_map'));
        self::assertSame(1, $this->countRows('cart_cart'));
    }

    public function testForceDoesNotRequireStockAcknowledgementWhenEveryOrderIsSkipped(): void
    {
        $plan = $this->plan(stockImpactQuantity: 2);

        $this->importer->apply($plan, force: false, allowStockImpact: true);
        $result = $this->importer->apply($plan, force: true, allowStockImpact: false);

        self::assertSame(0, $result['imported']);
        self::assertSame(1, $result['skipped']);
        self::assertSame(1, $this->countRows('cart_cart'));
    }

    private function plan(
        string $scope = 'all',
        ?string $sourceKey = null,
        int $stockImpactQuantity = 0,
    ): LegacyOrderImportPlan {
        $createdAt = new \DateTimeImmutable('2024-01-15 10:30:00');
        $cartId = '11111111-1111-4111-8111-111111111111';

        return new LegacyOrderImportPlan(
            sourceKey: $sourceKey ?? str_repeat('a', 64),
            sourceLabel: 'legacy_shop',
            scope: $scope,
            records: [[
                'source_cart_id' => 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
                'source_checkout_id' => 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
                'source_checksum' => str_repeat('b', 64),
                'cart' => [
                    'id' => $cartId,
                    'delivery_method_id' => null,
                    'invoice_address_id' => null,
                    'delivery_address_id' => null,
                    'user_id' => null,
                    'invoice_id' => null,
                    'token' => 'legacy-token',
                    'tax' => '0.20',
                    'group_code' => null,
                    'view_ht' => false,
                    'comment' => null,
                    'delivery' => false,
                    'cgv' => true,
                    'locale' => 'fr',
                    'code_carrier' => null,
                    'url_de_suivi_carrier' => null,
                    'tracking_carrier' => null,
                    'ip_client' => null,
                    'document_type' => 'order',
                    'expires_at' => $createdAt->modify('+3 days'),
                    'cart_pricing' => ['totalTTC' => 4200],
                    'cart_shipping' => ['amountTTC' => 0],
                    'cart_promos' => ['items' => []],
                    'promo_code_input' => null,
                    'promo_code_status' => null,
                    'promo_code_message_key' => null,
                    'deleted_at' => null,
                    'created_at' => $createdAt,
                    'updated_at' => $createdAt,
                    'marketing_attribution' => null,
                    'last_activity_at' => $createdAt,
                    'abandoned_at' => null,
                    'fulfilled_at' => null,
                ],
                'items' => [[
                    'id' => '22222222-2222-4222-8222-222222222222',
                    'cart_cart_id' => $cartId,
                    'product_variant_id' => null,
                    'quantity' => 2,
                    'selected_option_name' => null,
                    'selected_option_price' => null,
                    'selected_option_flat_fee' => false,
                    'sku_snapshot' => 'LEGACY-CUVEE',
                    'created_at' => $createdAt,
                    'updated_at' => $createdAt,
                ]],
                'delivery_address' => null,
                'invoice_address' => null,
                'checkout' => [
                    'id' => '33333333-3333-4333-8333-333333333333',
                    'payment_method_id' => null,
                    'cart_cart_id' => $cartId,
                    'payed_at' => $createdAt,
                    'initiated_at' => $createdAt,
                    'ordered_at' => $createdAt,
                    'payed_token' => 'legacy-payment-token',
                    'response' => ['legacy_order_import' => ['source' => 'viti-core-master']],
                    'marketing_attribution' => null,
                    'disabled' => false,
                    'deleted_at' => null,
                    'created_at' => $createdAt,
                    'updated_at' => $createdAt,
                ],
                'total_ttc' => 4200,
                'mapped_lines' => 0,
                'detached_lines' => 1,
                'stock_impact_quantity' => $stockImpactQuantity,
            ]],
            warnings: [],
            errors: [],
            summary: [
                'orders' => 1,
                'paid' => 1,
                'unpaid' => 0,
                'lines' => 1,
                'quantity' => 2,
                'total_ttc' => 4200,
                'mapped_lines' => 0,
                'detached_lines' => 1,
                'stock_impact_quantity' => $stockImpactQuantity,
                'gift_cards' => 0,
                'warnings' => 0,
                'errors' => 0,
            ],
        );
    }

    private function createSchema(): void
    {
        $statements = [
            <<<'SQL'
                CREATE TABLE cart_legacy_order_import_run (
                    id TEXT PRIMARY KEY, source_key TEXT UNIQUE NOT NULL, source_label TEXT NOT NULL, scope TEXT NOT NULL,
                    status TEXT NOT NULL, attempt_count INTEGER NOT NULL, source_count INTEGER NOT NULL,
                    imported_count INTEGER NOT NULL, skipped_count INTEGER NOT NULL, total_ttc INTEGER NOT NULL,
                    report TEXT NOT NULL, started_at TEXT NOT NULL, completed_at TEXT NULL
                )
                SQL,
            <<<'SQL'
                CREATE TABLE cart_legacy_order_import_map (
                    id TEXT PRIMARY KEY, run_id TEXT NOT NULL, source_key TEXT NOT NULL, source_checkout_id TEXT NOT NULL,
                    source_cart_id TEXT NOT NULL, target_cart_id TEXT NOT NULL, source_checksum TEXT NOT NULL,
                    imported_at TEXT NOT NULL, UNIQUE (source_key, source_cart_id)
                )
                SQL,
            <<<'SQL'
                CREATE TABLE cart_cart (
                    id TEXT PRIMARY KEY, delivery_method_id TEXT NULL, invoice_address_id TEXT NULL,
                    delivery_address_id TEXT NULL, user_id TEXT NULL, invoice_id TEXT NULL, token TEXT NULL,
                    tax TEXT NULL, group_code TEXT NULL, view_ht INTEGER NULL, comment TEXT NULL, delivery INTEGER NOT NULL,
                    cgv INTEGER NOT NULL, locale TEXT NOT NULL, code_carrier TEXT NULL, url_de_suivi_carrier TEXT NULL,
                    tracking_carrier TEXT NULL, ip_client TEXT NULL, document_type TEXT NOT NULL, expires_at TEXT NOT NULL,
                    cart_pricing TEXT NULL, cart_shipping TEXT NULL, cart_promos TEXT NULL, promo_code_input TEXT NULL,
                    promo_code_status TEXT NULL, promo_code_message_key TEXT NULL, deleted_at TEXT NULL,
                    created_at TEXT NOT NULL, updated_at TEXT NOT NULL, marketing_attribution TEXT NULL,
                    last_activity_at TEXT NULL, abandoned_at TEXT NULL, fulfilled_at TEXT NULL
                )
                SQL,
            <<<'SQL'
                CREATE TABLE cart_cart_item (
                    id TEXT PRIMARY KEY, cart_cart_id TEXT NOT NULL, product_variant_id TEXT NULL, quantity INTEGER NOT NULL,
                    selected_option_name TEXT NULL, selected_option_price INTEGER NULL,
                    selected_option_flat_fee INTEGER NOT NULL, sku_snapshot TEXT NOT NULL,
                    created_at TEXT NOT NULL, updated_at TEXT NOT NULL
                )
                SQL,
            <<<'SQL'
                CREATE TABLE cart_cart_checkout (
                    id TEXT PRIMARY KEY, payment_method_id TEXT NULL, cart_cart_id TEXT NOT NULL, payed_at TEXT NULL,
                    initiated_at TEXT NULL, ordered_at TEXT NULL, payed_token TEXT NULL, response TEXT NULL,
                    marketing_attribution TEXT NULL, disabled INTEGER NOT NULL, deleted_at TEXT NULL,
                    created_at TEXT NOT NULL, updated_at TEXT NOT NULL
                )
                SQL,
            'CREATE TABLE cart_delivery_address (id TEXT PRIMARY KEY)',
            'CREATE TABLE cart_invoice_address (id TEXT PRIMARY KEY, different_address INTEGER NOT NULL)',
        ];

        foreach ($statements as $statement) {
            $this->connection->executeStatement($statement);
        }
    }

    private function countRows(string $table): int
    {
        return (int) $this->connection->fetchOne(\sprintf('SELECT COUNT(*) FROM %s', $table));
    }
}
