<?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\LegacyOrderImportMapping;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

final class LegacyOrderImportMappingTest extends TestCase
{
    public function testLoadsAndNormalizesAValidMapping(): void
    {
        $mapping = $this->mappingFromJson(<<<'JSON'
            {
                "variants": {
                    "  SOURCE-VARIANT  ": "  sku:CUVEE-75  "
                },
                "payment_methods": {
                    "  CREDIT_CARD  ": "  payplug  "
                },
                "delivery_methods": {
                    "  SOURCE-DELIVERY  ": "  target-delivery  "
                }
            }
            JSON);

        self::assertTrue($mapping->hasVariant('source-variant'));
        self::assertSame('sku:CUVEE-75', $mapping->variant('SOURCE-VARIANT'));
        self::assertTrue($mapping->hasPaymentMethod('credit_card'));
        self::assertSame('payplug', $mapping->paymentMethod('CREDIT_CARD'));
        self::assertTrue($mapping->hasDeliveryMethod('source-delivery'));
        self::assertSame('target-delivery', $mapping->deliveryMethod('SOURCE-DELIVERY'));
    }

    public function testPreservesAnExplicitNullAndDistinguishesItFromAMissingEntry(): void
    {
        $mapping = $this->mappingFromJson(<<<'JSON'
            {
                "variants": {"source-variant": null},
                "payment_methods": {"legacy-payment": null},
                "delivery_methods": {"legacy-delivery": null}
            }
            JSON);

        self::assertTrue($mapping->hasVariant('SOURCE-VARIANT'));
        self::assertNull($mapping->variant('source-variant'));
        self::assertFalse($mapping->hasVariant('missing-variant'));
        self::assertNull($mapping->variant('missing-variant'));

        self::assertTrue($mapping->hasPaymentMethod('LEGACY-PAYMENT'));
        self::assertNull($mapping->paymentMethod('legacy-payment'));
        self::assertFalse($mapping->hasPaymentMethod('missing-payment'));

        self::assertTrue($mapping->hasDeliveryMethod('LEGACY-DELIVERY'));
        self::assertNull($mapping->deliveryMethod('legacy-delivery'));
        self::assertFalse($mapping->hasDeliveryMethod('missing-delivery'));
    }

    public function testPreservesTheUnresolvedSentinelForAUserMappingToComplete(): void
    {
        $mapping = $this->mappingFromJson(<<<'JSON'
            {
                "variants": {
                    "source-variant": "__A_COMPLETER__"
                },
                "_source_variants": {
                    "source-variant": {
                        "product_name": "Ancienne cuvee",
                        "type_name": "Bouteille"
                    }
                }
            }
            JSON);

        self::assertSame('__A_COMPLETER__', LegacyOrderImportMapping::UNRESOLVED);
        self::assertTrue($mapping->hasVariant('SOURCE-VARIANT'));
        self::assertSame(LegacyOrderImportMapping::UNRESOLVED, $mapping->variant('source-variant'));
    }

    public function testLoadsAndChecksTheGeneratedFileSourceContext(): void
    {
        $mapping = $this->mappingFromJson(<<<'JSON'
            {
                "schema_version": 1,
                "source": {
                    "source_id": "legacy-source",
                    "source_key": "stable-source-key",
                    "scope": "paid"
                },
                "variants": {},
                "payment_methods": {},
                "delivery_methods": {}
            }
            JSON);

        self::assertSame([], $mapping->contextErrors('legacy-source', 'stable-source-key', 'paid'));
        self::assertCount(3, $mapping->contextErrors('another-source', 'another-key', 'all'));
    }

    #[DataProvider('invalidJsonProvider')]
    public function testRejectsInvalidMappingFiles(string $json, string $expectedMessage): void
    {
        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage($expectedMessage);

        $this->mappingFromJson($json);
    }

    /**
     * @return iterable<string, array{string, string}>
     */
    public static function invalidJsonProvider(): iterable
    {
        yield 'malformed JSON' => ['{"variants":', 'n\'est pas un JSON valide'];
        yield 'non-object root' => ['"variants"', 'doit contenir un objet JSON'];
        yield 'non-object section' => ['{"variants":"source-variant"}', 'cle JSON "variants"'];
        yield 'empty source key' => ['{"variants":{"   ":"target"}}', 'chaine non vide'];
        yield 'non-string target' => ['{"variants":{"source":42}}', 'chaine non vide ou null'];
        yield 'empty target' => ['{"payment_methods":{"legacy":"   "}}', 'chaine non vide ou null'];
        yield 'generated context missing' => [
            '{"schema_version":1,"source":{"scope":"all"}}',
            'doit conserver source.source_id',
        ];
        yield 'generated source object missing' => [
            '{"schema_version":1}',
            'doit conserver source.source_id',
        ];
        yield 'generated source id missing' => [
            '{"schema_version":1,"source":{"source_key":"key","scope":"all"}}',
            'doit conserver source.source_id',
        ];
        yield 'generated source key missing' => [
            '{"schema_version":1,"source":{"source_id":"legacy","scope":"all"}}',
            'doit conserver source.source_key',
        ];
        yield 'generated source scope missing' => [
            '{"schema_version":1,"source":{"source_id":"legacy","source_key":"key"}}',
            'doit conserver source.scope',
        ];
        yield 'generated source is not an object' => [
            '{"schema_version":1,"source":"legacy"}',
            'cle JSON "source" doit contenir un objet',
        ];
        yield 'generated scope invalid' => [
            '{"schema_version":1,"source":{"source_id":"legacy","source_key":"key","scope":"recent"}}',
            'doit valoir "all" ou "paid"',
        ];
        yield 'normalized duplicate' => [
            '{"variants":{"SOURCE":"target-1"," source ":"target-2"}}',
            'definie plusieurs fois apres normalisation',
        ];
    }

    public function testRejectsAMissingFile(): void
    {
        $path = sys_get_temp_dir().'/viti-legacy-mapping-does-not-exist-'.bin2hex(random_bytes(8)).'.json';

        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage('introuvable ou illisible');

        LegacyOrderImportMapping::fromFile($path);
    }

    private function mappingFromJson(string $json): LegacyOrderImportMapping
    {
        $path = tempnam(sys_get_temp_dir(), 'viti-legacy-map-');
        self::assertNotFalse($path);

        try {
            self::assertNotFalse(file_put_contents($path, $json));

            return LegacyOrderImportMapping::fromFile($path);
        } finally {
            @unlink($path);
        }
    }
}
