<?php

namespace PublishPress\FuturePro\Modules\Notifications\Models;

use PublishPress\Future\Core\DI\Container;
use PublishPress\Future\Framework\Database\Interfaces\DBTableSchemaInterface;
use PublishPress\FuturePro\Core\ServicesAbstract;
use PublishPress\FuturePro\Modules\Notifications\Interfaces\NotificationModelInterface;
use PublishPress\FuturePro\Modules\Workflows\HooksAbstract;
use PublishPress\FuturePro\Modules\Workflows\Interfaces\EventDrivenActionModelInterface;
use PublishPress\FuturePro\Modules\Workflows\Models\EventDrivenActionModel;

class NotificationModel implements NotificationModelInterface
{
    public const NOTIFICATION_TYPE_INFO = 'info';

    public const NOTIFICATION_TYPE_WARNING = 'warning';

    public const NOTIFICATION_TYPE_ERROR = 'error';

    public const NOTIFICATION_TYPE_SUCCESS = 'success';

    public const NOTIFICATION_TYPE_DEFAULT = self::NOTIFICATION_TYPE_INFO;

    public const VALID_NOTIFICATION_TYPES = [
        self::NOTIFICATION_TYPE_INFO,
        self::NOTIFICATION_TYPE_WARNING,
        self::NOTIFICATION_TYPE_ERROR,
        self::NOTIFICATION_TYPE_SUCCESS,
    ];

    /**
     * @var DBTableSchemaInterface
     */
    private $dbTableSchema;

    /**
     * @var string
     */
    private $tableName;

    /**
     * @var null|int
     */
    public $id;

    /**
     * @var int
     */
    public $userId;

    /**
     * @var string
     */
    public $message;

    /**
     * @var string
     */
    public $type;

    /**
     * @var string
     */
    public $workflowId;

    /**
     * @var string
     */
    public $stepId;

    /**
     * @var bool
     */
    public $isRead;

    /**
     * @var string
     */
    public $createdAt;

    /**
     * @var string
     */
    public $readAt;

    /**
     * @var int
     */
    public $createdBy;

    /**
     * @var int
     */
    public $actionId;

    /**
     * @var bool
     */
    public $loaded = false;

    /**
     * @var HookableInterface
     */
    private $hooks;

    public function __construct()
    {
        $container = Container::getInstance();

        // TODO: Inject this
        $this->dbTableSchema = $container->get(ServicesAbstract::DB_TABLE_WORKFLOW_NOTIFICATIONS_SCHEMA);
        $this->tableName = $this->dbTableSchema->getTableName();
        $this->hooks = $container->get(ServicesAbstract::HOOKS);
    }

    public function load(int $notificationId): bool
    {
        global $wpdb;

        $this->loaded = false;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $notification = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM %i WHERE id = %d",
                $this->tableName,
                $notificationId
            ),
            ARRAY_A
        );

        if (empty($notification)) {
            return false;
        }

        $this->loaded = true;

        return $this->loadFromRow($notification);
    }

    public function loadFromRow(array $row): bool
    {
        $this->id = (int) $row['id'];
        $this->userId = (int) $row['user_id'];
        $this->message = $row['message'];
        $this->type = $row['type'];
        $this->workflowId = (int) $row['workflow_id'];
        $this->stepId = $row['step_id'];
        $this->isRead = (bool) $row['is_read'];
        $this->createdAt = $row['created_at'];
        $this->readAt = $row['read_at'];
        $this->createdBy = (int) $row['created_by'];
        $this->actionId = (int) $row['action_id'];

        return true;
    }

    public function createSuccess(): ?int
    {
        $this->type = self::NOTIFICATION_TYPE_SUCCESS;

        return $this->create();
    }

    public function createError(): ?int
    {
        $this->type = self::NOTIFICATION_TYPE_ERROR;

        return $this->create();
    }

    public function createInfo(): ?int
    {
        $this->type = self::NOTIFICATION_TYPE_INFO;

        return $this->create();
    }

    public function createWarning(): ?int
    {
        $this->type = self::NOTIFICATION_TYPE_WARNING;

        return $this->create();
    }

    public function markAsRead(): bool
    {
        global $wpdb;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
        $result = $wpdb->update(
            $this->tableName,
            [
                'is_read' => 1,
                'read_at' => current_time('mysql'),
            ],
            [
                'id' => $this->id
            ]
        );

        $this->isRead = $result !== false;

        if ($this->isRead) {
            $this->hooks->doAction(
                HooksAbstract::ACTION_MARKED_NOTIFICATION_AS_READ,
                $this->id,
                $this->userId
            );
        }

        return $this->isRead;
    }

    public function create(array $eventArgs = [], array $runtimeVariables = []): ?int
    {
        global $wpdb;

        $this->loaded = false;

        $this->type = strtolower($this->type);

        if (! in_array($this->type, self::VALID_NOTIFICATION_TYPES)) {
            $this->type = self::NOTIFICATION_TYPE_DEFAULT;
        }

        if (empty($this->userId)) {
            return null;
        }

        if (empty($this->message)) {
            return null;
        }

        $this->createdBy = get_current_user_id();
        $this->isRead = 0;

        $result = $wpdb->insert(
            $this->tableName,
            $this->toRow()
        );

        if ($result === false) {
            $this->id = null;
            return null;
        }

        $this->id = $wpdb->insert_id;

        $this->registerEventDrivenAction($eventArgs, $runtimeVariables);

        $this->loaded = true;

        $this->hooks->doAction(
            HooksAbstract::ACTION_CREATED_NOTIFICATION,
            $this->id,
            $this->type
        );

        return $this->id;
    }

    public function loaded(): bool
    {
        return $this->loaded;
    }

    public function belongsToCurrentUser(): bool
    {
        return $this->userId === get_current_user_id();
    }

    public function update(): bool
    {
        global $wpdb;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
        $result = $wpdb->update(
            $this->tableName,
            $this->toRow(),
            ['id' => $this->id]
        );

        return $result !== false;
    }

    private function toRow(): array
    {
        return [
            'user_id' => (int) $this->userId,
            'message' => sanitize_textarea_field($this->message),
            'type' => $this->type,
            'workflow_id' => (int) $this->workflowId,
            'step_id' => sanitize_text_field($this->stepId),
            'is_read' => (int) $this->isRead,
            'created_by' => (int) $this->createdBy,
            'action_id' => (int) $this->actionId,
        ];
    }

    private function registerEventDrivenAction(array $eventData, array $runtimeVariables): void
    {
        $container = Container::getInstance();
        $pluginVersion = $container->get(ServicesAbstract::PLUGIN_VERSION);

        $args = [
            'step' => [
                'nodeId' => $this->stepId,
            ],
            'event' => 'onNotificationOption',
            'eventData' => array_merge(
                [
                    'notificationId' => $this->id,
                    'options' => [
                        [
                            'name' => 'dismiss',
                            'label' => __('Dismiss', 'publishpress-future-pro'),
                            'hint' => __('Dismiss the notification', 'publishpress-future-pro'),
                        ],
                    ],
                ],
                $eventData
            ),
            'runtimeVariables' => $runtimeVariables,
            'pluginVersion' => $pluginVersion,
        ];

        $eventDrivenActionModel = new EventDrivenActionModel();
        $eventDrivenActionModel->userId = $this->userId;
        $eventDrivenActionModel->workflowId = $this->workflowId;
        $eventDrivenActionModel->stepId = $this->stepId;
        $eventDrivenActionModel->args = $args;

        $this->actionId = $eventDrivenActionModel->create();
        $this->update();
    }
}
