<?php

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

use PublishPress\Future\Core\HookableInterface;
use PublishPress\Future\Modules\Workflows\Interfaces\StepRunnerInterface;
use PublishPress\Future\Modules\Workflows\Interfaces\StepProcessorInterface;
use PublishPress\Future\Framework\Logger\LoggerInterface;
use PublishPress\Future\Modules\Workflows\Domain\Steps\Actions\Definitions\UpdatePost;
use PublishPress\Future\Modules\Workflows\Interfaces\ExecutionContextInterface;
use PublishPress\Future\Framework\WordPress\Models\PostModel;
use PublishPress\Future\Modules\Workflows\Domain\Engine\Traits\TimestampCalculator;
use PublishPress\Future\Modules\Workflows\Domain\Engine\VariableResolvers\PostResolver;
use WP_User;

class UpdatePostRunner implements StepRunnerInterface
{
    use TimestampCalculator;

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

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

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

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

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

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

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

    public function setup(array $step): void
    {
        $this->stepProcessor->setup($step, [$this, 'actionCallback']);
    }

    private function checkPostDataShouldBeUpdated(array $nodeSettings, string $key): bool
    {
        return isset($nodeSettings['postData'][$key])
            && isset($nodeSettings['postData'][$key]['update'])
            && $nodeSettings['postData'][$key]['update'] === true;
    }

    public function actionCallback(int $postId, array $nodeSettings, array $step)
    {
        $this->stepProcessor->executeSafelyWithErrorHandling(
            $step,
            function ($step, $postId, $nodeSettings) {
                /** @var PostModel $postModel */
                $postModel = call_user_func($this->expirablePostModelFactory, $postId);

                $newPostData = [];

                $nodeSlug = $this->stepProcessor->getSlugFromStep($step);

                if ($this->checkPostDataShouldBeUpdated($nodeSettings, 'title')) {
                    $newPostData['post_title'] = $this->executionContext->resolveExpressionsInText(
                        $nodeSettings['postData']['title']['expression']
                    );

                    $this->logger->debug(
                        $this->stepProcessor->prepareLogMessage(
                            'Updating post title to: %1$s | Slug: %2$s',
                            $newPostData['post_title'],
                            $nodeSlug
                        )
                    );
                }

                if ($this->checkPostDataShouldBeUpdated($nodeSettings, 'content')) {
                    $newPostData['post_content'] = $this->executionContext->resolveExpressionsInText(
                        $nodeSettings['postData']['content']['expression']
                    );

                    $this->logger->debug(
                        $this->stepProcessor->prepareLogMessage(
                            'Updating post content | Slug: %1$s',
                            $nodeSlug
                        )
                    );
                }

                if ($this->checkPostDataShouldBeUpdated($nodeSettings, 'excerpt')) {
                    $newPostData['post_excerpt'] = $this->executionContext->resolveExpressionsInText(
                        $nodeSettings['postData']['excerpt']['expression']
                    );

                    $this->logger->debug(
                        $this->stepProcessor->prepareLogMessage(
                            'Updating post excerpt | Slug: %1$s',
                            $nodeSlug
                        )
                    );
                }

                if ($this->checkPostDataShouldBeUpdated($nodeSettings, 'name')) {
                    $newPostData['post_name'] = $this->executionContext->resolveExpressionsInText(
                        $nodeSettings['postData']['name']['expression']
                    );

                    $this->logger->debug(
                        $this->stepProcessor->prepareLogMessage(
                            'Updating post name to: %1$s | Slug: %2$s',
                            $newPostData['post_name'],
                            $nodeSlug
                        )
                    );
                }

                if ($this->checkPostDataShouldBeUpdated($nodeSettings, 'author')) {
                    $authors = $nodeSettings['postData']['author']['authors'];

                    if (empty($authors)) {
                        $this->logger->warning(
                            $this->stepProcessor->prepareLogMessage(
                                'No authors found for post %1$s | Slug: %2$s',
                                $postId,
                                $nodeSlug
                            )
                        );
                    } else {
                        $newPostData['post_author'] = (int) $authors[0];

                        $this->logger->debug(
                            $this->stepProcessor->prepareLogMessage(
                                'Updating post author to: %1$s | Slug: %2$s',
                                $newPostData['post_author'],
                                $nodeSlug
                            )
                        );
                    }
                }

                if ($this->checkPostDataShouldBeUpdated($nodeSettings, 'date')) {
                    $timestamp = $this->calculateTimestamp(
                        $nodeSettings['postData']['date']['dateStrategy'] ?? 'now',
                        $nodeSettings['postData']['date']['dateSource'] ?? 'calendar',
                        $nodeSettings['postData']['date']['specificDate'] ?? '',
                        $nodeSettings['postData']['date']['customDateSource']['expression'] ?? '',
                        $nodeSettings['postData']['date']['dateOffset'] ?? ''
                    );

                    $newPostData['post_date'] = wp_date('Y-m-d H:i:s', $timestamp);
                    $newPostData['post_date_gmt'] = get_gmt_from_date($newPostData['post_date']);

                    $this->logger->debug(
                        $this->stepProcessor->prepareLogMessage(
                            'Updating post date to: %1$s | Slug: %2$s',
                            $newPostData['post_date'],
                            $nodeSlug
                        )
                    );
                }

                if ($this->checkPostDataShouldBeUpdated($nodeSettings, 'discussion')) {
                    $newPostData['comment_status'] = $nodeSettings['postData']['discussion']['commentStatus'] ?? 'open';
                    $newPostData['ping_status'] = $nodeSettings['postData']['discussion']['pingStatus'] ?? 'open';

                    $this->logger->debug(
                        $this->stepProcessor->prepareLogMessage(
                            'Updating post discussion | Slug: %1$s',
                            $nodeSlug
                        )
                    );
                }

                if (! empty($newPostData)) {
                    $postModel->update($newPostData);
                }

                $updatedPost = get_post($postId);

                $this->executionContext->setVariable($nodeSlug, [
                    'updatedPost' => new PostResolver(
                        $updatedPost,
                        $this->hooks,
                        $postModel->getPermalink(),
                        $this->expirablePostModelFactory
                    ),
                ]);
            },
            $postId,
            $nodeSettings
        );
    }
}
