<?php

namespace PublishPress\FuturePro\Modules\Workflows\Domain\Steps\Triggers\Runners;

use PublishPress\Future\Core\HookableInterface;
use PublishPress\Future\Modules\Workflows\Domain\Engine\VariableResolvers\PostResolver;
use PublishPress\Future\Modules\Workflows\Interfaces\InputValidatorsInterface;
use PublishPress\Future\Modules\Workflows\Interfaces\StepProcessorInterface;
use PublishPress\Future\Modules\Workflows\Interfaces\TriggerRunnerInterface;
use PublishPress\Future\Modules\Workflows\Interfaces\ExecutionContextInterface;
use PublishPress\Future\Framework\Logger\LoggerInterface;
use PublishPress\Future\Modules\Workflows\Domain\Engine\VariableResolvers\IntegerResolver;
use PublishPress\Future\Modules\Workflows\Domain\Steps\Triggers\Definitions\OnPostRowAction;
use PublishPress\FuturePro\Modules\Workflows\HooksAbstract;

class OnPostRowActionRunner implements TriggerRunnerInterface
{
    public const ACTION_RUN_POST_ROW_ACTION = 'ppfuture-run-post-row-action';

    public const NONCE_RUN_POST_ROW_ACTION = 'ppfuture-workflow-post-row-action-run';

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

    /**
     * @var array
     */
    private $step;

    /**
     * @var StepProcessorInterface
     */
    private $stepProcessor;

    /**
     * @var InputValidatorsInterface
     */
    private $postQueryValidator;

    /**
     * @var int
     */
    private $workflowId;

    /**
     * @var ExecutionContextInterface
     */
    private $executionContext;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var \Closure
     */
    private $expirablePostModelFactory;

    public function __construct(
        HookableInterface $hooks,
        StepProcessorInterface $stepProcessor,
        InputValidatorsInterface $postQueryValidator,
        ExecutionContextInterface $executionContext,
        LoggerInterface $logger,
        \Closure $expirablePostModelFactory
    ) {
        $this->hooks = $hooks;
        $this->stepProcessor = $stepProcessor;
        $this->postQueryValidator = $postQueryValidator;
        $this->executionContext = $executionContext;
        $this->logger = $logger;
        $this->expirablePostModelFactory = $expirablePostModelFactory;
    }

    public static function getNodeTypeName(): string
    {
        return OnPostRowAction::getNodeTypeName();
    }

    public function setup(int $workflowId, array $step): void
    {
        $this->step = $step;
        $this->workflowId = $workflowId;

        $this->hooks->addFilter(HooksAbstract::FILTER_POST_ROW_ACTIONS, [$this, 'registerRowActions'], 15, 2);
        $this->hooks->addFilter(HooksAbstract::FILTER_PAGE_ROW_ACTIONS, [$this, 'registerRowActions'], 15, 2);

        $this->hooks->addAction(HooksAbstract::ACTION_RUN_POST_ROW_ACTION, [$this, 'triggerCallback'], 10, 3);
    }

    public function registerRowActions($actions, $post)
    {
        $node = $this->stepProcessor->getNodeFromStep($this->step);
        $stepSlug = $this->stepProcessor->getSlugFromStep($this->step);

        $this->executionContext->setVariable($stepSlug, [
            'post' => new PostResolver($post, $this->hooks, '', $this->expirablePostModelFactory),
        ]);

        $postQueryArgs = [
            'post' => $post,
            'node' => $node,
        ];

        if (! $this->postQueryValidator->validate($postQueryArgs)) {
            return $actions;
        }

        $nodeSettings = $this->stepProcessor->getNodeSettings($node);

        $actionLabel = trim($nodeSettings['actionLabel'] ?? '');
        $nonce = wp_create_nonce(self::NONCE_RUN_POST_ROW_ACTION);

        if (empty($actionLabel)) {
            $actionLabel = __('Run workflow', 'post-expirator');
        }

        $askConfirmation = $nodeSettings['askForConfirmation']['enabled'] ?? false;
        $askConfirmationMessage = '';
        if ($askConfirmation) {
            $askConfirmationMessage = $nodeSettings['askForConfirmation']['message']['expression']
                ?? __('Are you sure you want to run this workflow?', 'publishpress-future-pro');

            $askConfirmationMessage = $this->executionContext->resolveExpressionsInText(
                $askConfirmationMessage
            );
        }

        $actions[$node['id']] = sprintf(
            '<a href="%s" class="ppfuture-run-workflow-from-post-row" data-ask-confirmation="%s" data-confirmation-message="%s" data-step-slug="%s" data-workflow-id="%d" data-post-id="%d">%s</a>', // phpcs:ignore Generic.Files.LineLength.TooLong
            esc_url(
                admin_url(
                    'admin.php?action=' . self::ACTION_RUN_POST_ROW_ACTION . '&workflowId=' . $this->workflowId
                    . '&stepId=' . $node['id'] . '&postId=' . $post->ID . '&_wpnonce=' . $nonce
                )
            ),
            $askConfirmation ? 1 : 0,
            $askConfirmationMessage,
            $stepSlug,
            $this->workflowId,
            $post->ID,
            esc_html($actionLabel)
        );

        return $actions;
    }

    public function triggerCallback($stepId, $workflowId, $postId)
    {
        $stepSlug = $this->stepProcessor->getSlugFromStep($this->step);
        $node = $this->stepProcessor->getNodeFromStep($this->step);
        $post = get_post($postId);

        if ($workflowId !== $this->workflowId) {
            return false;
        }

        if ($stepId !== $node['id']) {
            return false;
        }

        $this->executionContext->setVariable($stepSlug, [
            'post' => new PostResolver($post, $this->hooks, '', $this->expirablePostModelFactory),
            'postId' => new IntegerResolver($postId),
        ]);

        $this->executionContext->setVariable('global.trigger.postId', $postId);

        $postQueryArgs = [
            'post' => $post,
            'node' => $node,
        ];

        if (! $this->postQueryValidator->validate($postQueryArgs)) {
            return false;
        }

        $this->stepProcessor->executeSafelyWithErrorHandling(
            $this->step,
            function ($step, $stepSlug, $stepId, $workflowId, $post) {
                $this->stepProcessor->triggerCallbackIsRunning();

                $this->logger->debug(
                    $this->stepProcessor->prepareLogMessage(
                        'Trigger is running | Slug: %s | Post ID: %d',
                        $stepSlug,
                        $post->ID
                    )
                );

                $this->hooks->doAction(
                    HooksAbstract::ACTION_WORKFLOW_TRIGGER_EXECUTED,
                    $this->workflowId,
                    $this->step
                );

                $this->stepProcessor->runNextSteps($step);
            },
            $stepSlug,
            $stepId,
            $workflowId,
            $post
        );
    }
}
