<?php

// SPDX-FileCopyrightText: 2023 Icinga GmbH <https://icinga.com>
// SPDX-License-Identifier: GPL-3.0-or-later

namespace Icinga\Module\Notifications\Model;

use DateTime;
use Icinga\Application\Logger;
use Icinga\Module\Notifications\Common\Model;
use Icinga\Module\Notifications\Common\SourceHookLocator;
use Icinga\Module\Notifications\Hook\V2\SourceHook;
use Icinga\Module\Notifications\Repository\SourceRepository;
use ipl\Orm\Behavior\BoolCast;
use ipl\Orm\Behavior\MillisecondTimestamp;
use ipl\Orm\Behaviors;
use ipl\Orm\Query;
use ipl\Orm\Relations;
use ipl\Web\Widget\Icon;
use Throwable;

/**
 * @property int $id The primary key
 * @property string $type Type identifier
 * @property string $name The user-defined name
 * @property ?string $listener_username The username for HTTP authentication
 * @property ?string $listener_password_hash
 * @property ?string $listener_password Helper property to temporarily store the raw password (not persisted to the
 *                                      database). The repository hashes it and assigns the result to
 *                                      {@see self::$listener_password_hash} before saving the source to the database.
 *                                      See the usage in {@see SourceRepository::upsert()}.
 * @property ?string $client_certificate_subject The subject of the TLS client certificate
 * @property DateTime $changed_at
 * @property bool $deleted
 * @property bool $locked
 *
 * @property Query|Objects $object
 * @property Query|Rule $rule
 */
class Source extends Model
{
    /** @var array<string, Icon> */
    private static array $icons = [];

    public function getTableName(): string
    {
        return 'source';
    }

    public function getKeyName(): string
    {
        return 'id';
    }

    public function getColumns(): array
    {
        return [
            'type',
            'name',
            'listener_username',
            'listener_password_hash',
            'client_certificate_subject',
            'changed_at',
            'deleted',
            'locked'
        ];
    }

    public function getColumnDefinitions(): array
    {
        return [
            'type'              => t('Type'),
            'name'              => t('Name'),
            'listener_username' => t('Username'),
            'changed_at'        => t('Changed At')
        ];
    }

    public function getSearchColumns(): array
    {
        return ['type'];
    }

    public function getDefaultSort(): string
    {
        return 'source.name';
    }

    public function createBehaviors(Behaviors $behaviors): void
    {
        $behaviors->add(new MillisecondTimestamp(['changed_at']));
        $behaviors->add(new BoolCast(['deleted', 'locked']));
    }

    public function createRelations(Relations $relations): void
    {
        $relations->hasMany('object', Objects::class);
        $relations->hasMany('rule', Rule::class);
    }

    /**
     * Get the source icon
     *
     * @return Icon
     */
    public function getIcon(): Icon
    {
        if (array_key_exists($this->type, self::$icons)) {
            return self::$icons[$this->type];
        }

        // Fallback, in case an integration is inactive or missing
        $icon = new Icon('share-nodes');

        $hook = SourceHookLocator::forType($this->type);
        if ($hook !== null) {
            /** @var SourceHook $hook */
            try {
                $icon = $hook->getSourceIcon();
            } catch (Throwable $e) {
                Logger::error(
                    'Failed to retrieve source icon for source type "%s": %s',
                    $this->type,
                    $e
                );
            }
        }

        // Cache icons, they don't change during a session but are required multiple times
        self::$icons[$this->type] = $icon;

        return $icon;
    }
}
