<?php

declare(strict_types=1);

namespace Drupal\Tests\acquia_dam\Kernel;

use Drupal\KernelTests\KernelTestBase;
use Drupal\media\Entity\Media;
use Drupal\Tests\media\Traits\MediaTypeCreationTrait;

/**
 * Tests bundle field map registration for JSON:API support.
 *
 * @group acquia_dam
 * @group jsonapi
 */
class BundleFieldMapTest extends KernelTestBase {

  use MediaTypeCreationTrait;

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'system',
    'user',
    'field',
    'file',
    'image',
    'media',
    'serialization',
    'jsonapi',
    'acquia_dam',
  ];

  /**
   * The entity field manager.
   *
   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
   */
  protected $entityFieldManager;

  /**
   * The bundle field map service.
   *
   * @var \Drupal\acquia_dam\BundleFieldMapService
   */
  protected $bundleFieldMapService;

  /**
   * The JSON:API resource type repository.
   *
   * @var \Drupal\jsonapi\ResourceType\ResourceTypeRepositoryInterface
   */
  protected $resourceTypeRepository;

  /**
   * The JSON:API serializer.
   *
   * @var \Drupal\jsonapi\Serializer\Serializer
   */
  protected $serializer;

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();

    $this->installEntitySchema('user');
    $this->installEntitySchema('file');
    $this->installEntitySchema('media');
    $this->installSchema('file', ['file_usage']);
    $this->installConfig(['system', 'field', 'file', 'media', 'acquia_dam']);

    $this->entityFieldManager = $this->container->get('entity_field.manager');
    $this->bundleFieldMapService = $this->container->get('acquia_dam.bundle_field_map');
    $this->resourceTypeRepository = $this->container->get('jsonapi.resource_type.repository');
    $this->serializer = $this->container->get('jsonapi.serializer');
  }

  /**
   * Tests that DAM bundle fields are registered in field map.
   */
  public function testBundleFieldsRegisteredInFieldMap(): void {
    // Create an Acquia DAM media type.
    $this->createMediaType('acquia_dam_asset:image', [
      'id' => 'acquia_dam_image',
      'label' => 'DAM Image',
    ]);

    // Register bundle fields.
    $stats = $this->bundleFieldMapService->registerBundleFields();

    // Assert fields were registered.
    $this->assertEquals(1, $stats['bundles_processed']);
    $this->assertGreaterThan(0, $stats['fields_registered']);

    // Verify field map contains DAM fields.
    $field_map = $this->entityFieldManager->getFieldMap()['media'];
    $this->assertArrayHasKey('acquia_dam_asset_id', $field_map);
    $this->assertContains('acquia_dam_image', $field_map['acquia_dam_asset_id']['bundles']);
  }

  /**
   * Tests that JSON:API recognizes DAM bundle fields.
   */
  public function testJsonApiRecognizesDamFields(): void {
    // Create DAM media type.
    $this->createMediaType('acquia_dam_asset:image', [
      'id' => 'acquia_dam_test',
      'label' => 'DAM Test',
    ]);

    // Register fields.
    $this->bundleFieldMapService->registerBundleFields();

    // Get JSON:API resource type.
    $resource_type = $this->resourceTypeRepository->get('media', 'acquia_dam_test');

    // Assert JSON:API recognizes DAM fields.
    $this->assertTrue($resource_type->hasField('acquia_dam_asset_id'));
    $this->assertTrue($resource_type->hasField('acquia_dam_expiry_date'));
    $this->assertTrue($resource_type->hasField('acquia_dam_embed_codes'));
  }

  /**
   * Tests JSON:API can denormalize DAM media with mocked POST data.
   *
   * This tests the core issue: creating DAM media via JSON:API POST.
   */
  public function testJsonApiDenormalizationWithMockedPostData(): void {
    // Create DAM media type.
    $this->createMediaType('acquia_dam_asset:image', [
      'id' => 'acquia_dam_post',
      'label' => 'DAM POST Test',
    ]);

    // Register fields - THIS IS THE FIX.
    $this->bundleFieldMapService->registerBundleFields();

    // Get resource type.
    $resource_type = $this->resourceTypeRepository->get('media', 'acquia_dam_post');

    // Mock JSON:API POST payload.
    $mock_post_data = [
      'type' => 'media--acquia_dam_post',
      'attributes' => [
        'name' => 'Test Asset from JSON:API',
        'acquia_dam_asset_id' => [
          'asset_id' => 'mock-asset-12345',
        ],
      ],
    ];

    // Attempt denormalization (this would fail before the fix).
    $entity = $this->serializer->denormalize(
      $mock_post_data,
      Media::class,
      'api_json',
      ['resource_type' => $resource_type]
    );

    // Assert entity was created successfully.
    $this->assertInstanceOf(Media::class, $entity);
    $this->assertEquals('Test Asset from JSON:API', $entity->label());
    $this->assertEquals('mock-asset-12345', $entity->get('acquia_dam_asset_id')->asset_id);
  }

  /**
   * Tests JSON:API serialization includes DAM fields.
   */
  public function testJsonApiSerializationIncludesDamFields(): void {
    // Create DAM media type.
    $this->createMediaType('acquia_dam_asset:image', [
      'id' => 'acquia_dam_serialize',
      'label' => 'Serialize Test',
    ]);

    // Register fields.
    $this->bundleFieldMapService->registerBundleFields();

    // Create media entity with DAM field.
    $media = Media::create([
      'bundle' => 'acquia_dam_serialize',
      'name' => 'Test Serialization',
      'acquia_dam_asset_id' => [
        'asset_id' => 'serialize-test-789',
      ],
    ]);
    $media->save();

    // Get resource type and serialize.
    $resource_type = $this->resourceTypeRepository->get('media', 'acquia_dam_serialize');
    $normalized = $this->serializer->normalize($media, 'api_json', [
      'resource_type' => $resource_type,
    ]);

    // Assert DAM field is in serialized output.
    $this->assertArrayHasKey('attributes', $normalized['data']);
    $this->assertArrayHasKey('acquia_dam_asset_id', $normalized['data']['attributes']);
  }

  /**
   * Tests hook_entity_bundle_create registers fields automatically.
   */
  public function testBundleCreationHookAutoRegistersFields(): void {
    // Clear cache first.
    $this->entityFieldManager->clearCachedFieldDefinitions();

    // Create media type (triggers hook_entity_bundle_create).
    $this->createMediaType('acquia_dam_asset:video', [
      'id' => 'acquia_dam_auto',
      'label' => 'Auto Register',
    ]);

    // Verify fields are automatically registered.
    $resource_type = $this->resourceTypeRepository->get('media', 'acquia_dam_auto');
    $this->assertTrue($resource_type->hasField('acquia_dam_asset_id'));
  }

  /**
   * Tests non-DAM bundles don't get DAM fields.
   */
  public function testNonDamBundlesExcluded(): void {
    // Create regular media type.
    $this->createMediaType('image', [
      'id' => 'regular_image',
      'label' => 'Regular Image',
    ]);

    // Create DAM media type.
    $this->createMediaType('acquia_dam_asset:image', [
      'id' => 'acquia_dam_image',
      'label' => 'DAM Image',
    ]);

    // Register fields.
    $this->bundleFieldMapService->registerBundleFields();

    // Assert regular media doesn't have DAM fields.
    $regular_resource = $this->resourceTypeRepository->get('media', 'regular_image');
    $this->assertFalse($regular_resource->hasField('acquia_dam_asset_id'));

    // Assert DAM media has DAM fields.
    $dam_resource = $this->resourceTypeRepository->get('media', 'acquia_dam_image');
    $this->assertTrue($dam_resource->hasField('acquia_dam_asset_id'));
  }

  /**
   * Tests multiple DAM bundles all get fields registered.
   */
  public function testMultipleDamBundlesRegistered(): void {
    // Create multiple DAM types.
    $bundles = ['image', 'video', 'document'];
    foreach ($bundles as $type) {
      $this->createMediaType("acquia_dam_asset:{$type}", [
        'id' => "acquia_dam_{$type}",
        'label' => "DAM " . ucfirst($type),
      ]);
    }

    // Register fields.
    $stats = $this->bundleFieldMapService->registerBundleFields();
    $this->assertEquals(3, $stats['bundles_processed']);

    // Verify each bundle has fields in JSON:API.
    foreach ($bundles as $type) {
      $resource_type = $this->resourceTypeRepository->get('media', "acquia_dam_{$type}");
      $this->assertTrue($resource_type->hasField('acquia_dam_asset_id'));
    }
  }

  /**
   * Tests field registration is idempotent.
   */
  public function testFieldRegistrationIsIdempotent(): void {
    // Create DAM media type.
    $this->createMediaType('acquia_dam_asset:image', [
      'id' => 'acquia_dam_repeat',
      'label' => 'Repeat Test',
    ]);

    // Register multiple times.
    $stats1 = $this->bundleFieldMapService->registerBundleFields();
    $stats2 = $this->bundleFieldMapService->registerBundleFields();
    $stats3 = $this->bundleFieldMapService->registerBundleFields();

    // Stats should be identical.
    $this->assertEquals($stats1, $stats2);
    $this->assertEquals($stats2, $stats3);

    // JSON:API should still recognize fields.
    $resource_type = $this->resourceTypeRepository->get('media', 'acquia_dam_repeat');
    $this->assertTrue($resource_type->hasField('acquia_dam_asset_id'));
  }

}
