<?php

trait notification_type_t
{
    /**
     * The name of the notification type
     * @var string
     */
    public string $name;
    /**
     * The description of the notification type
     * @var string
     */
    public string $description;
    /**
     * The type of the notification
     * @notation This is the identifier of the notification type, it is used to identify the notification type in the system.
     * @var string
     */
    public string $type;


    /**
     * The constructor of the notification type
     * @throws Exception If the id is not set
     * @throws Exception If the name is not set
     * @throws Exception If the description is not set
     */
    public function __construct()
    {
        self::run();
        self::validate();
    }

    /**
     * The run method, to be implemented by the class using this trait
     * @notation This method should be implemented in the class using this trait, it is not meant to be called directly. It will be called when the notification type is triggered.
     * @throws Exception If type is not set
     * @throws Exception If name is not set
     * @throws Exception If description is not set
     */
    abstract public function run(): void;

    /**
     * Validate the notification type
     * @throws Exception If the type is not set
     * @throws Exception If the name is not set
     * @throws Exception If the description is not set
     */
    public function validate(): void
    {
        if (!isset($this->type)) {
            throw new Exception('The type is not set');
        }
        if (empty($this->name)) {
            throw new Exception('The name is not set');
        }
        if (empty($this->description)) {
            throw new Exception('The description is not set');
        }
    }

    /**
     * Set the type of the notification
     * @param string $type
     * @throws Exception If the type is empty
     * @throws Exception If the type is not a string
     * @throws Exception If the type is not a valid format (e.g. "TYPE_NAME")
     */
    public function set_type(string $type): void
    {
        if (empty($type)) {
            throw new Exception('The type is empty');
        }
        if (!preg_match('/^[A-Z_]+$/', $type)) {
            throw new Exception('The type is not a valid format (e.g. "TYPE_NAME")');
        }
        $this->type = $type;
    }

    /**
     * Set the name of the notification type
     * @param string $name
     * @throws Exception If the name is empty
     */
    public function set_name(string $name): void
    {
        if (empty($name)) {
            throw new Exception('The name is empty');
        }
        $this->name = $name;
    }

    /**
     * Set the description of the notification type
     * @param string $description
     * @throws Exception If the description is empty
     */
    public function set_description(string $description): void
    {
        if (empty($description)) {
            throw new Exception('The description is empty');
        }
        $this->description = $description;
    }

    /**
     * Get the notification type as an array
     * @return array
     */
    public function as_array(): array
    {
        return [
            'type' => $this->type,
            'name' => $this->name,
            'description' => $this->description,
            'data' => self::get_data(),
        ];
    }

    /**
     * Get the data of the notification type
     * @notation This takes all data_* properties and returns them as an array
     * @return array
     */
    public function get_data(): array
    {
        // Get all the properties of the class
        $properties = get_object_vars($this);
        // Filter the properties to only include those that start with "data_"
        $data_properties = array_filter($properties, function ($key) {
            return str_starts_with($key, 'data_');
        }, ARRAY_FILTER_USE_KEY);
        // Remove the "data_" prefix from the keys
        // Return the data properties as an array
        return array_combine(
            array_map(function ($key) {
                return substr($key, 5);
            }, array_keys($data_properties)),
            array_values($data_properties)
        );
    }

    /**
     * Get the notification type as an object
     * @return object
     */
    public function as_object(): object
    {
        return (object)[
            'type' => $this->type,
            'name' => $this->name,
            'description' => $this->description,
            'data' => self::get_data(),
        ];
    }

}