<?php

/**
 * Created by PhpStorm.
 * User: Eric
 * Date: 10/09/2018
 * Time: 11:02
 */

namespace Combodo\iTop\Test\UnitTest\Core;

use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
use CoreException;
use DeleteException;
use Exception;
use MetaModel;
use TagSetFieldData;

/**
 * @group itopFaqLight
 */
class TagSetFieldDataTest extends ItopDataTestCase
{
	// Need database COMMIT in order to create the FULLTEXT INDEX of MySQL
	public const USE_TRANSACTION = false;

	/**
	 * @throws \CoreException
	 */
	public function testGetAllowedValues()
	{
		$aAllowedValues = TagSetFieldData::GetAllowedValues(TAG_CLASS, TAG_ATTCODE);
		$iInitialCount = count($aAllowedValues);
		$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'tag1', 'First');
		$aAllowedValues = TagSetFieldData::GetAllowedValues(TAG_CLASS, TAG_ATTCODE);
		$iCurrCount = count($aAllowedValues);
		static::assertEquals(1, $iCurrCount - $iInitialCount);
		$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'tag2', 'Second');
		$aAllowedValues = TagSetFieldData::GetAllowedValues(TAG_CLASS, TAG_ATTCODE);
		$iCurrCount = count($aAllowedValues);
		static::assertEquals(2, $iCurrCount - $iInitialCount);
		$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'tag3', 'Third');
		$aAllowedValues = TagSetFieldData::GetAllowedValues(TAG_CLASS, TAG_ATTCODE);
		$iCurrCount = count($aAllowedValues);
		static::assertEquals(3, $iCurrCount - $iInitialCount);
		$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'tag4', 'Fourth');
		$aAllowedValues = TagSetFieldData::GetAllowedValues(TAG_CLASS, TAG_ATTCODE);
		$iCurrCount = count($aAllowedValues);
		static::assertEquals(4, $iCurrCount - $iInitialCount);
	}

	/**
	 * @throws \CoreException
	 */
	public function testDoCheckToWrite()
	{
		$aAllowedValues = TagSetFieldData::GetAllowedValues(TAG_CLASS, TAG_ATTCODE);
		$iInitialCount = count($aAllowedValues);
		$this->debug("Currently $iInitialCount tags defined");
		$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'tag1', 'First');
		$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'tag2', 'Second');
		$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'tag3', 'Third');
		$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'tag4', 'Fourth');
		$aAllowedValues = TagSetFieldData::GetAllowedValues(TAG_CLASS, TAG_ATTCODE);
		$iCurrCount = count($aAllowedValues);
		static::assertEquals(4, $iCurrCount - $iInitialCount);

		try {
			$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'tag4', 'Fourth');
		} catch (CoreException $e) {
			$this->debug($e->getMessage());
		}
		$aAllowedValues = TagSetFieldData::GetAllowedValues(TAG_CLASS, TAG_ATTCODE);
		$iCurrCount = count($aAllowedValues);
		static::assertEquals(4, $iCurrCount - $iInitialCount);

		try {
			$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'tag4', 'zembrek');
		} catch (CoreException $e) {
			$this->debug($e->getMessage());
		}
		$aAllowedValues = TagSetFieldData::GetAllowedValues(TAG_CLASS, TAG_ATTCODE);
		$iCurrCount = count($aAllowedValues);
		static::assertEquals(4, $iCurrCount - $iInitialCount);

		try {
			$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'zembrek', 'Fourth');
		} catch (CoreException $e) {
			$this->debug($e->getMessage());
		}
		$aAllowedValues = TagSetFieldData::GetAllowedValues(TAG_CLASS, TAG_ATTCODE);
		$iCurrCount = count($aAllowedValues);
		static::assertEquals(4, $iCurrCount - $iInitialCount);
	}

	/**
	 * @group itopFaqLight
	 * @throws \CoreException
	 * @throws \CoreUnexpectedValue
	 * @throws \Exception
	 */
	public function testDoCheckToDelete()
	{
		$oTagData = $this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'tag1', 'First');
		$oTagData->DBDelete();

		// Create a tag
		$oTagData = $this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'tag1', 'First');
		//Use it
		$oObjWithTagSet = $this->CreateObjectWithTagSet();
		$oObjWithTagSet->Set(TAG_ATTCODE, 'tag1');
		$oObjWithTagSet->DBWrite();

		// Try to delete the tag, must complain !
		$this->expectException(DeleteException::class);
		$oTagData->DBDelete();
	}

	/**
	 * @throws \CoreException
	 * @throws \Exception
	 */
	public function testComputeValues()
	{
		$sTagClass = TagSetFieldData::GetTagDataClassName(TAG_CLASS, TAG_ATTCODE);
		$oTagData = $this->createObject($sTagClass, [
			'code' => 'tag1',
			'label' => 'First',
		]);
		$this->debug("Created {$oTagData->Get('obj_class')}::{$oTagData->Get('obj_attcode')}");

		static::assertEquals(TAG_CLASS, $oTagData->Get('obj_class'));
		static::assertEquals(TAG_ATTCODE, $oTagData->Get('obj_attcode'));
	}

	/**
	 * Test invalid tag codes
	 * @dataProvider InvalidTagCodeProvider
	 *
	 * @param string $sTagCode
	 *
	 */
	public function testInvalidTagCode($sTagCode)
	{
		try {
			$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, $sTagCode, 'First');
		} catch (CoreException $e) {
			static::assertTrue(true);
			return;
		}
		// Should not pass here
		static::assertFalse(true);
	}

	public function InvalidTagCodeProvider()
	{
		return [
			'No space' => ['tag1 1'],
			'No _' => ['tag_1'],
			'No -' => ['tag-1'],
			'No %' => ['tag%1'],
			'At least 3 chars' => [''],
			'At least 3 chars 1' => ['a'],
			'At least 3 chars 2' => ['ab'],
			'No #' => ['#tag'],
			'No !' => ['tag!'],
			'Stop Word 1' => ['about'],
			'Stop Word 2' => ['from'],
			'Stop Word 3' => ['that'],
			'Stop Word 4' => ['where'],
			'Stop Word 5' => ['who'],
		];
	}

	/**
	 * Test invalid tag labels
	 */
	public function testInvalidTagLabel()
	{
		try {
			$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'tag1', 'First|Second');
		} catch (CoreException $e) {
			static::assertFalse(false);
			return;
		}
		// Should not pass here
		static::assertFalse(true);
	}

	/**
	 * @group itopFaqLight
	 * Test that tag code cannot be modified if used
	 *
	 * @throws \CoreException
	 * @throws \CoreUnexpectedValue
	 * @throws \Exception
	 */
	public function testUpdateCode()
	{
		$oTagData = $this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'tag1', 'First');
		$oTagData->Set('code', 'tag2');
		$oTagData->DBWrite();

		//Use it
		$oObjWithTagSet = $this->CreateObjectWithTagSet();
		$oObjWithTagSet->Set(TAG_ATTCODE, 'tag2');
		$oObjWithTagSet->DBWrite();

		// Try to change the code of the tag, must complain !
		$oTagData->Set('code', 'tag1');
		$this->expectException(CoreException::class);
		$oTagData->DBWrite();
	}

	/**
	 * Check that the code length is correctly checked
	 *
	 * @throws \CoreException
	 */
	public function testMaxTagCodeLength()
	{
		/** @var \AttributeTagSet $oAttdef */
		$oAttdef = MetaModel::GetAttributeDef(TAG_CLASS, TAG_ATTCODE);

		$iMaxLength = $oAttdef->GetTagCodeMaxLength();
		$sTagCode = str_repeat('a', $iMaxLength);

		// Should work
		$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, $sTagCode, $sTagCode);

		// Too long
		$sTagCode = str_repeat('a', $iMaxLength + 1);
		try {
			$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, $sTagCode, $sTagCode);
		} catch (CoreException $e) {
			$this->debug('Awaited: '.$e->getMessage());
			static::assertTrue(true);
			return;
		}
		// Failed
		static::assertFalse(true);
	}

	/**
	 * @group itopFaqLight
	 */
	public function testMaxTagsAllowed()
	{
		/** @var \AttributeTagSet $oAttDef */
		$oAttDef = MetaModel::GetAttributeDef(TAG_CLASS, TAG_ATTCODE);
		$iMaxTags = $oAttDef->GetMaxItems();
		for ($i = 0; $i < $iMaxTags; $i++) {
			$sTagCode = 'MaxTag'.$i;
			$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, $sTagCode, $sTagCode);
		}
		$oObjWithTagSet = $this->CreateObjectWithTagSet();
		$this->debug("Max number of tags is $iMaxTags");
		$sValue = '';
		for ($i = 0; $i < ($iMaxTags + 1); $i++) {
			try {
				$sTagCode = 'MaxTag'.$i;
				$sValue .= "$sTagCode ";
				$oObjWithTagSet->Set(TAG_ATTCODE, $sValue);
				$oObjWithTagSet->DBWrite();
			} catch (Exception $e) {
				// Should fail on the last iteration
				static::assertEquals($iMaxTags, $i);
				$this->debug("Setting (".($i + 1).") tag(s) failed");
				return;
			}
			$this->debug("Setting (".($i + 1).") tag(s) worked");
		}

		static::assertFalse(true);
	}
}
