<?php

namespace Drupal\Tests\lingotek\Unit;

use Drupal\Core\Config\Config;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\lingotek\Entity\LingotekProfile;
use Drupal\lingotek\LingotekFilterManager;
use Drupal\Tests\UnitTestCase;

/**
 * @coversDefaultClass \Drupal\lingotek\LingotekFilterManager
 * @group lingotek
 * @preserveGlobalState disabled
 */
class LingotekFilterManagerUnitTest extends UnitTestCase {

  /**
   * The Lingotek Filter manager.
   *
   * @var \Drupal\lingotek\LingotekFilterManagerInterface
   */
  protected $filterManager;

  /**
   * The Lingotek account settings.
   *
   * @var \Drupal\Core\Config\Config|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $accountConfig;

  /**
   * The config object.
   *
   * @var \Drupal\Core\Config\Config|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $config;

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();
    $this->accountConfig = $this->createMock(Config::class);
    $this->config = $this->createMock(Config::class);
    $configFactory = $this->createMock(ConfigFactoryInterface::class);
    $configFactory->expects($this->any())
      ->method('get')
      ->will($this->returnCallback(function ($config_name) {
        return $config_name === 'lingotek.account' ? $this->accountConfig : $this->config;
      }
      ));
    $this->filterManager = new LingotekFilterManager($configFactory);
  }

  /**
   * @covers ::getLocallyAvailableFilters
   */
  public function testGetLocallyAvailableFilters() {
    // Test with no local filters.
    $this->accountConfig->expects($this->at(0))
      ->method('get')
      ->with('resources.filter')
      ->will($this->returnValue([]));

    $filters = $this->filterManager->getLocallyAvailableFilters();
    $this->assertNotEmpty($filters);
    $this->assertEquals($filters, ['project_default' => 'Project Default', 'drupal_default' => 'Drupal Default']);

    // Test with some filters.
    $expectedConfig = ['aaa' => 'Test filter'];

    $this->accountConfig->expects($this->any())
      ->method('get')
      ->with('resources.filter')
      ->willReturnCallback(function ($configKey) use ($expectedConfig) {
        if ($configKey === 'resources.filter') {
          return $expectedConfig;
        }
        return NULL;
      });
    $filters = $this->filterManager->getLocallyAvailableFilters();
    $this->assertNotEmpty($filters);
    $this->assertEquals(['project_default' => 'Project Default', 'drupal_default' => 'Drupal Default', 'aaa' => 'Test filter'], $filters);
  }

  public function getDefaultFilterProvider() {
    return [
      ['bbb', ['aaa' => 'Test label', 'bbb' => 'Another label'], 'bbb'],
      ['aaa', ['aaa' => 'Test label', 'bbb' => 'Another label'], 'aaa'],
      ['xxx', ['aaa' => 'Test label', 'bbb' => 'Another label'], NULL],
      ['xxx', [], NULL],
    ];
  }

  /**
   * @covers ::getDefaultSubfilter
   * @dataProvider getDefaultFilterProvider
   */
  public function testGetDefaultFilter($id, $filters, $expectedFilter) {
    $expectedCalls = [
      ['default.filter', $id],
      ['resources.filter', $filters],
  ];
    $this->accountConfig->expects($this->exactly(count($expectedCalls)))
      ->method('get')
      ->willReturnCallback(function ($key) use ($expectedCalls) {
        foreach ($expectedCalls as $call) {
          if ($call[0] === $key) {
            return $call[1];
          }
        }
          return NULL;
      });

    $filter = $this->filterManager->getDefaultFilter();
    $this->assertEquals($expectedFilter, $filter);
  }

  /**
   * @covers ::getDefaultSubfilter
   * @dataProvider getDefaultFilterProvider
   */
  public function testGetDefaultSubfilter($id, $filters, $expectedFilter) {
    $expectedCalls = [
      ['default.subfilter', $id],
      ['resources.filter', $filters],
  ];
    $this->accountConfig->expects($this->exactly(count($expectedCalls)))
      ->method('get')
      ->willReturnCallback(function ($key) use ($expectedCalls) {
        foreach ($expectedCalls as $call) {
          if ($call[0] === $key) {
            return $call[1];
          }
        }
          return NULL;
      });

    $filter = $this->filterManager->getDefaultSubfilter();
    $this->assertEquals($expectedFilter, $filter);
  }

  public function getDefaultFilterLabelProvider() {
    return [
      ['bbb', ['aaa' => 'Test label', 'bbb' => 'Another label'], 'Another label'],
      ['aaa', ['aaa' => 'Test label', 'bbb' => 'Another label'], 'Test label'],
      ['xxx', ['aaa' => 'Test label', 'bbb' => 'Another label'], ''],
      ['xxx', [], ''],
    ];
  }

  /**
   * @covers ::getDefaultFilterLabel
   * @dataProvider getDefaultFilterLabelProvider
   */
  public function testGetDefaultFilterLabel($id, $filters, $expectedLabel) {
    $expectedCalls = [
      ['default.filter', $id],
      ['resources.filter', $filters],
      ['resources.filter', $filters],
  ];
    $this->accountConfig->expects($this->exactly(count($expectedCalls)))
      ->method('get')
      ->willReturnCallback(function ($key) use ($expectedCalls) {
        foreach ($expectedCalls as $call) {
          if ($call[0] === $key) {
            return $call[1];
          }
        }
          return NULL;
      });

    $label = $this->filterManager->getDefaultFilterLabel();
    $this->assertEquals($expectedLabel, $label);
  }

  /**
   * @covers ::getDefaultSubfilterLabel
   * @dataProvider getDefaultFilterLabelProvider
   */
  public function testGetDefaultSubfilterLabel($id, $filters, $expectedLabel) {
    $expectedCalls = [
      ['default.subfilter', $id],
      ['resources.filter', $filters],
      ['resources.filter', $filters],
  ];
    $this->accountConfig->expects($this->exactly(count($expectedCalls)))
      ->method('get')
      ->willReturnCallback(function ($key) use ($expectedCalls) {
        foreach ($expectedCalls as $call) {
          if ($call[0] === $key) {
            return $call[1];
          }
        }
          return NULL;
      });

    $label = $this->filterManager->getDefaultSubfilterLabel();
    $this->assertEquals($expectedLabel, $label);
  }

  /**
   * @covers ::getSubfilterId
   */
  public function testGetFilterId() {
    // Filter id has the original value.
    $profile = new LingotekProfile(['id' => 'profile1', 'project' => 'my_test_project', 'vault' => 'my_test_vault', 'filter' => 'my_filter'], 'lingotek_profile');
    $filter = $this->filterManager->getFilterId($profile);
    $this->assertEquals('my_filter', $filter);

    // Filter is replaced with project default.
    $profile = new LingotekProfile(['id' => 'profile1', 'project' => 'my_test_project', 'vault' => 'my_test_vault', 'filter' => 'project_default'], 'lingotek_profile');
    $filter = $this->filterManager->getFilterId($profile);
    $this->assertEquals(NULL, $filter);

    // Filter is replaced with drupal default.
    $profile = new LingotekProfile(['id' => 'profile1', 'project' => 'my_test_project', 'vault' => 'my_test_vault', 'filter' => 'drupal_default'], 'lingotek_profile');
    $filter = $this->filterManager->getFilterId($profile);
    $this->assertEquals('4f91482b-5aa1-4a4a-a43f-712af7b39625', $filter);

    // Filter is replaced with the default.
    $expectedCalls = [
      ['default.filter', 'another_different_filter'],
      ['resources.filter', ['another_different_filter' => 'Another different filter']],
  ];
    $this->accountConfig->expects($this->exactly(count($expectedCalls)))
      ->method('get')
      ->willReturnCallback(function ($key) use ($expectedCalls) {
        foreach ($expectedCalls as $call) {
          if ($call[0] === $key) {
            return $call[1];
          }
        }
          return NULL;
      });

    $profile = new LingotekProfile(['id' => 'profile1', 'project' => 'my_test_project', 'vault' => 'my_test_vault', 'filter' => 'default'], 'lingotek_profile');
    $filter = $this->filterManager->getFilterId($profile);
    $this->assertEquals('another_different_filter', $filter);
  }

  /**
   * @covers ::getSubfilterId
   */
  public function testGetSubfilterId() {
    // Filter id has the original value.
    $profile = new LingotekProfile(['id' => 'profile1', 'project' => 'my_test_project', 'vault' => 'my_test_vault', 'subfilter' => 'my_filter'], 'lingotek_profile');
    $filter = $this->filterManager->getSubfilterId($profile);
    $this->assertEquals('my_filter', $filter);

    // Filter is replaced with project default.
    $profile = new LingotekProfile(['id' => 'profile1', 'project' => 'my_test_project', 'vault' => 'my_test_vault', 'subfilter' => 'project_default'], 'lingotek_profile');
    $filter = $this->filterManager->getSubfilterId($profile);
    $this->assertEquals(NULL, $filter);

    // Filter is replaced with drupal default.
    $profile = new LingotekProfile(['id' => 'profile1', 'project' => 'my_test_project', 'vault' => 'my_test_vault', 'subfilter' => 'drupal_default'], 'lingotek_profile');
    $filter = $this->filterManager->getSubfilterId($profile);
    $this->assertEquals('0e79f34d-f27b-4a0c-880e-cd9181a5d265', $filter);

    // Filter is replaced with the default.
    $expectedCalls = [
      ['default.subfilter', 'another_different_filter'],
      ['resources.filter', ['another_different_filter' => 'Another different filter']],
  ];
    $this->accountConfig->expects($this->exactly(count($expectedCalls)))
      ->method('get')
      ->willReturnCallback(function ($key) use ($expectedCalls) {
        foreach ($expectedCalls as $call) {
          if ($call[0] === $key) {
            return $call[1];
          }
        }
          return NULL;
      });

    $profile = new LingotekProfile(['id' => 'profile1', 'project' => 'my_test_project', 'vault' => 'my_test_vault', 'subfilter' => 'default'], 'lingotek_profile');
    $filter = $this->filterManager->getSubfilterId($profile);
    $this->assertEquals('another_different_filter', $filter);
  }

}
