<?php declare(strict_types = 0);
/*
** Copyright (C) 2021-2026 initMAX s.r.o.
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** 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 Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see <https://www.gnu.org/licenses/>.
**/

namespace Modules\uxMAX\Services;

/**
 * PRO-teaser helper (Zabbix 6.0/6.2 / manifest_version 1 legacy tree).
 *
 * OWNED by the CI library (tools/pro-teaser/ProTeaser.v1.php) and injected into
 * every widget's legacy build by build-edition.py - widgets must NOT copy or
 * edit it. The v1 counterpart of ProTeaser.v2.php: same services/_pro_teaser.json
 * spec, same JS (window.initmaxProTeaser), but built with the v1 global
 * CWidgetField* classes + CWidgetHelper rendering. The widget form makes:
 *
 *   foreach (ProTeaser::fields() as $name => $field) { $this->addField($name, $field); }  // SkeletonForm
 *   $rows = array_merge($rows, ProTeaser::rows($fields));  // skeleton.form.view.php
 *   $form->addItem(new CScriptTag(ProTeaser::initJs($form->getId())));
 *
 * teaser_fields is the SINGLE source of the PRO field definitions for both
 * editions (enabled in PRO, disabled/greyed in FREE).
 */
final class ProTeaser {

	private static ?array $spec = null;

	public static function spec(): array {
		if (self::$spec === null) {
			$file = __DIR__ . '/_pro_teaser.json';
			self::$spec = is_file($file)
				? (json_decode((string) file_get_contents($file), true) ?: [])
				: [];
		}
		return self::$spec;
	}

	public static function edition(): string {
		return self::spec()['edition'] ?? 'pro';
	}

	public static function isFree(): bool {
		return self::edition() !== 'pro';
	}

	/** @return string[] names of the PRO fields, in declared order */
	public static function fieldNames(): array {
		return array_keys(self::spec()['fields'] ?? []);
	}

	/**
	 * @return array name => CWidgetField - the configured PRO field objects
	 * (enabled in PRO, disabled with neutral defaults in FREE). The widget form
	 * registers them through its own addField().
	 */
	public static function fields(): array {
		$free = self::isFree();
		$disabled = ($free && defined('\CWidgetField::FLAG_DISABLED')) ? \CWidgetField::FLAG_DISABLED : 0;

		$out = [];
		foreach (self::spec()['fields'] ?? [] as $name => $f) {
			$field = self::makeField((string) $name, $f);
			if ($field === null) {
				continue;
			}

			$default = ($free && array_key_exists('free_default', $f))
				? $f['free_default'] : ($f['default'] ?? null);
			if ($default !== null) {
				$field->setDefault($default);
			}
			if ($disabled !== 0) {
				$field->setFlags($disabled);
			}
			$out[$name] = $field;
		}
		return $out;
	}

	/**
	 * @return array list of [label, control] rows for the v1 form view
	 * (CWidgetHelper container addItem/addRow).
	 */
	public static function rows(array $fields): array {
		$rows = [];
		foreach (self::spec()['fields'] ?? [] as $name => $f) {
			if (!isset($fields[$name])) {
				continue;
			}
			$control = self::control($f, $fields[$name]);
			if ($control === null) {
				continue;
			}

			$label = \CWidgetHelper::getLabel($fields[$name]);
			$hint_key = $f['hint_key'] ?? '';
			if ($hint_key !== '') {
				$label->addItem(\makeHelpIcon(self::tr($hint_key)));
			}
			$rows[] = [$label, $control];
		}
		return $rows;
	}

	/**
	 * JS that wires window.initmaxProTeaser (logo + locks + footer). The
	 * library source (pro-teaser.js, shipped next to this file by the CI build)
	 * is inlined ahead of the call - the v1 tree has no manifest assets bundle,
	 * so the form view inlines this whole string in a CScriptTag. The
	 * `window.x = window.x || new class` guard makes it idempotent.
	 */
	public static function initJs(string $form_id = '', string $logo_free = 'initmax-free',
			string $logo_pro = 'initmax-pro'): string {
		$spec = self::spec();
		$free = self::isFree();

		$hints = [];
		foreach ($spec['fields'] ?? [] as $name => $f) {
			$hints[$name] = self::tr($f['hint_key'] ?? '');
		}

		$config = [
			'logo_class' => $free ? $logo_free : $logo_pro,
			'disable_fields' => $free ? array_keys($spec['fields'] ?? []) : [],
			'hints' => $hints,
			'upgrade_label' => self::tr($spec['upgrade_label_key'] ?? 'form.pro_teaser.upgrade'),
			'upgrade_url' => $spec['upgrade_url'] ?? '#',
			'feedback_label' => self::trOr('form.pro_teaser.feedback', 'Give us feedback'),
			'feedback_url' => $spec['feedback_url'] ?? '',
		];

		$lib = '';
		$lib_file = __DIR__ . '/pro-teaser.js';
		if (is_file($lib_file)) {
			$lib = (string) file_get_contents($lib_file) . "\n";
		}

		return $lib . 'window.initmaxProTeaser.apply(' . json_encode($form_id) . ', '
			. json_encode($config) . ');';
	}

	private static function makeField(string $name, array $f) {
		$label = self::tr($f['label_key'] ?? '');
		switch ($f['type'] ?? '') {
			case 'checkbox':
				return new \CWidgetFieldCheckBox($name, $label);
			case 'integer':
				return new \CWidgetFieldIntegerBox($name, $label, (int) ($f['min'] ?? 0), (int) ($f['max'] ?? 100));
			case 'radio':
				// setModern: 6.0/6.2 default a radio field to bare radio
				// buttons, while 6.4+ always draws the segmented button bar.
				// Without this the same field looked like a different control
				// on the older frontends.
				return (new \CWidgetFieldRadioButtonList($name, $label, self::options($f)))
					->setModern(true);
			case 'select':
				return new \CWidgetFieldSelect($name, $label, self::options($f));
			case 'color':
				return new \CWidgetFieldColor($name, $label);
			case 'checkboxlist':
				return new \CWidgetFieldCheckBoxList($name, $label);
			default:
				return null;
		}
	}

	private static function control(array $f, $field) {
		switch ($f['type'] ?? '') {
			case 'checkbox':
				return \CWidgetHelper::getCheckBox($field);
			case 'integer':
				return \CWidgetHelper::getIntegerBox($field);
			case 'radio':
				return \CWidgetHelper::getRadioButtonList($field);
			case 'select':
				return \CWidgetHelper::getSelect($field);
			case 'color':
				return \CWidgetHelper::getColor($field, true)->addClass('form-field');
			case 'checkboxlist':
				return \CWidgetHelper::getCheckBoxList($field, self::options($f));
			default:
				return null;
		}
	}

	/** @return array<int|string,string> option value => translated label */
	private static function options(array $f): array {
		$out = [];
		foreach ($f['options'] ?? [] as $value => $label_key) {
			$out[$value] = self::tr((string) $label_key);
		}
		return $out;
	}

	private static function tr(string $key): string {
		return $key === '' ? '' : WidgetTranslator::translate($key);
	}

	/** tr() with a literal fallback for keys older widgets don't translate yet. */
	private static function trOr(string $key, string $fallback): string {
		$out = self::tr($key);

		return ($out === '' || $out === $key) ? $fallback : $out;
	}
}
