<?php

namespace FleekDash\Controllers;

use FleekDash\Core\AuthMiddleware;
use FleekDash\Core\CacheInvalidationManager;
use FleekDash\Core\TemplateUpdateException;

// Load debug helpers
if (!function_exists('fleekdash_log_debug')) {
    require_once FLEEKDASH_PLUGIN_DIR . 'includes/Core/debug-helpers.php';
}
use FleekDash\Libs\API\ResponseService;
use FleekDash\Controllers\Post\PageTemplateHelper;
use FleekDash\Controllers\Post\PostCacheHelper;
use FleekDash\Controllers\Post\PostFilterOptionsHelper;
use FleekDash\Controllers\Post\PostFormatter;
use FleekDash\Controllers\Post\PostIndexHelper;
use FleekDash\Controllers\Post\PostMetaProcessor;
use FleekDash\Controllers\Post\PostSchemaProvider;
use FleekDash\Controllers\Post\PostSearchFilter;
use FleekDash\Controllers\Post\PostDuplicateHandler;
use FleekDash\Controllers\Post\PostStoreHandler;
use FleekDash\Controllers\Post\PostUpdateHandler;
use WP_Query;

/**
 * Class PostController
 *
 * Handles post-related API endpoints for all post types.
 *
 * @package FleekDash\Controllers
 */
class PostController
{
    /** Transient cache TTL for posts list (5 minutes) */
    private const POSTS_LIST_CACHE_TTL = 300;

    /** Error message when post is not found */
    private const MSG_POST_NOT_FOUND = 'Post not found';

    /**
     * Get a paginated list of posts
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function index($request)
    {
        try {
            $validationError = $this->validateIndexRequest($request);
            if ($validationError !== null) {
                return $validationError;
            }
            return $this->executeIndexQuery($request);
        } catch (\Exception $e) {
            return ResponseService::error('Failed to fetch posts: ' . $e->getMessage());
        }
    }

    /**
     * Avoid serving a stale transient that cached an empty list while stats still show rows.
     *
     * @param mixed $body Cached response body
     */
    private function cachedPostsListHasRows($body): bool
    {
        if (!is_array($body)) {
            return false;
        }

        $data = $body['data'] ?? null;

        return is_array($data) && count($data) > 0;
    }

    /**
     * Validate index request (post type). Returns error response or null if valid.
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response|null
     */
    private function validateIndexRequest($request): ?\WP_REST_Response
    {
        $post_type = $request->get_param('post_type') ?? 'post';
        if ($post_type === 'all') {
            $post_type = 'any';
        }
        if (!\post_type_exists($post_type) && $post_type !== 'any') {
            return ResponseService::error("Invalid post type: {$post_type}", null, 400);
        }
        return null;
    }

    /**
     * Execute posts list query (cache check, WP_Query, format, store cache).
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    private function executeIndexQuery($request): \WP_REST_Response
    {
        $page = (int) ($request->get_param('page') ?? 1);
        $per_page = (int) ($request->get_param('per_page') ?? 10);
        $search = $request->get_param('search');
        $orderby = $request->get_param('orderby') ?? 'date';
        $order = strtoupper($request->get_param('order') ?? 'DESC');
        $post_type = $request->get_param('post_type') ?? 'post';
        if ($post_type === 'all') {
            $post_type = 'any';
        }
        $post_status = PostIndexHelper::normalizePostStatus($request);
        $taxonomies = $request->get_param('taxonomies') ?? [];

        $bust_cache = in_array(
            $request->get_param('bust_cache'),
            ['1', 'true', true],
            true
        );

        if (!$bust_cache) {
            $params = compact('post_type', 'post_status', 'page', 'per_page', 'orderby', 'order', 'search', 'taxonomies');
            $cache_key = PostIndexHelper::buildCacheKey($params);
            $cached = \get_transient($cache_key);
            if (
                is_array($cached)
                && isset($cached['body'], $cached['created_at'])
                && !CacheInvalidationManager::isCacheStale($cache_key, (int) $cached['created_at'])
                && $this->cachedPostsListHasRows($cached['body'])
            ) {
                return new \WP_REST_Response($cached['body'], 200);
            }
        }

        $params = compact('post_type', 'post_status', 'page', 'per_page', 'orderby', 'order', 'search', 'taxonomies');
        $args = PostIndexHelper::buildQueryArgs($params);

        if ($search) {
            \add_filter('posts_where', [PostSearchFilter::class, 'extendSearchToIncludeSlug'], 10, 2);
        }
        $query = new WP_Query($args);
        if ($search) {
            \remove_filter('posts_where', [PostSearchFilter::class, 'extendSearchToIncludeSlug'], 10);
        }
        $posts = array_map([PostFormatter::class, 'formatPostForGrid'], $query->posts);
        $response_body = [
            'success' => true,
            'data' => $posts,
            'meta' => [
                'pagination' => [
                    'current_page' => $page,
                    'per_page' => $per_page,
                    'total' => (int) $query->found_posts,
                    'total_pages' => (int) $query->max_num_pages,
                ],
            ],
        ];
        if (!$bust_cache) {
            $params = compact('post_type', 'post_status', 'page', 'per_page', 'orderby', 'order', 'search', 'taxonomies');
            \set_transient(
                PostIndexHelper::buildCacheKey($params),
                ['body' => $response_body, 'created_at' => time()],
                self::POSTS_LIST_CACHE_TTL
            );
        }
        return new \WP_REST_Response($response_body, 200);
    }

    /**
     * Get a single post by ID
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function show($request)
    {
        try {
            AuthMiddleware::check('authenticated');

            $post = \get_post($request->get_param('id'));

            if (!$post) {
                return ResponseService::error(self::MSG_POST_NOT_FOUND, null, 404);
            }

            return ResponseService::success(PostFormatter::formatPostForGrid($post));

        } catch (\Exception $e) {
            return ResponseService::error('Failed to fetch post: ' . $e->getMessage());
        }
    }

    /**
     * Create a new post
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function store($request)
    {
        try {
            return PostStoreHandler::execute($request);
        } catch (\Exception $e) {
            return ResponseService::error('Failed to create post: ' . $e->getMessage());
        }
    }

    /**
     * Update an existing post
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function update($request)
    {
        try {
            return PostUpdateHandler::execute($request);
        } catch (\Exception $e) {
            return ResponseService::error('Failed to update post: ' . $e->getMessage());
        }
    }

    /**
     * Delete a post
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function destroy($request)
    {
        try {
            $auth_result = AuthMiddleware::check('authenticated');
            $post_id = (int) $request->get_param('id');
            $force = (bool) ($request->get_param('force') ?? false);
            $post = \get_post($post_id);

            $error = null;
            if (!$post) {
                $error = ResponseService::error(self::MSG_POST_NOT_FOUND, null, 404);
            } elseif ($auth_result['authenticated'] && !\current_user_can("delete_{$post->post_type}") && !\current_user_can('manage_options')) {
                $error = ResponseService::error("You don't have permission to delete this post", null, 403);
            } else {
                $deleted = $force ? \wp_delete_post($post_id, true) : wp_trash_post($post_id);
                $error = !$deleted ? ResponseService::error('Failed to delete post') : null;
            }

            if ($error !== null) {
                return $error;
            }
            return ResponseService::success(null, 'Post deleted successfully');
        } catch (\Exception $e) {
            return ResponseService::error('Failed to delete post: ' . $e->getMessage());
        }
    }

    /**
     * Duplicate a post/page/product with full content and meta (page builders, Gutenberg, templates, etc.)
     *
     * Content is read and written directly via DB for post_content/post_excerpt so that WordPress
     * content filters (e.g. content_save_pre) do not strip Gutenberg block markup (HTML comments).
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function duplicate($request)
    {
        try {
            return PostDuplicateHandler::execute($request);
        } catch (\Exception $e) {
            return ResponseService::error('Failed to duplicate post: ' . $e->getMessage());
        }
    }

    /**
     * Get a list of all post types
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function getPostTypes($request)
    {
        return PostSchemaProvider::getPostTypes($request);
    }

    /**
     * Get all taxonomies for a post type
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function getTaxonomies($request)
    {
        return PostSchemaProvider::getTaxonomies($request);
    }

    /**
     * Get authors for filtering
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function getAuthors($request)
    {
        try {
            $post_type = $request->get_param('post_type') ?? 'post';
            $cache_key = 'fleekdash_authors_' . \sanitize_key($post_type);
            return PostCacheHelper::getCachedOrCompute($cache_key, 5 * MINUTE_IN_SECONDS, function () use ($post_type) {
                $authors = get_users([
                    'orderby' => 'display_name',
                    'order' => 'ASC',
                    'fields' => ['ID', 'display_name', 'user_email', 'user_login']
                ]);
                $author_options = [];
                foreach ($authors as $author) {
                    $post_count = count_user_posts($author->ID, $post_type, true);
                    $avatar_url = get_avatar_url($author->ID, ['size' => 32]);
                    $author_options[] = [
                        'id' => (string) $author->ID,
                        'name' => $author->display_name,
                        'email' => $author->user_email,
                        'username' => $author->user_login,
                        'avatar_url' => $avatar_url,
                        'post_count' => $post_count
                    ];
                }
                return $author_options;
            });
        } catch (\Exception $e) {
            return ResponseService::error('Failed to fetch authors: ' . $e->getMessage());
        }
    }

    /**
     * Get tags for filtering
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function getTags($request)
    {
        try {
            $post_type = $request->get_param('post_type') ?? 'post';
            if (!\post_type_exists($post_type)) {
                return ResponseService::error('Invalid post type specified', null, 400);
            }
            $cache_key = 'fleekdash_tags_' . \sanitize_key($post_type);
            return PostCacheHelper::getCachedOrCompute($cache_key, 5 * MINUTE_IN_SECONDS, function () use ($post_type) {
                return PostFilterOptionsHelper::computeTagOptions($post_type);
            });
        } catch (\Exception $e) {
            return ResponseService::error('Failed to fetch tags: ' . $e->getMessage());
        }
    }

    /**
     * Get all labels (taxonomies and terms) for filtering
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function getLabels($request)
    {
        try {
            $post_type = $request->get_param('post_type') ?? 'post';
            if (!\post_type_exists($post_type)) {
                return ResponseService::error('Invalid post type specified', null, 400);
            }
            $cache_key = 'fleekdash_labels_' . \sanitize_key($post_type);
            return PostCacheHelper::getCachedOrCompute($cache_key, 5 * MINUTE_IN_SECONDS, function () use ($post_type) {
                return PostFilterOptionsHelper::computeLabelOptions($post_type);
            });
        } catch (\Exception $e) {
            return ResponseService::error('Failed to fetch labels: ' . $e->getMessage());
        }
    }

    /**
     * Update page template
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function updatePageTemplate($request)
    {
        try {
            $err = $this->validateUpdatePageTemplateRequest($request);
            if ($err !== null) {
                return $err;
            }
            $page_id = (int) $request->get_param('id');
            $template = $request->get_param('template') ?? '';
            $this->applyPageTemplateUpdate($page_id, $template);
            $updated_post = \get_post($page_id);
            return ResponseService::success(PostFormatter::formatPostForGrid($updated_post), 'Template updated successfully');
        } catch (\Exception $e) {
            return ResponseService::error('Failed to update template: ' . $e->getMessage(), null, 500);
        }
    }

    /**
     * Validate updatePageTemplate request. Returns error response or null if valid.
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response|null
     */
    private function validateUpdatePageTemplateRequest($request): ?\WP_REST_Response
    {
        $page_id = (int) $request->get_param('id');
        $error = null;
        if (!$page_id || $page_id < 1) {
            $error = ResponseService::error('Invalid page ID', null, 400);
        } else {
            $post = \get_post($page_id);
            if (!$post || $post->post_type !== 'page') {
                $error = ResponseService::error('Post is not a page', null, 400);
            } elseif (!\current_user_can('edit_post', $page_id)) {
                $error = ResponseService::error('Insufficient permissions', null, 403);
            }
        }
        return $error;
    }

    /**
     * Apply page template meta update and clear cache.
     *
     * @param int $page_id
     * @param string $template
     * @throws TemplateUpdateException On update failure
     */
    private function applyPageTemplateUpdate(int $page_id, string $template): void
    {
        $current_template = \get_post_meta($page_id, '_wp_page_template', true);
        if ($current_template !== $template) {
            $result = \update_post_meta($page_id, '_wp_page_template', $template);
            if ($result === false) {
                throw new TemplateUpdateException('Failed to update template');
            }
        }
        clean_post_cache($page_id);
    }

    /**
     * Get all available page templates
     *
     * @param \WP_REST_Request $_request Required by route callback signature, not used
     * @return \WP_REST_Response
     */
    public function getPageTemplates($_request)
    {
        \assert($_request instanceof \WP_REST_Request);
        try {
            $templates = PageTemplateHelper::getAllPageTemplates();

            // Always include default template as first option
            $result = array_merge(
                [
                    [
                        'value' => '',
                        'label' => 'Default Template',
                    ],
                ],
                $templates
            );

            return ResponseService::success($result, 'Page templates retrieved successfully');
        } catch (\Throwable $e) {
            return ResponseService::error('Failed to get templates: ' . $e->getMessage(), null, 500);
        }
    }

    /**
     * Get most recent post created via AI for a post type
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function getMostRecentAIPost($request)
    {
        $response = ResponseService::error(
            'Failed to fetch most recent AI post',
            null,
            500,
        );

        try {
            $auth_check = AuthMiddleware::check('authenticated');
            if (!$auth_check['authenticated']) {
                $response = ResponseService::error('Authentication required', null, 401);
            } else {
                global $wpdb;
                $post_type = $request->get_param('post_type') ?? 'post';
                $post = $wpdb->get_row($wpdb->prepare(
                    "SELECT p.* FROM {$wpdb->posts} p
                     INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
                     WHERE p.post_type = %s AND pm.meta_key = 'document_id'
                     AND p.post_status != 'trash'
                     ORDER BY p.post_date DESC LIMIT 1",
                    $post_type
                ), ARRAY_A);

                if (!is_array($post) || $post === []) {
                    $response = ResponseService::error('No AI-created post found', null, 404);
                } else {
                    $formatter = new PostFormatter();
                    $response = ResponseService::success('Most recent AI post retrieved', [
                        'success' => true,
                        'post' => $formatter->formatPost($post),
                    ]);
                }
            }
        } catch (\Exception $e) {
            $response = ResponseService::error(
                'Failed to fetch most recent AI post: ' . $e->getMessage(),
                null,
                500,
            );
        }

        return $response;
    }
}
