<?php

/**
 *
 * @OA\Tag(
 *   name="detektivo",
 *   description="Detektivo module",
 * )
 */

$this->on('restApi.config', function($restApi) {

    /**
     * @OA\Get(
     *     path="/detektivo/search/{index}",
     *     tags={"detektivo"},
     *     @OA\Parameter(
     *         description="Index name",
     *         in="path",
     *         name="index",
     *         required=true,
     *         @OA\Schema(type="string")
     *     ),
     *    @OA\Parameter(
     *         description="Query string",
     *         in="query",
     *         name="q",
     *         required=true,
     *         @OA\Schema(type="string")
     *     ),
     *    @OA\Parameter(
     *         description="Comma seperated list of fields to retrieve",
     *         in="query",
     *         name="fields",
     *         required=false,
     *         @OA\Schema(type="string")
     *    ),
     *    @OA\Parameter(
     *         description="Maximum number of documents returned",
     *         in="query",
     *         name="limit",
     *         required=false,
     *         @OA\Schema(type="integer")
     *     ),
     *    @OA\Parameter(
     *         description="Number of documents to skip",
     *         in="query",
     *         name="offset",
     *         required=false,
     *         @OA\Schema(type="integer")
     *     ),
     *    @OA\Parameter(
     *         description="Enable fuzzy search (typo tolerance)",
     *         in="query",
     *         name="fuzzy",
     *         required=false,
     *         @OA\Schema(type="boolean")
     *     ),
     *    @OA\Parameter(
     *         description="Fuzzy search algorithm for IndexLite backend only (levenshtein, jaro_winkler, trigram, soundex, hybrid, fts5). Ignored for Meilisearch.",
     *         in="query",
     *         name="fuzzy_algorithm",
     *         required=false,
     *         @OA\Schema(type="string", enum={"levenshtein", "jaro_winkler", "trigram", "soundex", "hybrid", "fts5"})
     *     ),
     *    @OA\Parameter(
     *         description="Fuzzy search distance threshold for IndexLite backend only (0.0-1.0). Ignored for Meilisearch.",
     *         in="query",
     *         name="fuzzy_threshold",
     *         required=false,
     *         @OA\Schema(type="number", format="float", minimum=0.0, maximum=1.0)
     *     ),
     *    @OA\Parameter(
     *         description="Minimum fuzzy search score for IndexLite backend only (0.0-1.0). Ignored for Meilisearch.",
     *         in="query",
     *         name="fuzzy_min_score",
     *         required=false,
     *         @OA\Schema(type="number", format="float", minimum=0.0, maximum=1.0)
     *     ),
     *    @OA\Parameter(
     *         description="Enable typo tolerance (Meilisearch)",
     *         in="query",
     *         name="typo_tolerance",
     *         required=false,
     *         @OA\Schema(type="boolean")
     *     ),
     *    @OA\Parameter(
     *         description="Minimum ranking score threshold (Meilisearch)",
     *         in="query",
     *         name="ranking_score_threshold",
     *         required=false,
     *         @OA\Schema(type="number", format="float", minimum=0.0, maximum=1.0)
     *     ),
     *    @OA\Parameter(
     *         description="Enable search term highlighting",
     *         in="query",
     *         name="highlights",
     *         required=false,
     *         @OA\Schema(type="boolean")
     *     ),
     *    @OA\Parameter(
     *         description="Comma-separated list of fields to highlight",
     *         in="query",
     *         name="attributes_to_highlight",
     *         required=false,
     *         @OA\Schema(type="string")
     *     ),
     *    @OA\Parameter(
     *         description="Comma-separated list of facet fields (supports multiple facets)",
     *         in="query",
     *         name="facets",
     *         required=false,
     *         @OA\Schema(type="string")
     *     ),
     *    @OA\Parameter(
     *         description="Facet limit per field",
     *         in="query",
     *         name="facet_limit",
     *         required=false,
     *         @OA\Schema(type="integer")
     *     ),
     *    @OA\Parameter(
     *         description="Facet offset per field",
     *         in="query",
     *         name="facet_offset",
     *         required=false,
     *         @OA\Schema(type="integer")
     *     ),
     *    @OA\Parameter(
     *         description="Field boosts as JSON object (e.g. {'title':2.0,'content':1.0})",
     *         in="query",
     *         name="boosts",
     *         required=false,
     *         @OA\Schema(type="string")
     *     ),
     *    @OA\Parameter(
     *         description="Sort order as JSON object (e.g. {'price':'asc'})",
     *         in="query",
     *         name="sort",
     *         required=false,
     *         @OA\Schema(type="string")
     *     ),
     *    @OA\Parameter(
     *         description="Synonyms as JSON object (e.g. {'phone':['mobile']})",
     *         in="query",
     *         name="synonyms",
     *         required=false,
     *         @OA\Schema(type="string")
     *     ),
     *    @OA\Parameter(
     *         description="Enable highlighting (true or comma-separated fields)",
     *         in="query",
     *         name="highlight",
     *         required=false,
     *         @OA\Schema(type="string")
     *     ),
     *    @OA\Parameter(
     *         description="Filter query (string or JSON array of strings)",
     *         in="query",
     *         name="filter",
     *         required=false,
     *         @OA\Schema(type="string")
     *     ),
     *     @OA\Response(response="200", description="List of matched documents", @OA\JsonContent()),
     *     @OA\Response(response="404", description="Index not found")
     * )
    */

    $restApi->addEndPoint('/detektivo/search/{index}', [

        'GET' => function($params, $app) {

            $index = $params['index'];

            if (!$app->module('detektivo')->exists($index)) {
                return false;
            }

            if (!$app->helper('acl')->isAllowed('detektivo/api/search', $app->helper('auth')->getUser('role'))) {
                $app->response->status = 403;
                return ['error' => 'Permission denied'];
            }

            $idx = $app->module('detektivo')->index($index);
            $query = $app->param('q:string', '');
            $fields = $app->param('fields:string', '*');
            $filter = $app->param('filter', null);
            $limit = $app->param('limit:int', 50);
            $offset = $app->param('offset:int', 0);
            
            // Fuzzy search parameters
            $fuzzy = $app->param('fuzzy:boolean', null);
            $fuzzyAlgorithm = $app->param('fuzzy_algorithm:string', null);
            $fuzzyThreshold = $app->param('fuzzy_threshold:float', null);
            $fuzzyMinScore = $app->param('fuzzy_min_score:float', null);
            $typoTolerance = $app->param('typo_tolerance:boolean', null);
            $rankingScoreThreshold = $app->param('ranking_score_threshold:float', null);
            $highlights = $app->param('highlights:boolean', null);
            $attributesToHighlight = $app->param('attributes_to_highlight:string', null);
            $facetsParam = $app->param('facets', null);
            $facetLimit = $app->param('facet_limit:int', null);
            $facetOffset = $app->param('facet_offset:int', null);
            $boostsParam = $app->param('boosts', null);

            if (!trim($query)) {
                $app->response->status = 412;
                return ['error' => 'Query parameter is missing or empty!'];
            }
            
            // Validate fuzzy search parameters
            if ($fuzzyAlgorithm !== null) {
                $validAlgorithms = ['levenshtein', 'jaro_winkler', 'trigram', 'soundex', 'hybrid', 'fts5'];
                if (!in_array($fuzzyAlgorithm, $validAlgorithms)) {
                    $app->response->status = 400;
                    return ['error' => 'Invalid fuzzy_algorithm. Must be one of: ' . implode(', ', $validAlgorithms)];
                }
            }
            
            if ($fuzzyThreshold !== null && ($fuzzyThreshold < 0.0 || $fuzzyThreshold > 1.0)) {
                $app->response->status = 400;
                return ['error' => 'fuzzy_threshold must be between 0.0 and 1.0'];
            }
            
            if ($fuzzyMinScore !== null && ($fuzzyMinScore < 0.0 || $fuzzyMinScore > 1.0)) {
                $app->response->status = 400;
                return ['error' => 'fuzzy_min_score must be between 0.0 and 1.0'];
            }
            
            if ($rankingScoreThreshold !== null && ($rankingScoreThreshold < 0.0 || $rankingScoreThreshold > 1.0)) {
                $app->response->status = 400;
                return ['error' => 'ranking_score_threshold must be between 0.0 and 1.0'];
            }

            $params = [
                'fields' => $fields,
                'limit' => $limit,
                'offset' => $offset,
            ];

            if ($filter) {
                $params['filter'] = $filter;
            }
            
            // Add fuzzy search parameters if provided
            if ($fuzzy !== null) {
                $params['fuzzy'] = $fuzzy;
            }
            
            if ($fuzzyAlgorithm) {
                $params['fuzzy_algorithm'] = $fuzzyAlgorithm;
            }
            
            if ($fuzzyThreshold !== null) {
                $params['fuzzy_threshold'] = $fuzzyThreshold;
            }
            
            if ($fuzzyMinScore !== null) {
                $params['fuzzy_min_score'] = $fuzzyMinScore;
            }
            
            if ($typoTolerance !== null) {
                $params['typoTolerance'] = $typoTolerance;
            }
            
            if ($rankingScoreThreshold !== null) {
                $params['rankingScoreThreshold'] = $rankingScoreThreshold;
            }
            
            if ($highlights !== null) {
                $params['highlights'] = $highlights;
            }
            
            if ($attributesToHighlight) {
                $params['attributesToHighlight'] = explode(',', $attributesToHighlight);
            }

            // Handle facets
            if ($facetsParam) {
                if (is_string($facetsParam)) {
                    $facetsParam = trim($facetsParam);
                    if ($facetsParam !== '') {
                        // Try JSON decode first
                        if ($facetsParam[0] === '[' || $facetsParam[0] === '{') {
                            try {
                                $decoded = json_decode($facetsParam, true);
                                if (is_array($decoded)) {
                                    $facets = array_values(array_filter($decoded, fn($v) => is_string($v) && $v !== ''));
                                }
                            } catch (\Throwable $e) {}
                        }
                        if (!isset($facets)) {
                            $facets = array_map('trim', explode(',', $facetsParam));
                        }
                    }
                } elseif (is_array($facetsParam)) {
                    $facets = array_values(array_filter($facetsParam, fn($v) => is_string($v) && $v !== ''));
                }

                if (!empty($facets)) {
                    $params['facets'] = $facets;
                    if ($facetLimit !== null) $params['facet_limit'] = $facetLimit;
                    if ($facetOffset !== null) $params['facet_offset'] = $facetOffset;
                }
            }

            // Handle boosts
            if ($boostsParam) {
                $boosts = null;
                if (is_string($boostsParam)) {
                    try { $boosts = json_decode($boostsParam, true); } catch (\Throwable $e) {}
                } elseif (is_array($boostsParam)) {
                    $boosts = $boostsParam;
                }
                if (is_array($boosts)) {
                    $params['boosts'] = $boosts;
                }
            }

            // Handle sort
            $sortParam = $app->param('sort', null);
            if ($sortParam) {
                $sort = null;
                if (is_string($sortParam)) {
                    try { $sort = json_decode($sortParam, true); } catch (\Throwable $e) {}
                } elseif (is_array($sortParam)) {
                    $sort = $sortParam;
                }
                if (is_array($sort)) {
                    $params['sort'] = $sort;
                }
            }

            // Handle synonyms
            $synonymsParam = $app->param('synonyms', null);
            if ($synonymsParam) {
                $synonyms = null;
                if (is_string($synonymsParam)) {
                    try { $synonyms = json_decode($synonymsParam, true); } catch (\Throwable $e) {}
                } elseif (is_array($synonymsParam)) {
                    $synonyms = $synonymsParam;
                }
                if (is_array($synonyms)) {
                    $params['synonyms'] = $synonyms;
                }
            }

            // Handle highlight
            $highlightParam = $app->param('highlight', null);
            if ($highlightParam !== null) {
                if ($highlightParam === 'true' || $highlightParam === true || $highlightParam === '1') {
                    $params['highlight'] = true;
                } elseif (is_string($highlightParam)) {
                    // Check if it's a JSON array
                    if ($highlightParam[0] === '[') {
                        try {
                            $decoded = json_decode($highlightParam, true);
                            if (is_array($decoded)) {
                                $params['highlight'] = $decoded;
                            }
                        } catch (\Throwable $e) {}
                    } else {
                        // Comma separated list
                        $params['highlight'] = array_map('trim', explode(',', $highlightParam));
                    }
                } elseif (is_array($highlightParam)) {
                    $params['highlight'] = $highlightParam;
                }
            }

            // Handle filter (allow JSON array for structured filters)
            if ($filter) {
                if (is_string($filter) && ($filter[0] === '[')) {
                     try {
                        $decoded = json_decode($filter, true);
                        if (is_array($decoded)) {
                            $params['filter'] = $decoded;
                        } else {
                            $params['filter'] = $filter;
                        }
                    } catch (\Throwable $e) {
                        $params['filter'] = $filter;
                    }
                } else {
                    $params['filter'] = $filter;
                }
            }

            return $idx->search($query, $params);
        }
    ]);

});

$this->on('graphql.config', function($gql) {
    $app = $this;
    include(__DIR__.'/graphql/detektivo.php');
});
