<?php

/**
 * Class CRM_Core_DAO_AllCoreTablesTest
 * @group headless
 */
class CRM_Core_DAO_AllCoreTablesTest extends CiviUnitTestCase {

  public function testGetTableForClass(): void {
    $this->assertEquals('civicrm_email', CRM_Core_DAO_AllCoreTables::getTableForClass('CRM_Core_DAO_Email'));
    $this->assertEquals('civicrm_email', CRM_Core_DAO_AllCoreTables::getTableForClass('CRM_Core_BAO_Email'));
  }

  /**
   * Ensure that hook_civicrm_entityTypes runs and correctly handles the
   * 'fields_callback' option.
   */
  public function testHook(): void {
    // 1. First, check the baseline fields()...
    $fields = CRM_Core_DAO_Email::fields();
    $this->assertFalse(isset($fields['location_type_id']['foo']));

    $exports = CRM_Core_DAO_Email::export();
    $this->assertFalse(isset($exports['contact_id']));

    // 2. Now, let's hook into it...
    $this->hookClass->setHook('civicrm_entityTypes', [$this, '_hook_civicrm_entityTypes']);
    unset(Civi::$statics['CRM_Core_DAO_Email']);
    CRM_Core_DAO_AllCoreTables::flush();

    // 3. And see if the data has changed...
    $fields = CRM_Core_DAO_Email::fields();
    $this->assertEquals('bar', $fields['location_type_id']['foo']);

    $exports = CRM_Core_DAO_Email::export();
    $this->assertTrue(is_array($exports['contact_id']));
  }

  /**
   * Implements hook_civicrm_entityTypes().
   *
   * Note: `fields_callback` is now deprecated in favor of the `civi.entity.fields` event.
   * @deprecated
   * @see CRM_Utils_Hook::entityTypes()
   */
  public function _hook_civicrm_entityTypes(&$entityTypes) {
    $entityTypes['Email']['fields_callback'][] = function ($class, &$fields) {
      $fields['location_type_id']['foo'] = 'bar';
      $fields['contact_id']['export'] = TRUE;
    };
  }

  protected function tearDown(): void {
    CRM_Utils_Hook::singleton()->reset();
    CRM_Core_DAO_AllCoreTables::flush();
    parent::tearDown();
  }

  /**
   * Test CRM_Core_DAO_AllCoreTables::indices() function.
   *
   * Ensure indices are listed correctly with and without localization
   *
   * @group locale
   */
  public function testIndices(): void {
    // civicrm_group UI_title is localizable
    // Check indices without localization
    $indices = CRM_Core_DAO_AllCoreTables::indices(FALSE);
    $this->assertEquals($indices['civicrm_group']['UI_title']['name'], 'UI_title');
    $this->assertEquals($indices['civicrm_group']['UI_title']['sig'], 'civicrm_group::1::title');

    // Not sure how we should be setting the locales, but this works for testing purposes
    $domain = new CRM_Core_DAO_Domain();
    $domain->find(TRUE);
    $domain->locales = implode(CRM_Core_DAO::VALUE_SEPARATOR, ['en_UK', 'fr_FR']);
    $domain->save();

    // Check indices with localization
    $indices = CRM_Core_DAO_AllCoreTables::indices(TRUE);
    $this->assertEquals($indices['civicrm_group']['UI_title_en_UK']['name'], 'UI_title_en_UK');
    $this->assertEquals($indices['civicrm_group']['UI_title_en_UK']['sig'], 'civicrm_group::1::title_en_UK');

    $this->assertEquals($indices['civicrm_group']['UI_title_fr_FR']['name'], 'UI_title_fr_FR');
    $this->assertEquals($indices['civicrm_group']['UI_title_fr_FR']['sig'], 'civicrm_group::1::title_fr_FR');
  }

  /**
   * Check CRM_Core_DAO_AllCoreTables::multilingualize()
   */
  public function testMultilingualize(): void {
    // in civicrm_group, title is localizable, name is not
    $originalIndices = [
      'test_index1' => [
        'name' => 'test_index1',
        'field' => [
          'name',
        ],
        'localizable' => 0,
      ],
      'test_index2' => [
        'name' => 'test_index2',
        'field' => [
          'title',
        ],
        'localizable' => 1,
      ],
      'test_index3' => [
        'name' => 'test_index3',
        'field' => [
          'name(3)',
        ],
        'localizable' => 0,
      ],
      'test_index4' => [
        'name' => 'test_index4',
        'field' => [
          'title(4)',
        ],
        'localizable' => 1,
      ],
      'test_index5' => [
        'name' => 'test_index5',
        'field' => [
          'title(4)',
          'name(3)',
        ],
        'localizable' => 1,
      ],
    ];

    $expectedIndices = [
      'test_index1' => [
        'name' => 'test_index1',
        'field' => [
          'name',
        ],
        'localizable' => 0,
        'sig' => 'civicrm_group::0::name',
      ],
      'test_index2_en_UK' => [
        'name' => 'test_index2_en_UK',
        'field' => [
          'title_en_UK',
        ],
        'localizable' => 1,
        'sig' => 'civicrm_group::0::title_en_UK',
      ],
      'test_index2_fr_FR' => [
        'name' => 'test_index2_fr_FR',
        'field' => [
          'title_fr_FR',
        ],
        'localizable' => 1,
        'sig' => 'civicrm_group::0::title_fr_FR',
      ],
      'test_index3' => [
        'name' => 'test_index3',
        'field' => [
          'name(3)',
        ],
        'localizable' => 0,
        'sig' => 'civicrm_group::0::name(3)',
      ],
      'test_index4_en_UK' => [
        'name' => 'test_index4_en_UK',
        'field' => [
          'title_en_UK(4)',
        ],
        'localizable' => 1,
        'sig' => 'civicrm_group::0::title_en_UK(4)',
      ],
      'test_index4_fr_FR' => [
        'name' => 'test_index4_fr_FR',
        'field' => [
          'title_fr_FR(4)',
        ],
        'localizable' => 1,
        'sig' => 'civicrm_group::0::title_fr_FR(4)',
      ],
      'test_index5_en_UK' => [
        'name' => 'test_index5_en_UK',
        'field' => [
          'title_en_UK(4)',
          'name(3)',
        ],
        'localizable' => 1,
        'sig' => 'civicrm_group::0::title_en_UK(4)::name(3)',
      ],
      'test_index5_fr_FR' => [
        'name' => 'test_index5_fr_FR',
        'field' => [
          'title_fr_FR(4)',
          'name(3)',
        ],
        'localizable' => 1,
        'sig' => 'civicrm_group::0::title_fr_FR(4)::name(3)',
      ],
    ];

    // Not sure how we should be setting the locales, but this works for testing purposes
    $domain = new CRM_Core_DAO_Domain();
    $domain->find(TRUE);
    $domain->locales = implode(CRM_Core_DAO::VALUE_SEPARATOR, ['en_UK', 'fr_FR']);
    $domain->save();

    // needs a real DAO so use Group
    $newIndices = CRM_Core_DAO_AllCoreTables::multilingualize('CRM_Contact_DAO_Group', $originalIndices);
    $this->assertEquals($newIndices, $expectedIndices);
  }

  /**
   * Test CRM_Core_DAO_AllCoreTables::isCoreTable
   */
  public function testIsCoreTable(): void {
    $this->assertTrue(CRM_Core_DAO_AllCoreTables::isCoreTable('civicrm_contact'), 'civicrm_contact should be a core table');
    $this->assertFalse(CRM_Core_DAO_AllCoreTables::isCoreTable('civicrm_invalid_table'), 'civicrm_invalid_table should NOT be a core table');
  }

  public function testGetBriefName(): void {
    $this->assertEquals('Contact', CRM_Core_DAO_AllCoreTables::getEntityNameForClass('CRM_Contact_BAO_Contact'));
    $this->assertEquals('Contact', CRM_Core_DAO_AllCoreTables::getEntityNameForClass('CRM_Contact_DAO_Contact'));
    $this->assertNull(CRM_Core_DAO_AllCoreTables::getEntityNameForClass('CRM_Core_DAO_XqZy'));
  }

  public function testGetFullName(): void {
    $this->assertEquals('CRM_Contact_DAO_Contact', CRM_Core_DAO_AllCoreTables::getDAONameForEntity('Contact'));
    $this->assertNull(CRM_Core_DAO_AllCoreTables::getDAONameForEntity('XqZy'));
  }

  public function testGetEntityNameForTable(): void {
    $this->assertEquals('Contact', CRM_Core_DAO_AllCoreTables::getEntityNameForTable('civicrm_contact'));
    $this->assertEquals('RelationshipCache', CRM_Core_DAO_AllCoreTables::getEntityNameForTable('civicrm_relationship_cache'));
    $this->assertNull(CRM_Core_DAO_AllCoreTables::getEntityNameForTable('civicrm_invalid_table'));
  }

}
