<?php

namespace Drupal\Tests\eb\Kernel\Entity;

use Drupal\eb\Entity\EbDefinition;
use Drupal\Tests\eb\Kernel\EbKernelTestBase;

/**
 * Kernel tests for EbDefinition config entity.
 *
 * @coversDefaultClass \Drupal\eb\Entity\EbDefinition
 * @group eb
 */
class EbDefinitionTest extends EbKernelTestBase {

  /**
   * Tests creating a definition entity.
   *
   * @covers ::preSave
   */
  public function testCreateDefinition(): void {
    $definition = EbDefinition::create([
      'id' => 'test_definition',
      'label' => 'Test Definition',
      'description' => 'A test definition.',
      'bundle_definitions' => [
        ['entity_type' => 'node', 'bundle_id' => 'article', 'label' => 'Article'],
      ],
    ]);

    $definition->save();

    $loaded = EbDefinition::load('test_definition');
    $this->assertNotNull($loaded);
    $this->assertEquals('Test Definition', $loaded->label());
    $this->assertEquals('A test definition.', $loaded->getDescription());
    $this->assertEquals(['node'], $loaded->getEntityTypes());
  }

  /**
   * Tests default application status is draft.
   *
   * @covers ::getApplicationStatus
   */
  public function testDefaultApplicationStatus(): void {
    $definition = EbDefinition::create([
      'id' => 'draft_definition',
      'label' => 'Draft Definition',
    ]);

    $definition->save();

    $this->assertEquals('draft', $definition->getApplicationStatus());
    $this->assertTrue($definition->isDraft());
    $this->assertFalse($definition->isApplied());
  }

  /**
   * Tests marking definition as applied.
   *
   * @covers ::markAsApplied
   * @covers ::isApplied
   */
  public function testMarkAsApplied(): void {
    $definition = EbDefinition::create([
      'id' => 'applied_definition',
      'label' => 'Applied Definition',
    ]);

    $definition->save();
    $definition->markAsApplied();
    $definition->save();

    $loaded = EbDefinition::load('applied_definition');
    $this->assertEquals('applied', $loaded->getApplicationStatus());
    $this->assertTrue($loaded->isApplied());
    $this->assertFalse($loaded->isDraft());
    $this->assertGreaterThan(0, $loaded->getAppliedDate());
  }

  /**
   * Tests marking definition as outdated.
   *
   * @covers ::markAsOutdated
   * @covers ::isOutdated
   */
  public function testMarkAsOutdated(): void {
    $definition = EbDefinition::create([
      'id' => 'outdated_definition',
      'label' => 'Outdated Definition',
    ]);

    $definition->save();
    $definition->markAsApplied();
    $definition->markAsOutdated();
    $definition->save();

    $loaded = EbDefinition::load('outdated_definition');
    $this->assertEquals('outdated', $loaded->getApplicationStatus());
    $this->assertTrue($loaded->isOutdated());
    $this->assertFalse($loaded->isApplied());
  }

  /**
   * Tests bundle definitions.
   *
   * @covers ::getBundleDefinitions
   * @covers ::setBundleDefinitions
   * @covers ::addBundleDefinition
   * @covers ::getBundleCount
   */
  public function testBundleDefinitions(): void {
    $definition = EbDefinition::create([
      'id' => 'bundle_definition',
      'label' => 'Bundle Definition',
    ]);

    $bundleDefinitions = [
      ['bundle_id' => 'article', 'label' => 'Article'],
      ['bundle_id' => 'page', 'label' => 'Page'],
    ];

    $definition->setBundleDefinitions($bundleDefinitions);
    $definition->save();

    $loaded = EbDefinition::load('bundle_definition');
    $this->assertCount(2, $loaded->getBundleDefinitions());
    $this->assertEquals(2, $loaded->getBundleCount());
  }

  /**
   * Tests getting a single bundle definition.
   *
   * @covers ::getBundleDefinition
   */
  public function testGetBundleDefinition(): void {
    $definition = EbDefinition::create([
      'id' => 'single_bundle',
      'label' => 'Single Bundle',
      'bundle_definitions' => [
        ['bundle_id' => 'article', 'label' => 'Article'],
        ['bundle_id' => 'page', 'label' => 'Page'],
      ],
    ]);

    $definition->save();

    $loaded = EbDefinition::load('single_bundle');
    $articleBundle = $loaded->getBundleDefinition('article');
    $this->assertNotNull($articleBundle);
    $this->assertEquals('Article', $articleBundle['label']);

    $nonexistent = $loaded->getBundleDefinition('nonexistent');
    $this->assertNull($nonexistent);
  }

  /**
   * Tests field definitions.
   *
   * @covers ::getFieldDefinitions
   * @covers ::setFieldDefinitions
   * @covers ::getFieldCount
   */
  public function testFieldDefinitions(): void {
    $definition = EbDefinition::create([
      'id' => 'field_definition',
      'label' => 'Field Definition',
    ]);

    $fieldDefinitions = [
      ['field_name' => 'field_subtitle', 'field_type' => 'text', 'bundle' => 'article'],
      ['field_name' => 'field_body', 'field_type' => 'text_long', 'bundle' => 'article'],
    ];

    $definition->setFieldDefinitions($fieldDefinitions);
    $definition->save();

    $loaded = EbDefinition::load('field_definition');
    $this->assertCount(2, $loaded->getFieldDefinitions());
    $this->assertEquals(2, $loaded->getFieldCount());
  }

  /**
   * Tests getting fields for a specific bundle.
   *
   * @covers ::getFieldsForBundle
   */
  public function testGetFieldsForBundle(): void {
    $definition = EbDefinition::create([
      'id' => 'multi_bundle_fields',
      'label' => 'Multi Bundle Fields',
      'field_definitions' => [
        ['field_name' => 'field_article_image', 'bundle' => 'article'],
        ['field_name' => 'field_article_tags', 'bundle' => 'article'],
        ['field_name' => 'field_page_sidebar', 'bundle' => 'page'],
      ],
    ]);

    $definition->save();

    $loaded = EbDefinition::load('multi_bundle_fields');
    $articleFields = $loaded->getFieldsForBundle('article');
    $this->assertCount(2, $articleFields);

    $pageFields = $loaded->getFieldsForBundle('page');
    $this->assertCount(1, $pageFields);
  }

  /**
   * Tests display field definitions (flat format).
   *
   * @covers ::getDisplayFieldDefinitions
   * @covers ::setDisplayFieldDefinitions
   */
  public function testDisplayFieldDefinitions(): void {
    $definition = EbDefinition::create([
      'id' => 'display_definition',
      'label' => 'Display Definition',
    ]);

    $displayFieldDefinitions = [
      [
        'entity_type' => 'node',
        'bundle' => 'article',
        'display_type' => 'form',
        'mode' => 'default',
        'field_name' => 'field_subtitle',
        'widget' => 'string_textfield',
        'weight' => 0,
      ],
      [
        'entity_type' => 'node',
        'bundle' => 'article',
        'display_type' => 'view',
        'mode' => 'default',
        'field_name' => 'field_subtitle',
        'formatter' => 'string',
        'weight' => 0,
      ],
    ];

    $definition->setDisplayFieldDefinitions($displayFieldDefinitions);
    $definition->save();

    $loaded = EbDefinition::load('display_definition');
    $displays = $loaded->getDisplayFieldDefinitions();
    $this->assertCount(2, $displays);
    $types = array_column($displays, 'display_type');
    $this->assertContains('form', $types);
    $this->assertContains('view', $types);
  }

  /**
   * Tests menu definitions.
   *
   * @covers ::getMenuDefinitions
   * @covers ::setMenuDefinitions
   */
  public function testMenuDefinitions(): void {
    $definition = EbDefinition::create([
      'id' => 'menu_definition',
      'label' => 'Menu Definition',
    ]);

    $menuDefinitions = [
      ['menu_id' => 'main', 'title' => 'Main Menu'],
    ];

    $definition->setMenuDefinitions($menuDefinitions);
    $definition->save();

    $loaded = EbDefinition::load('menu_definition');
    $menus = $loaded->getMenuDefinitions();
    $this->assertCount(1, $menus);
  }

  /**
   * Tests dependencies data.
   *
   * @covers ::getDependenciesData
   * @covers ::setDependenciesData
   * @covers ::getModuleDependencies
   */
  public function testDependenciesData(): void {
    $definition = EbDefinition::create([
      'id' => 'deps_definition',
      'label' => 'Dependencies Definition',
    ]);

    $depsData = [
      'module' => ['media', 'paragraphs'],
    ];

    $definition->setDependenciesData($depsData);
    $definition->save();

    $loaded = EbDefinition::load('deps_definition');
    $deps = $loaded->getDependenciesData();
    $this->assertArrayHasKey('module', $deps);
    $this->assertContains('media', $loaded->getModuleDependencies());
  }

  /**
   * Tests created and changed timestamps.
   *
   * @covers ::getCreatedTime
   * @covers ::getChangedTime
   * @covers ::preSave
   */
  public function testTimestamps(): void {
    $definition = EbDefinition::create([
      'id' => 'timestamp_definition',
      'label' => 'Timestamp Definition',
    ]);

    $definition->save();

    $loaded = EbDefinition::load('timestamp_definition');
    $this->assertGreaterThan(0, $loaded->getCreatedTime());
    $this->assertGreaterThan(0, $loaded->getChangedTime());
    $this->assertEquals($loaded->getCreatedTime(), $loaded->getChangedTimeAcrossTranslations());
  }

  /**
   * Tests entity deletion.
   */
  public function testDeleteDefinition(): void {
    $definition = EbDefinition::create([
      'id' => 'delete_definition',
      'label' => 'Delete Definition',
    ]);

    $definition->save();
    $this->assertNotNull(EbDefinition::load('delete_definition'));

    $definition->delete();
    $this->assertNull(EbDefinition::load('delete_definition'));
  }

}
