<?php

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

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\DuplicatePost;
use PublishPress\Future\Modules\Workflows\Interfaces\ExecutionContextInterface;
use PublishPress\Future\Modules\Workflows\Interfaces\WorkflowExecutionSafeguardInterface;

class DuplicatePostRunner implements StepRunnerInterface
{
    /**
     * @var StepProcessorInterface
     */
    private $stepProcessor;

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

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

    /**
     * @var WorkflowExecutionSafeguardInterface
     */
    private $executionSafeguard;

    public function __construct(
        StepProcessorInterface $stepProcessor,
        ExecutionContextInterface $executionContext,
        LoggerInterface $logger,
        WorkflowExecutionSafeguardInterface $executionSafeguard
    ) {
        $this->stepProcessor = $stepProcessor;
        $this->executionContext = $executionContext;
        $this->logger = $logger;
        $this->executionSafeguard = $executionSafeguard;
    }

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

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

    public function actionCallback(int $postId, array $nodeSettings, array $step)
    {
        $this->stepProcessor->executeSafelyWithErrorHandling(
            $step,
            function ($step, $postId, $nodeSettings) {

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

                $newPostStatus = $nodeSettings['newPostStatus']['status'] ?? 'draft';
                $copyMeta = (bool)($nodeSettings['copyMeta'] ?? false);
                $copyTaxonomies = (bool)($nodeSettings['copyTaxonomies'] ?? false);
                $copyFeaturedImage = (bool)($nodeSettings['copyFeaturedImage'] ?? true);
                $titlePrefix = $nodeSettings['titlePrefix'] ?? '';

                $newPostId = $this->duplicatePost(
                    $postId,
                    $newPostStatus,
                    $copyMeta,
                    $copyTaxonomies,
                    $copyFeaturedImage,
                    $titlePrefix
                );

                if ($newPostId) {
                    $this->executionContext->setVariable(
                        $nodeSlug,
                        [
                            'newPostIds' => [$newPostId]
                        ]
                    );

                    $this->logger->debug(
                        $this->stepProcessor->prepareLogMessage(
                            'Post %1$s duplicated to %2$s on step %3$s',
                            $postId,
                            $newPostId,
                            $nodeSlug
                        )
                    );
                } else {
                    $this->logger->error(
                        $this->stepProcessor->prepareLogMessage(
                            'Failed to duplicate post %1$s on step %2$s',
                            $postId,
                            $nodeSlug
                        )
                    );
                }
            },
            $postId,
            $nodeSettings
        );
    }

    /**
     * Duplicates a post and returns the new post ID
     *
     * @param int $postId The ID of the post to duplicate
     * @param string $newPostStatus The status for the new post
     * @param bool $copyMeta Whether to copy post meta
     * @param bool $copyTaxonomies Whether to copy post taxonomies
     * @param bool $copyFeaturedImage Whether to copy post featured image
     * @param string $titlePrefix Prefix to add to the post title
     * @return int|false The new post ID or false on failure
     */
    private function duplicatePost(
        int $postId,
        string $newPostStatus,
        bool $copyMeta,
        bool $copyTaxonomies,
        bool $copyFeaturedImage,
        string $titlePrefix
    ) {
        // Get the original post
        $post = get_post($postId);

        if (!$post) {
            return false;
        }

        // Create post duplicate
        $newPostId = $this->createNewPost($post, $newPostStatus, $titlePrefix);

        if (is_wp_error($newPostId)) {
            return false;
        }

        if ($copyFeaturedImage) {
            $this->copyFeaturedImage($postId, $newPostId);
        }

        if ($copyTaxonomies) {
            $this->copyTaxonomies($postId, $newPostId, $post->post_type);
        }

        if ($copyMeta) {
            $this->copyMeta($postId, $newPostId);
        }

        return $newPostId;
    }

    private function createNewPost($post, string $status, string $prefix): int
    {
        $newPostData = [
            'post_author'    => $post->post_author,
            'post_content'   => $post->post_content,
            'post_title'     => $prefix . $post->post_title,
            'post_excerpt'   => $post->post_excerpt,
            'post_status'    => $status,
            'post_type'      => $post->post_type,
            'comment_status' => $post->comment_status,
            'ping_status'    => $post->ping_status,
            'post_parent'    => $post->post_parent,
            'menu_order'     => $post->menu_order,
            'post_password'  => $post->post_password,
        ];

        return wp_insert_post($newPostData);
    }

    private function copyFeaturedImage(int $originalPostId, int $newPostId): void
    {
        $thumbnailId = get_post_thumbnail_id($originalPostId);
        if ($thumbnailId) {
            set_post_thumbnail($newPostId, $thumbnailId);
        }
    }

    private function copyTaxonomies(int $originalPostId, int $newPostId, string $postType): void
    {
        $taxonomies = get_object_taxonomies($postType);
        foreach ($taxonomies as $taxonomy) {
            $terms = wp_get_object_terms($originalPostId, $taxonomy, ['fields' => 'slugs']);
            if (!empty($terms) && !is_wp_error($terms)) {
                wp_set_object_terms($newPostId, $terms, $taxonomy);
            }
        }
    }

    private function copyMeta(int $originalPostId, int $newPostId): void
    {
        $postMeta = get_post_meta($originalPostId);
        foreach ($postMeta as $metaKey => $metaValues) {
            foreach ($metaValues as $metaValue) {
                add_post_meta($newPostId, $metaKey, maybe_unserialize($metaValue));
            }
        }
    }
}
