<?php
/*
** 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;

use CButton;
use CCheckBox;
use CCol;
use CColHeader;
use CDiv;
use CFile;
use CForm;
use CInput;
use CLabel;
use CRow;
use CSelect;
use CSimpleButton;
use CSubmit;
use CTable;
use CTabView;
use CTextBox;
use CUrl;
use CVar;
use Modules\uxMAX\Includes\Compat;

/**
 * THE uxMAX administration form - written ONCE, rendered by both module trees.
 *
 * uxMAX ships two trees because Zabbix accepts manifest_version 1 only below
 * 6.4 and manifest_version 2 only from 6.4 up. The views directory is the one
 * place the build cannot share automatically, so `views/mod.uxmax.form.php` and
 * `legacy/views/mod.uxmax.form.php` are two-line files that both call this
 * class. That is deliberate: the moment the markup lives in two files, 6.0 and
 * 7.4 start showing different forms, and nothing in CI notices.
 *
 * Everything that differs between 6.0 and 7.4 is resolved through Compat, so
 * the field set, the labels and the ORDER are identical on every supported
 * version - which is exactly what the customer sees.
 */
final class ConfigFormPage {

    /**
     * @param \CView $view  The view rendering this page (for addJsFile()).
     * @param array  $data  Preferences, as ConfigurationForm assembled them.
     */
    public static function show($view, array $data): void {
        $view->addJsFile('multilineinput.js');

        $content = (new CDiv([
            self::section(ModuleTranslator::translate('form.section.color-tags'), self::colorTagRows($data)),
            self::section(ModuleTranslator::translate('form.section.dashboards'), self::dashboardRows($data)),
            self::section(ModuleTranslator::translate('form.section.interface'), self::interfaceRows($data)),
            self::section(ModuleTranslator::translate('form.section.appearance'), self::appearanceRows($data)),
            self::section(ModuleTranslator::translate('form.section.syntax-highlighting'), self::syntaxRows($data))
        ]))->addClass('uxmax-form-container');

        $form = (new CForm('post', (new CUrl('zabbix.php'))->getUrl(), 'multipart/form-data'))
            ->addClass('uxmax-form');

        // 6.0/6.2 guard with a `sid` field CForm adds itself, so this is empty
        // there; 6.4+ hand back the CSRF token field.
        foreach (Compat::csrfVars('mod.uxmax.form.update') as $name => $value) {
            $form->addVar($name, $value);
        }

        $form
            ->addVar('action', 'mod.uxmax.form.update')
            ->addItem(getMessages())
            ->addItem(
                (new CTabView())
                    ->addTab('uxmax', ModuleTranslator::translate('tabs.general'), $content)
                    ->setFooter(makeFormFooter(
                        new CSubmit('update', ModuleTranslator::translate('form.button.update'))
                    ))
            );

        Compat::page(ModuleTranslator::translate('module.name'))
            ->addItem($form)
            ->show();
    }

    /**
     * One settings row: label on the left, field on the right with a
     * description box below it. Rows are separated by a rule via CSS.
     *
     * @param string|array $label_text
     * @param mixed        $field
     */
    private static function row($label_text, $field, string $description, ?string $for = null): CDiv {
        return (new CDiv([
            (new CDiv($for !== null ? new CLabel($label_text, $for) : new CLabel($label_text)))
                ->addClass('uxmax-setting-label'),
            (new CDiv([
                $field,
                (new CDiv($description))->addClass('uxmax-field-desc')
            ]))->addClass('uxmax-setting-field')
        ]))->addClass('uxmax-setting-row');
    }

    private static function section(string $title, array $rows): CDiv {
        return (new CDiv([
            (new CDiv($title))->addClass('uxmax-section-title'),
            (new CDiv($rows))->addClass('uxmax-section-body')
        ]))->addClass('uxmax-section');
    }

    private static function dashboardRows(array $data): array {
        return [
            self::row(
                ModuleTranslator::translate('form.hide-widget-header'),
                (new CCheckBox('state[hidewidgetheader]', 1))->setChecked((int) $data['state']['hidewidgetheader']),
                ModuleTranslator::translate('hints.hide-widget-header'),
                'hidewidgetheader'
            ),
            self::row(
                ModuleTranslator::translate('form.compact-dashboard'),
                (new CCheckBox('state[compactdashboard]', 1))->setChecked((int) $data['state']['compactdashboard']),
                ModuleTranslator::translate('hints.compact-dashboard'),
                'compactdashboard'
            )
        ];
    }

    private static function interfaceRows(array $data): array {
        return [
            self::row(
                ModuleTranslator::translate('form.enable-dragging-of-modal-windows'),
                (new CCheckBox('state[windrag]', 1))->setChecked((int) $data['state']['windrag']),
                ModuleTranslator::translate('hints.windrag'),
                'windrag'
            ),
            self::row(
                ModuleTranslator::translate('form.modal-window-minimal-width.label'),
                [
                    (new CCheckBox('state[modalwidth]', 1))->setChecked((int) $data['state']['modalwidth']),
                    (new CTextBox('modalwidth[value]', $data['modalwidth']['value']))
                        ->removeId()
                        ->setWidth(ZBX_TEXTAREA_SMALL_WIDTH),
                    (new CDiv())->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
                    (new CSimpleButton(ModuleTranslator::translate('form.modal-window-minimal-width.button.open')))
                        ->setId('uxmax-modal-button')
                        ->addClass(ZBX_STYLE_BTN_ALT)
                ],
                ModuleTranslator::translate('hints.modal-width')
            ),
            self::row(
                ModuleTranslator::translate('form.latest-show-disabled-items'),
                (new CCheckBox('state[latestshowdisabled]', 1))->setChecked((int) $data['state']['latestshowdisabled']),
                ModuleTranslator::translate('hints.latest-show-disabled'),
                'latestshowdisabled'
            )
        ];
    }

    private static function appearanceRows(array $data): array {
        return [
            self::row(
                ModuleTranslator::translate('form.custom-color-theme'),
                [
                    new CDiv([
                        (new CCheckBox('state[asidebg]', 1))->setChecked((int) $data['state']['asidebg']),
                        (new CLabel([
                            (new CInput('color', 'color[asidebg]', $data['color']['asidebg']))
                                ->setEnabled(!!$data['state']['asidebg']),
                            ModuleTranslator::translate('form.navigation-background-color')
                        ]))->addClass(!!$data['state']['asidebg'] ? null : ZBX_STYLE_DISABLED)
                    ]),
                    new CDiv([
                        (new CCheckBox('state[bodybg]', 1))->setChecked((int) $data['state']['bodybg']),
                        (new CLabel([
                            (new CInput('color', 'color[bodybg]', $data['color']['bodybg']))
                                ->setEnabled(!!$data['state']['bodybg']),
                            ModuleTranslator::translate('form.body-background-color')
                        ]))->addClass(!!$data['state']['bodybg'] ? null : ZBX_STYLE_DISABLED)
                    ])
                ],
                ModuleTranslator::translate('hints.color-theme')
            ),
            self::row(
                ModuleTranslator::translate('form.custom-font.label'),
                [
                    (new CCheckBox('state[customfont]', 1))->setChecked((int) $data['state']['customfont']),
                    (new CDiv([
                        (new CTable())
                            ->setFooter(
                                (new CCol(
                                    Compat::buttonLink(ModuleTranslator::translate('form.custom-font.button.add'))
                                        ->addClass('element-table-add')
                                ))->setColSpan(3)
                            ),
                        Compat::templateTag('fonts-row-tmpl', self::fontRowTemplate()),
                        Compat::templateTag('fonts-data', json_encode($data['fonts']))
                    ]))
                        ->setId('uxmax-fonts-table')
                        ->addStyle('vertical-align: top;')
                        ->addClass(ZBX_STYLE_TABLE_FORMS_SEPARATOR)
                ],
                ModuleTranslator::translate('hints.custom-font')
            )
        ];
    }

    private static function fontRowTemplate(): CRow {
        return (new CRow([
            [
                (new CInput('radio', 'fonts_enabled', '#{rowNum}'))
                    ->setAttribute('#{enabled}', '')
                    ->removeId(),
                Compat::nbsp(),
                (new CSelect('fonts[#{rowNum}][type]'))
                    ->setId('fonts_type_#{rowNum}')
                    ->addOptions(CSelect::createOptionsFromArray([
                        Preferences::FONT_TYPE_CSS_URL =>
                            ModuleTranslator::translate('form.custom-font.variant.use-external-css'),
                        Preferences::FONT_TYPE_FILE =>
                            ModuleTranslator::translate('form.custom-font.variant.upload-font-file')
                    ]))
                    ->setWidth(ZBX_TEXTAREA_SMALL_WIDTH)
            ],
            [
                (new CDiv([
                    (new CTextBox('fonts[#{rowNum}][url]', '#{url}'))
                        ->removeId()
                        ->setAttribute('placeholder',
                            ModuleTranslator::translate('form.custom-font.variant.google-font-url'))
                        ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH),
                    Compat::nbsp(),
                    (new CCheckBox('fonts[#{rowNum}][selfhosted]', 1))
                        ->setId('fonts_selfhosted_#{rowNum}')
                        ->setAttribute('#{selfhosted}', ''),
                    (new CLabel(ModuleTranslator::translate('form.custom-font.hint.store-font-locally'),
                        'fonts_selfhosted_#{rowNum}'))
                ]))->setId('vs_fonts_url_#{rowNum}'),
                (new CDiv([
                    (new CFile('fonts[#{rowNum}][file]', ''))
                        ->setId('fonts_file_#{rowNum}')
                        ->setAttribute('accept', '.woff,.woff2,.ttf,.otf,.eot')
                        ->addStyle('display: none;'),
                    (new CTextBox('fonts[#{rowNum}][file_name]', '#{file_name}', true))
                        ->setAttribute('data-file-name', '#{file_name}')
                        ->setAttribute('placeholder',
                            ModuleTranslator::translate('form.custom-font.hint.supported-formats'))
                        ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH),
                    Compat::nbsp(),
                    // The NAME is load-bearing: uxmax.form.js opens the hidden
                    // file input from `button[name$="[select]"]`.
                    (new CButton('fonts[#{rowNum}][select]',
                        ModuleTranslator::translate('form.custom-font.button.select')))
                        ->addClass(ZBX_STYLE_BTN_ALT)
                ]))->setId('vs_fonts_file_#{rowNum}'),
                new CVar('fonts[#{rowNum}][pngid]', '#{pngid}'),
                new CVar('fonts[#{rowNum}][font_family]', '#{font_family}')
            ],
            Compat::buttonLink(ModuleTranslator::translate('form.custom-font.button.remove'))
                ->addClass('element-table-remove')
        ]))->addClass('form_row');
    }

    private static function colorTagRows(array $data): array {
        return [
            self::row(
                ModuleTranslator::translate('form.color-tags.label'),
                [
                    (new CCheckBox('state[colortags]', 1))->setChecked((int) $data['state']['colortags']),
                    (new CDiv([
                        (new CTable())
                            ->setHeader([
                                new CColHeader(ModuleTranslator::translate('form.color-tags.table.match')),
                                new CColHeader(ModuleTranslator::translate('form.color-tags.table.string')),
                                '',
                                ''
                            ])
                            ->setFooter(
                                (new CCol(
                                    Compat::buttonLink(ModuleTranslator::translate('form.color-tags.button.add'))
                                        ->addClass('element-table-add')
                                ))->setColSpan(4)
                            ),
                        Compat::templateTag('colortag-row-tmpl', self::colorTagRowTemplate()),
                        Compat::templateTag('colortag-data', json_encode($data['colortags']))
                    ]))
                        ->setId('uxmax-colortag-table')
                        ->addClass(ZBX_STYLE_TABLE_FORMS_SEPARATOR)
                ],
                ModuleTranslator::translate('hints.color-tags')
            ),
            self::row(
                ModuleTranslator::translate('form.allow-user-override'),
                [
                    new CVar('state[allowuseroverride]', '0'),
                    (new CCheckBox('state[allowuseroverride]', 1))
                        ->setChecked((int) $data['state']['allowuseroverride'])
                ],
                ModuleTranslator::translate('hints.allow-user-override'),
                'allowuseroverride'
            )
        ];
    }

    private static function colorTagRowTemplate(): CRow {
        return (new CRow([
            (new CSelect('colortags[#{rowNum}][match]'))
                ->removeId()
                ->addOptions(CSelect::createOptionsFromArray([
                    Preferences::MATCH_BEGIN => ModuleTranslator::translate('form.color-tags.match.starts-with'),
                    Preferences::MATCH_CONTAIN => ModuleTranslator::translate('form.color-tags.match.contains'),
                    Preferences::MATCH_END => ModuleTranslator::translate('form.color-tags.match.ends-with')
                ]))
                ->setWidth(ZBX_TEXTAREA_SMALL_WIDTH),
            (new CTextBox('colortags[#{rowNum}][value]', '#{value}'))
                ->removeId()
                ->setAttribute('placeholder', ModuleTranslator::translate('form.color-tags.value'))
                ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH),
            new CLabel([
                (new CInput('color', 'colortags[#{rowNum}][color]', '#{color}'))->removeId()
            ]),
            Compat::buttonLink(ModuleTranslator::translate('form.color-tags.button.remove'))
                ->addClass('element-table-remove')
        ]))->addClass('form_row');
    }

    private static function syntaxRows(array $data): array {
        return [
            self::row(
                ModuleTranslator::translate('form.syntax-highlight.label'),
                [
                    (new CTextBox('syntax[fontSize]', $data['syntax']['fontSize']))
                        ->removeId()
                        ->setWidth(ZBX_TEXTAREA_SMALL_WIDTH),
                    (new CDiv())->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
                    (new CSelect('syntax[font]'))
                        ->removeId()
                        ->setValue($data['syntax']['font'])
                        ->addOptions(CSelect::createOptionsFromArray(Preferences::FONT))
                        ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH),
                    Compat::templateTag('import-fonts-data', json_encode(array_values(Preferences::FONT_IMPORT_URL))),
                    new CDiv([
                        (new CCheckBox('state[syntax]', 1))->setChecked((int) $data['state']['syntax']),
                        new CLabel(ModuleTranslator::translate('form.syntax-highlight.js'), 'state[syntax]')
                    ]),
                    new CDiv([
                        (new CCheckBox('state[exprhighlight]', 1))->setChecked((int) $data['state']['exprhighlight']),
                        new CLabel(ModuleTranslator::translate('form.syntax-highlight.expression'),
                            'state[exprhighlight]')
                    ]),
                    (new CDiv(implode("\n", [
                        '// Playground syntax higlight mode javascript.',
                        'function foo() {',
                        '    let x = "Hello world";',
                        '',
                        '    return x;',
                        '}'
                    ])))->setId('uxmax-ace-playground')
                ],
                ModuleTranslator::translate('hints.syntax-highlight')
            )
        ];
    }
}
