<?php

namespace PublishPress\FuturePro\Modules\Workflows\Models;

use PublishPress\Future\Core\DI\Container;
use PublishPress\Future\Framework\Database\Interfaces\DBTableSchemaInterface;
use PublishPress\FuturePro\Core\ServicesAbstract;
use PublishPress\FuturePro\Modules\Workflows\Interfaces\EventDrivenActionModelInterface;

class EventDrivenActionModel implements EventDrivenActionModelInterface
{
    public const STATUS_PENDING = 'pending';

    public const STATUS_RUNNING = 'running';

    public const STATUS_COMPLETED = 'completed';

    public const STATUS_FAILED = 'failed';

    public const STATUS_CANCELLED = 'cancelled';

    public const VALID_STATUSES = [
        self::STATUS_PENDING,
        self::STATUS_RUNNING,
        self::STATUS_COMPLETED,
        self::STATUS_FAILED,
        self::STATUS_CANCELLED,
    ];

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

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @var array
     */
    public $args;

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

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

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

        // TODO: Inject this
        $this->pluginVersion = $container->get(ServicesAbstract::PLUGIN_VERSION);
        $this->dbTableSchema = $container->get(ServicesAbstract::DB_TABLE_WORKFLOW_EVENT_DRIVEN_ACTIONS_SCHEMA);
        $this->logTableSchema = $container->get(ServicesAbstract::DB_TABLE_WORKFLOW_EVENT_DRIVEN_ACTIONS_LOG_SCHEMA);

        $this->tableName = $this->dbTableSchema->getTableName();
        $this->logTableName = $this->logTableSchema->getTableName();
    }

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

        $this->loaded = false;

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

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

        $this->loaded = true;

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

    public function loadFromRow(array $row): bool
    {
        $this->id = (int) $row['id'];
        $this->userId = (int) $row['user_id'];
        $this->workflowId = (int) $row['workflow_id'];
        $this->stepId = $row['step_id'];
        $this->status = $row['status'];
        $this->createdAt = $row['created_at'];
        $this->error = $row['error'];
        $this->startedAt = $row['started_at'];
        $this->completedAt = $row['completed_at'];
        $this->args = json_decode($row['args'], true);

        return true;
    }

    public function create(): ?int
    {
        global $wpdb;

        $this->loaded = false;

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

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

        if (! isset($this->args['pluginVersion'])) {
            $this->args['pluginVersion'] = $this->pluginVersion;
        }

        $result = $wpdb->insert(
            $this->tableName,
            [
                'user_id' => (int) $this->userId,
                'workflow_id' => (int) $this->workflowId,
                'step_id' => sanitize_text_field($this->stepId),
                'status' => self::STATUS_PENDING,
                'created_at' => current_time('mysql'),
                'error' => null,
                'started_at' => null,
                'completed_at' => null,
                'args' => wp_json_encode($this->args),
            ]
        );

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

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

        $this->createLog(__('Event driven action created', 'publishpress-future-pro'));

        $this->loaded = true;

        return $this->id;
    }

    public function markAsRunning(): bool
    {
        if (! $this->belongsToCurrentUser()) {
            return false;
        }

        $this->status = self::STATUS_RUNNING;
        $this->startedAt = current_time('mysql');

        $updateSuccessful = $this->update();

        if ($updateSuccessful) {
            $this->createLog(__('Event driven action started', 'publishpress-future-pro'));
        }

        return $updateSuccessful;
    }

    public function markAsCompleted(): bool
    {
        if (! $this->belongsToCurrentUser()) {
            return false;
        }

        $this->status = self::STATUS_COMPLETED;
        $this->completedAt = current_time('mysql');

        $updateSuccessful = $this->update();

        if ($updateSuccessful) {
            $this->createLog(__('Event driven action completed', 'publishpress-future-pro'));
        }

        return $updateSuccessful;
    }

    public function markAsFailed(string $error = ''): bool
    {
        if (! $this->belongsToCurrentUser()) {
            return false;
        }

        $this->status = self::STATUS_FAILED;
        $this->error = $error;

        $updateSuccessful = $this->update();

        if ($updateSuccessful) {
            $this->createLog(__('Event driven action failed', 'publishpress-future-pro'));
        }

        return $updateSuccessful;
    }

    public function markAsCancelled(): bool
    {
        if (! $this->belongsToCurrentUser()) {
            return false;
        }

        $this->status = self::STATUS_CANCELLED;

        $updateSuccessful = $this->update();

        if ($updateSuccessful) {
            $this->createLog(__('Event driven action cancelled', 'publishpress-future-pro'));
        }

        return $updateSuccessful;
    }

    public function getLogs(): array
    {
        global $wpdb;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
        return $wpdb->get_results(
            $wpdb->prepare(
                "SELECT * FROM %i WHERE action_id = %d ORDER BY created_at ASC",
                $this->logTableName,
                $this->id
            ),
            ARRAY_A
        );
    }

    public function update(): bool
    {
        if (! $this->belongsToCurrentUser()) {
            return false;
        }

        global $wpdb;

        if (empty($this->id)) {
            return false;
        }

        if (empty($this->status)) {
            $this->status = self::STATUS_PENDING;
        }

        $data = [
            'status' => $this->status,
            'started_at' => $this->startedAt,
            'completed_at' => $this->completedAt,
            'error' => $this->error,
            'args' => wp_json_encode($this->args),
        ];

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

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

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

    private function createLog(string $message): void
    {
        global $wpdb;

        $wpdb->insert(
            $this->logTableName,
            [
                'action_id' => $this->id,
                'user_id' => get_current_user_id(),
                'message' => sanitize_text_field($message),
                'created_at' => current_time('mysql'),
            ]
        );
    }
}
