<?php
/**
 * Search Controller
 *
 * Handles universal search requests for the CMDK system
 *
 * @package FleekDash\Controllers
 */

namespace FleekDash\Controllers;

// Load debug helpers
if (!function_exists('fleekdash_log_error')) {
    require_once FLEEKDASH_PLUGIN_DIR . 'includes/Core/debug-helpers.php';
}

use FleekDash\Core\SearchIndexManager;
use FleekDash\Services\SearchCacheService;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;

class SearchController extends BaseController
{
    private $searchManager;
    private $cacheService;

    public function __construct()
    {
        $this->searchManager = new SearchIndexManager();
        $this->cacheService = new SearchCacheService();
    }

    /**
     * Universal search endpoint for CMDK
     *
     * @param WP_REST_Request $request
     * @return WP_REST_Response|WP_Error
     */
    public function universalSearch(WP_REST_Request $request)
    {
        try {
            $query = \sanitize_text_field($request->get_param('q'));
            $types = $this->parseSearchTypes($request->get_param('types'));
            $types = array_intersect($types, ['post', 'user', 'menu', 'setting']);
            $limit = (int) ($request->get_param('limit') ?? 20);

            $validationError = $this->validateSearchInput($query, $types, $limit);
            if ($validationError !== null) {
                return $validationError;
            }

            return $this->buildSearchResponse($query, $types, $limit);

        } catch (\Exception $e) {
            fleekdash_log_error('FleekDash Search Error: ' . $e->getMessage());
            return new WP_Error(
                'search_error',
                'An error occurred while searching: ' . $e->getMessage(),
                ['status' => 500],
            );
        }
    }

    /**
     * Build search response (cache or fresh results)
     *
     * @param string $query
     * @param array $types
     * @param int $limit
     * @return \WP_REST_Response
     */
    private function buildSearchResponse(string $query, array $types, int $limit): \WP_REST_Response
    {
        $cache_key = $this->cacheService->getCacheKey($query, $types, $limit);
        $cached_results = $this->cacheService->getCachedResults($cache_key);

        if ($cached_results !== null && is_array($cached_results)) {
            return \rest_ensure_response([
                'results' => $cached_results,
                'total' => count($cached_results),
                'cache_hit' => true,
                'cache_layer' => 'cache'
            ]);
        }

        $results = $this->fetchSearchResults($query, $types, $limit);
        $this->cacheService->cacheResults($cache_key, $results);

        return \rest_ensure_response([
            'results' => $results,
            'total' => count($results),
            'cache_hit' => false,
            'search_time' => $this->searchManager->getLastSearchTime()
        ]);
    }

    /**
     * Parse types parameter from request
     *
     * @param mixed $typesParam
     * @return array
     */
    private function parseSearchTypes($typesParam): array
    {
        if (is_string($typesParam)) {
            return explode(',', $typesParam);
        }
        return is_array($typesParam) ? $typesParam : ['post', 'user', 'menu', 'setting'];
    }

    /**
     * Validate search input, return WP_Error or null
     *
     * @param string $query
     * @param array $types
     * @param int $limit
     * @return WP_Error|null
     */
    private function validateSearchInput(string $query, array $types, int $limit): ?WP_Error
    {
        $error = null;
        if (strlen($query) < 2) {
            $error = new WP_Error('query_too_short', 'Search query must be at least 2 characters long', ['status' => 400]);
        } elseif ($limit < 1 || $limit > 50) {
            $error = new WP_Error('invalid_limit', 'Limit must be between 1 and 50', ['status' => 400]);
        } elseif (empty($types)) {
            $error = new WP_Error('invalid_types', 'At least one valid type must be specified', ['status' => 400]);
        }
        return $error;
    }

    /**
     * Fetch search results, populating index if empty
     *
     * @param string $query
     * @param array $types
     * @param int $limit
     * @return array
     */
    private function fetchSearchResults(string $query, array $types, int $limit): array
    {
        $results = $this->searchManager->search($query, $types, $limit);
        if (!is_array($results)) {
            $results = [];
        }
        if (empty($results)) {
            $this->populateSearchIndex();
            $results = $this->searchManager->search($query, $types, $limit);
            if (!is_array($results)) {
                $results = [];
            }
        }
        return $results;
    }

    /**
     * Rebuild search index endpoint
     */
    public function rebuildIndex(WP_REST_Request $request)
    {
        $validation = $this->validateRebuildRequest($request);
        if ($validation instanceof WP_Error) {
            return $validation;
        }

        try {
            $types = $validation;
            $result = $this->executeRebuild($types);

            return \rest_ensure_response([
                'status' => 'success',
                'message' => 'Search index rebuilt successfully',
                'types' => $types,
                'indexed_items' => $result
            ]);

        } catch (\Exception $e) {
            fleekdash_log_error('FleekDash Search Index Rebuild Error: ' . $e->getMessage());
            return new WP_Error(
                'rebuild_error',
                'An error occurred while rebuilding the search index: ' . $e->getMessage(),
                ['status' => 500],
            );
        }
    }

    /**
     * Validate rebuild request (permissions + types). Returns WP_Error or validated types array.
     *
     * @param WP_REST_Request $request
     * @return WP_Error|array
     */
    private function validateRebuildRequest(WP_REST_Request $request)
    {
        if (!\current_user_can('manage_options')) {
            return new WP_Error('insufficient_permissions', 'Insufficient permissions', ['status' => 403]);
        }
        $types = $request->get_param('types') ?? ['post', 'user', 'menu', 'setting'];
        $types = array_intersect($types, ['post', 'user', 'menu', 'setting']);
        if (empty($types)) {
            return new WP_Error('invalid_types', 'At least one valid type must be specified', ['status' => 400]);
        }
        return $types;
    }

    /**
     * Execute index rebuild: clear cache, clear table, rebuild
     *
     * @param array $types
     * @return array
     */
    private function executeRebuild(array $types): array
    {
        $this->cacheService->clearAllSearchCache();
        global $wpdb;
        $table_name = $wpdb->prefix . 'fleekdash_search_index';
        foreach ($types as $type) {
            $wpdb->delete($table_name, ['object_type' => $type]);
        }
        return $this->searchManager->rebuildIndex($types);
    }

    /**
     * Admin menu ingestion endpoint
     */
    public function ingestAdminMenu(WP_REST_Request $request)
    {
        try {
            $payload = $request->get_json_params();
            $items = isset($payload['items']) && is_array($payload['items']) ? $payload['items'] : [];
            if (empty($items)) {
                return new WP_Error('invalid_payload', 'Missing items array', ['status' => 400]);
            }

            global $wpdb;
            $table_name = $wpdb->prefix . 'fleekdash_search_index';
            $wpdb->delete($table_name, ['object_type' => 'menu']);

            $count = $this->ingestMenuItems($items, $wpdb, $table_name);

            return \rest_ensure_response([
                'status' => 'success',
                'ingested' => $count,
            ]);

        } catch (\Exception $e) {
            fleekdash_log_error('FleekDash Menu Ingest Error: ' . $e->getMessage());
            return new WP_Error('ingest_error', 'Failed to ingest admin menu', ['status' => 500]);
        }
    }

    /**
     * Recursively ingest menu items into search index
     *
     * @param array $menuItems
     * @param \wpdb $wpdb
     * @param string $tableName
     * @param string $parent
     * @return int Items ingested
     */
    private function ingestMenuItems(array $menuItems, $wpdb, string $tableName, string $parent = ''): int
    {
        $count = 0;
        foreach ($menuItems as $entry) {
            $title = isset($entry['name']) ? \wp_strip_all_tags((string) $entry['name']) : '';
            if ($title === '') {
                continue;
            }

            $row = $this->buildMenuIngestRow($entry, $title, $parent);
            $wpdb->insert($tableName, $row);
            $count++;

            if (!empty($entry['submenu']) && is_array($entry['submenu'])) {
                $count += $this->ingestMenuItems($entry['submenu'], $wpdb, $tableName, $title);
            }
        }
        return $count;
    }

    /**
     * Build a single menu row for search index insert
     *
     * @param array $entry
     * @param string $title
     * @param string $parent
     * @return array
     */
    private function buildMenuIngestRow(array $entry, string $title, string $parent): array
    {
        $url = $entry['url'] ?? '';
        $href = $entry['href'] ?? '';
        $parts = $this->buildMenuIngestContentParts($entry, $parent);

        $rawId = $entry['unique_id'] ?? ($href ?: $url ?: $title);
        $metadata = [
            'url' => $url,
            'href' => $href,
            'route_type' => $entry['route_type'] ?? '',
            'is_wp_native' => isset($entry['is_wp_native']) ? (bool) $entry['is_wp_native'] : true,
            'parent' => $parent,
            'popularity_score' => 5,
        ];

        return [
            'object_id' => (int) sprintf('%u', crc32((string) $rawId)),
            'object_type' => 'menu',
            'title' => $title,
            'content' => $parts['content'],
            'excerpt' => $parts['excerpt'],
            'keywords' => $parts['keywords'],
            'search_vector' => $title . ' ' . $parts['content'] . ' ' . $parts['keywords'],
            'metadata' => json_encode($metadata),
            'popularity_score' => 5
        ];
    }

    /**
     * Build content parts for menu ingest row
     *
     * @param array $entry
     * @param string $parent
     * @return array{content: string, excerpt: string, keywords: string}
     */
    private function buildMenuIngestContentParts(array $entry, string $parent): array
    {
        $url = $entry['url'] ?? '';
        $href = $entry['href'] ?? '';
        $routeType = $entry['route_type'] ?? '';
        $isWpNative = isset($entry['is_wp_native']) ? (bool) $entry['is_wp_native'] : true;

        $content = trim(($parent ? ($parent . ' ') : '') . ($href ?: $url));
        $excerpt = $content !== '' ? \wp_trim_words($content, 15) : '';
        $keywords = trim(($routeType ? ($routeType . ' ') : '') . ($isWpNative ? 'wordpress_iframe' : 'react'));

        return ['content' => $content, 'excerpt' => $excerpt, 'keywords' => $keywords];
    }

    /**
     * Populate search index with initial data
     *
     * @param array $types
     */
    private function populateSearchIndex($types = ['post', 'user', 'menu', 'setting']): void
    {
        try {
            $this->searchManager->rebuildIndex($types);
        } catch (\Exception $e) {
            fleekdash_log_error('FleekDash Search Auto-Populate Error: ' . $e->getMessage());
        }
    }
}
