<?php

namespace App\Services;

use App\Models\Discussion;
use App\Models\KnowledgeBaseQuestion;
use App\Models\SearchLog;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

/**
 * SearchService
 * 
 * Handles search functionality across discussions and knowledge base.
 * Provides methods for searching, filtering, and logging search queries.
 * 
 * @package App\Services
 */
class SearchService
{
    /**
     * Search across all content types.
     *
     * @param string $query
     * @param array $filters
     * @param int $perPage
     * @return array
     */
    public function searchAll(string $query, array $filters = [], int $perPage = 10): array
    {
        $startTime = microtime(true);
        
        $results = [
            'discussions' => collect(),
            'knowledge_base' => collect(),
            'total' => 0,
            'query' => $query,
        ];

        // Search discussions if enabled
        if (empty($filters['type']) || $filters['type'] === 'discussions') {
            $results['discussions'] = $this->searchDiscussions($query, $filters, $perPage);
            $results['total'] += $results['discussions']->total();
        }

        // Search knowledge base if enabled
        if (empty($filters['type']) || $filters['type'] === 'knowledge_base') {
            $results['knowledge_base'] = $this->searchKnowledgeBase($query, $filters, $perPage);
            $results['total'] += $results['knowledge_base']->total();
        }

        $executionTime = round((microtime(true) - $startTime) * 1000);

        // Log the search
        $this->logSearch($query, $filters, $results['total'], $executionTime);

        return $results;
    }

    /**
     * Search discussions.
     *
     * @param string $query
     * @param array $filters
     * @param int $perPage
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
     */
    public function searchDiscussions(string $query, array $filters = [], int $perPage = 10): LengthAwarePaginator
    {
        $searchQuery = Discussion::query()
            ->with(['user', 'topic'])
            ->published()
            ->search($query);

        // Apply topic filter
        if (!empty($filters['topic_id'])) {
            $searchQuery->where('topic_id', $filters['topic_id']);
        }

        // Apply date range filter
        if (!empty($filters['date_from'])) {
            $searchQuery->where('created_at', '>=', $filters['date_from']);
        }
        if (!empty($filters['date_to'])) {
            $searchQuery->where('created_at', '<=', $filters['date_to']);
        }

        // Apply sorting
        $sort = $filters['sort'] ?? 'relevance';
        switch ($sort) {
            case 'newest':
                $searchQuery->orderByDesc('created_at');
                break;
            case 'oldest':
                $searchQuery->orderBy('created_at');
                break;
            case 'popular':
                $searchQuery->orderByDesc('view_count');
                break;
            case 'most_replies':
                $searchQuery->orderByDesc('reply_count');
                break;
            default:
                // Relevance - order by match quality
                $searchQuery->orderByRaw("
                    CASE 
                        WHEN title LIKE ? THEN 1
                        WHEN title LIKE ? THEN 2
                        WHEN content LIKE ? THEN 3
                        ELSE 4
                    END
                ", ["%{$query}%", "%{$query}%", "%{$query}%"]);
        }

        return $searchQuery->paginate($perPage);
    }

    /**
     * Search knowledge base.
     *
     * @param string $query
     * @param array $filters
     * @param int $perPage
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
     */
    public function searchKnowledgeBase(string $query, array $filters = [], int $perPage = 10): LengthAwarePaginator
    {
        $searchQuery = KnowledgeBaseQuestion::query()
            ->with(['user', 'category'])
            ->published()
            ->search($query);

        // Apply category filter
        if (!empty($filters['category_id'])) {
            $searchQuery->where('category_id', $filters['category_id']);
        }

        // Apply difficulty filter
        if (!empty($filters['difficulty'])) {
            $searchQuery->where('difficulty', $filters['difficulty']);
        }

        // Apply tag filter
        if (!empty($filters['tag'])) {
            $searchQuery->whereJsonContains('tags', $filters['tag']);
        }

        // Apply date range filter
        if (!empty($filters['date_from'])) {
            $searchQuery->where('published_at', '>=', $filters['date_from']);
        }
        if (!empty($filters['date_to'])) {
            $searchQuery->where('published_at', '<=', $filters['date_to']);
        }

        // Apply sorting
        $sort = $filters['sort'] ?? 'relevance';
        switch ($sort) {
            case 'newest':
                $searchQuery->orderByDesc('published_at');
                break;
            case 'oldest':
                $searchQuery->orderBy('published_at');
                break;
            case 'popular':
                $searchQuery->orderByDesc('view_count');
                break;
            case 'helpful':
                $searchQuery->orderByDesc('helpful_count');
                break;
            default:
                // Relevance - order by match quality
                $searchQuery->orderByRaw("
                    CASE 
                        WHEN title LIKE ? THEN 1
                        WHEN title LIKE ? THEN 2
                        WHEN content LIKE ? THEN 3
                        ELSE 4
                    END
                ", ["%{$query}%", "%{$query}%", "%{$query}%"]);
        }

        return $searchQuery->paginate($perPage);
    }

    /**
     * Get search suggestions.
     *
     * @param string $query
     * @param int $limit
     * @return \Illuminate\Support\Collection
     */
    public function getSuggestions(string $query, int $limit = 5): Collection
    {
        $suggestions = collect();

        if (strlen($query) < 2) {
            return $suggestions;
        }

        // Get discussion title suggestions
        $discussionTitles = Discussion::published()
            ->where('title', 'like', "%{$query}%")
            ->limit($limit)
            ->pluck('title')
            ->map(function ($title) {
                return [
                    'text' => $title,
                    'type' => 'discussion',
                ];
            });

        // Get question title suggestions
        $questionTitles = KnowledgeBaseQuestion::published()
            ->where('title', 'like', "%{$query}%")
            ->limit($limit)
            ->pluck('title')
            ->map(function ($title) {
                return [
                    'text' => $title,
                    'type' => 'knowledge_base',
                ];
            });

        // Get popular search suggestions
        $popularSearches = SearchLog::query()
            ->select('query', DB::raw('COUNT(*) as count'))
            ->where('query', 'like', "%{$query}%")
            ->groupBy('query')
            ->orderByDesc('count')
            ->limit($limit)
            ->pluck('query')
            ->map(function ($q) {
                return [
                    'text' => $q,
                    'type' => 'popular',
                ];
            });

        return $suggestions
            ->merge($discussionTitles)
            ->merge($questionTitles)
            ->merge($popularSearches)
            ->unique('text')
            ->take($limit);
    }

    /**
     * Get popular searches.
     *
     * @param int $limit
     * @return \Illuminate\Support\Collection
     */
    public function getPopularSearches(int $limit = 10): Collection
    {
        return SearchLog::query()
            ->select('query', DB::raw('COUNT(*) as count'))
            ->where('created_at', '>=', now()->subDays(30))
            ->groupBy('query')
            ->orderByDesc('count')
            ->limit($limit)
            ->get()
            ->map(function ($item) {
                return [
                    'query' => $item->query,
                    'count' => $item->count,
                ];
            });
    }

    /**
     * Get recent searches for a user.
     *
     * @param int $userId
     * @param int $limit
     * @return \Illuminate\Support\Collection
     */
    public function getRecentSearches(int $userId, int $limit = 10): Collection
    {
        return SearchLog::query()
            ->where('user_id', $userId)
            ->orderByDesc('created_at')
            ->limit($limit)
            ->get(['query', 'search_type', 'results_count', 'created_at']);
    }

    /**
     * Log a search query.
     *
     * @param string $query
     * @param array $filters
     * @param int $resultsCount
     * @param int $responseTimeMs
     * @return void
     */
    protected function logSearch(string $query, array $filters, int $resultsCount, int $responseTimeMs): void
    {
        try {
            SearchLog::create([
                'query' => $query,
                'user_id' => auth()->id(),
                'search_type' => $filters['type'] ?? 'all',
                'filters' => !empty($filters) ? $filters : null,
                'results_count' => $resultsCount,
                'response_time_ms' => $responseTimeMs,
                'session_id' => session()->getId(),
                'ip_address' => request()->ip(),
                'user_agent' => request()->userAgent(),
            ]);
        } catch (\Exception $e) {
            // Silently fail - search logging is not critical
            \Log::warning('Failed to log search: ' . $e->getMessage());
        }
    }

    /**
     * Highlight search terms in text.
     *
     * @param string $text
     * @param string $query
     * @param string $highlightTag
     * @return string
     */
    public function highlightTerms(string $text, string $query, string $highlightTag = 'mark'): string
    {
        $terms = explode(' ', $query);
        
        foreach ($terms as $term) {
            if (strlen($term) >= 2) {
                $pattern = '/(' . preg_quote($term, '/') . ')/i';
                $replacement = "<{$highlightTag} class=\"search-highlight\">\$1</{$highlightTag}>";
                $text = preg_replace($pattern, $replacement, $text);
            }
        }

        return $text;
    }

    /**
     * Get search analytics.
     *
     * @param int $days
     * @return array
     */
    public function getAnalytics(int $days = 30): array
    {
        $startDate = now()->subDays($days);

        return [
            'total_searches' => SearchLog::where('created_at', '>=', $startDate)->count(),
            'unique_queries' => SearchLog::where('created_at', '>=', $startDate)
                ->distinct('query')
                ->count('query'),
            'average_results' => round(SearchLog::where('created_at', '>=', $startDate)
                ->avg('results_count'), 2),
            'average_response_time' => round(SearchLog::where('created_at', '>=', $startDate)
                ->avg('response_time_ms'), 2),
            'searches_without_results' => SearchLog::where('created_at', '>=', $startDate)
                ->where('results_count', 0)
                ->count(),
            'top_searches' => $this->getPopularSearches(10),
            'searches_by_type' => SearchLog::where('created_at', '>=', $startDate)
                ->select('search_type', DB::raw('COUNT(*) as count'))
                ->groupBy('search_type')
                ->get()
                ->keyBy('search_type')
                ->map->count,
            'daily_searches' => SearchLog::where('created_at', '>=', $startDate)
                ->select(DB::raw('DATE(created_at) as date'), DB::raw('COUNT(*) as count'))
                ->groupBy('date')
                ->orderBy('date')
                ->get(),
        ];
    }

    /**
     * Clear search history for a user.
     *
     * @param int $userId
     * @return void
     */
    public function clearUserHistory(int $userId): void
    {
        SearchLog::where('user_id', $userId)->delete();
    }

    /**
     * Get related content based on tags/keywords.
     *
     * @param string $type 'discussion' or 'question'
     * @param int $id
     * @param int $limit
     * @return \Illuminate\Support\Collection
     */
    public function getRelatedContent(string $type, int $id, int $limit = 5): Collection
    {
        if ($type === 'discussion') {
            $discussion = Discussion::with('topic')->find($id);
            if (!$discussion) {
                return collect();
            }

            return Discussion::published()
                ->where('id', '!=', $id)
                ->where(function ($query) use ($discussion) {
                    $query->where('topic_id', $discussion->topic_id)
                        ->orWhere('title', 'like', '%' . substr($discussion->title, 0, 30) . '%');
                })
                ->latestActivity()
                ->limit($limit)
                ->get();
        }

        if ($type === 'question') {
            $question = KnowledgeBaseQuestion::with('category', 'tags')->find($id);
            if (!$question) {
                return collect();
            }

            $query = KnowledgeBaseQuestion::published()
                ->where('id', '!=', $id)
                ->where(function ($q) use ($question) {
                    $q->where('category_id', $question->category_id);
                    
                    if (!empty($question->tags)) {
                        foreach ($question->tags as $tag) {
                            $q->orWhereJsonContains('tags', $tag);
                        }
                    }
                });

            return $query->recent()->limit($limit)->get();
        }

        return collect();
    }
}
