<?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\Includes;

use API;
use CArrayHelper;
use CRoleHelper;
use CSettingsHelper;

/**
 * uxMAX's replacement for CControllerLatest::prepareData(), shared by the
 * latest.view and latest.view.refresh controllers.
 *
 * The ONLY behavioural change is the item status filter: with the
 * "latestshowdisabled" preference on, disabled items are queried as well, so
 * they appear in Monitoring -> Latest data (greyed out, with a badge).
 * Everything else must match core's own query for the frontend in question,
 * and that query is NOT the same across 6.0 - 7.4:
 *
 *   items_rw     6.2+   - the read-write set behind "Execute now".
 *                         6.0 has neither the key nor CRoleHelper's
 *                         ACTIONS_INVOKE_EXECUTE_NOW constant, so reading it
 *                         there is a fatal, not a missing feature.
 *   state filter 7.0+   - 6.x have no `state` in the filter at all.
 *   name field   7.0+   - `name_resolved` replaced `name`. A 6.x item.get
 *                         REJECTS name_resolved outright, and the whole Latest
 *                         data page then renders as an API error.
 *
 * All three are decided by capability, never by a parsed version - the
 * frontend can be upgraded under an installed module.
 */
trait LatestDataQuery {

    /** Is the admin's "show disabled items" preference on? */
    private function uxmaxShowDisabled(): bool {
        return isset($this->module)
            && !empty($this->module->preferences->get()['state']['latestshowdisabled']);
    }

    protected function prepareData(array $filter, $sort_field, $sort_order) {
        $status_filter = $this->uxmaxShowDisabled()
            ? [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED]
            : [ITEM_STATUS_ACTIVE];

        $name_field = Compat::itemNameField();
        $with_items_rw = Compat::latestHasItemsRw();

        $groupids = $filter['groupids'] ? getSubGroups($filter['groupids']) : null;

        $hosts = API::Host()->get([
            'output' => ['hostid', 'name', 'status', 'maintenanceid', 'maintenance_status', 'maintenance_type'],
            'groupids' => $groupids,
            'hostids' => $filter['hostids'] ?: null,
            'preservekeys' => true
        ]);

        $item_filter = ['status' => $status_filter];

        // 6.0 - 6.4 do not carry a state filter; adding the key there sends an
        // unknown field to item.get.
        if (array_key_exists('state', $filter)) {
            $item_filter['state'] = $filter['state'] == -1 ? null : $filter['state'];
        }

        $search_limit = CSettingsHelper::get(CSettingsHelper::SEARCH_LIMIT);
        $select_items_cnt = 0;
        $select_items = [];

        foreach ($hosts as $hostid => $host) {
            if ($select_items_cnt > $search_limit) {
                unset($hosts[$hostid]);
                continue;
            }

            $select_items += API::Item()->get([
                'output' => ['itemid', 'hostid', 'value_type'],
                'hostids' => [$hostid],
                'webitems' => true,
                'evaltype' => $filter['evaltype'],
                'tags' => $filter['tags'] ?: null,
                'filter' => $item_filter,
                'search' => $filter['name'] === '' ? null : [$name_field => $filter['name']],
                'preservekeys' => true
            ]);

            $select_items_cnt = count($select_items);
        }

        if (!$select_items) {
            return $with_items_rw
                ? ['hosts' => [], 'items' => [], 'items_rw' => []]
                : ['hosts' => [], 'items' => []];
        }

        $items = API::Item()->get([
            'output' => ['itemid', 'type', 'hostid', $name_field, 'key_', 'delay', 'history', 'trends',
                'status', 'value_type', 'units', 'description', 'state', 'error'
            ],
            'selectTags' => ['tag', 'value'],
            'selectValueMap' => ['mappings'],
            'itemids' => array_keys($select_items),
            'webitems' => true,
            'preservekeys' => true
        ]);

        if ($name_field !== 'name') {
            $items = CArrayHelper::renameObjectsKeys($items, [$name_field => 'name']);
        }

        $result = ['hosts' => $hosts];

        if ($with_items_rw) {
            // "Invoke Execute now on read-only hosts" ON -> read-write set is
            // the same; OFF -> ask the API which items really are writable.
            $items_rw = $items;

            if (!$this->hasInput('filter_counters') && $this->getUserType() < USER_TYPE_SUPER_ADMIN
                    && !$this->checkAccess(CRoleHelper::ACTIONS_INVOKE_EXECUTE_NOW)) {
                $items_rw = API::Item()->get([
                    'output' => [],
                    'itemids' => array_keys($items),
                    'editable' => true,
                    'preservekeys' => true
                ]);
            }

            $result['items_rw'] = $items_rw;
        }

        if ($sort_field === 'host') {
            $items = array_map(function ($item) use ($hosts) {
                return $item + ['host_name' => $hosts[$item['hostid']]['name']];
            }, $items);

            CArrayHelper::sort($items, [['field' => 'host_name', 'order' => $sort_order]]);
        }
        else {
            CArrayHelper::sort($items, [['field' => 'name', 'order' => $sort_order]]);
        }

        $result['items'] = $items;

        return $result;
    }
}
