<?php
/* Copyright (C) 2025       Laurent Destailleur         <eldy@users.sourceforge.net>
 * Copyright (C) 2025       Mohamed DAOUD               <mdaoud@dolicloud.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

/**
 *      \file       test/phpunit/EInvoicingSamplesTest.php
 *      \ingroup    test
 *      \brief      Regression test for the  sample invoice chain (deposit, standard, credit
 *                  note) built by EInvoicing::generateSampleEInvoicesForTests().
 * 					Generated XML is compared against the reference fixtures
 * 					in test/phpunit/fixtures/einvoicing_samples/.
 */


// This script must only be run from the command line.
if (PHP_SAPI !== 'cli') {
	echo "Error: this script must be run from the command line (CLI), not through a web server.\n";
	exit(1);
}

// PHPUnit loads this file via an include done inside one of its own methods, not at the true global
// scope. Binding these as global here first ensures master.inc.php's assignments below land on the
// real global variables (needed by Dolibarr core functions that do their own "global $conf;" etc.).
global $conf, $user, $langs, $db;

// Load Dolibarr environment
// This module is deployed by symlinking this repository into htdocs/custom/einvoicing of one or
// several Dolibarr instances. Some test runners resolve the real (non-symlinked) path of this
// file before including it, which breaks a fixed "../../htdocs/master.inc.php" relative path.
// DOLIBARR_HTDOCS let's the developer/CI point explicitly at the Dolibarr instance to test
// against; otherwise we fall back to the standard relative path (valid when this file is reached
// through the htdocs/custom/einvoicing/test/phpunit symlink without realpath resolution).
$dolibarrHtdocs = getenv('DOLIBARR_HTDOCS');
if (!$dolibarrHtdocs) {
	$dolibarrHtdocs = dirname(__FILE__) . '/../../htdocs';
}
if (!file_exists($dolibarrHtdocs . '/master.inc.php')) {
	throw new \RuntimeException('Could not locate master.inc.php under "' . $dolibarrHtdocs . '/". Set the environment variable (export DOLIBARR_HTDOCS=...) to the htdocs directory of the Dolibarr instance to test against.');
}
require_once $dolibarrHtdocs . '/master.inc.php';


/**
 * The main.inc.php has been included so the following variable are now defined:
 * @var Conf $conf
 * @var DoliDB $db
 * @var HookManager $hookmanager
 * @var Translate $langs
 * @var User $user
 */

dol_include_once('einvoicing/class/einvoicing.class.php');
require_once __DIR__ . '/CommonClassTestCompat.inc.php';


/**
 * Class EInvoicingSamplesTest
 *
 * This test class ensures that the generated XML output for different invoice types remains
 * consistent and matches the committed reference fixtures. It validates three invoice types:
 * - Deposit invoices
 * - Standard invoices
 * - Credit notes
 *
 * Generated XML is compared against the reference fixtures stored in
 * test/phpunit/fixtures/einvoicing_samples/ using semantic XML comparison.
 */
class EInvoicingSamplesTest extends CommonClassTest
{

	/**
	 * Holds the generated sample invoice chain (deposit, standard, credit note) for the whole test class.
	 * @var array{deposit:string, standard:string, creditnote:string}|null
	 */
	private static $generated = null;

	/**
	 * Generates the sample invoice chain (deposit, standard, credit note) once for the whole test class and returns it.
	 *
	 * @return array{deposit:string, standard:string, creditnote:string}
	 */
	private function getGenerated()
	{
		if (self::$generated === null) {
			self::$generated = EInvoicing::generateSampleEInvoicesForTests();
		}

		return self::$generated;
	}

	/**
	 * Loads a committed reference fixture.
	 *
	 * @param	string	$filename	Fixture file name (e.g. 'cii_deposit.xml')
	 * @return	string	Reference XML content
	 */
	private function loadFixture($filename)
	{
		$path = __DIR__ . '/fixtures/einvoicing_samples/' . $filename;
		$this->assertFileExists(
			$path,
			'Reference fixture ' . $filename . ' is missing. Generate it with: scripts/regenerate_einvoicing_fixtures.php'
		);

		return (string) file_get_contents($path);
	}

	/**
	 * Compares a generated (already normalized) XML against its reference fixture.
	 *
	 * @param	string	$expectedFixtureFile	Fixture file name (e.g. 'cii_deposit.xml')
	 * @param	string	$actualXml				Normalized XML actually generated by this test run
	 * @return	void
	 */
	private function assertMatchesFixture($expectedFixtureFile, $actualXml)
	{
		$expectedXml = $this->loadFixture($expectedFixtureFile);

		$this->assertXmlStringEqualsXmlString(
			$expectedXml,
			$actualXml,
			'Generated CII XML no longer matches ' . $expectedFixtureFile . '. If this change is intentional, regenerate the reference invoices with: scripts/regenerate_einvoicing_fixtures.php, review the diff, then commit the updated fixture.'
		);
	}

	/**
	 * The deposit sample invoice must keep producing the same CII XML.
	 *
	 * @return void
	 */
	public function testDepositInvoiceMatchesReference()
	{
		$generated = $this->getGenerated();
		$this->assertMatchesFixture('cii_deposit.xml', $generated['deposit']);
	}

	/**
	 * The standard sample invoice must keep producing the same CII XML.
	 *
	 * @return void
	 */
	public function testStandardInvoiceMatchesReference()
	{
		$generated = $this->getGenerated();
		$this->assertMatchesFixture('cii_standard.xml', $generated['standard']);
	}

	/**
	 * The credit note sample invoice must keep producing the same CII XML.
	 *
	 * @return void
	 */
	public function testCreditNoteInvoiceMatchesReference()
	{
		$generated = $this->getGenerated();
		$this->assertMatchesFixture('cii_creditnote.xml', $generated['creditnote']);
	}
}
