<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Validator;
use App\Models\Messenger\Conversation;
use App\Models\Messenger\Message;
use App\Models\Messenger\ConversationParticipant;
use App\Models\Messenger\MessageReceipt;
use App\Models\Messenger\UserEvent;
use App\Models\User;
use App\Services\Messenger\EventDispatcher;
use App\Http\Resources\UserShortResource;

class MessengerController extends Controller
{
    protected $eventDispatcher;

    public function __construct(EventDispatcher $eventDispatcher)
    {
        $this->eventDispatcher = $eventDispatcher;
    }

    public function index()
    {
        return view('messenger.index');
    }

    /**
     * Get user's conversations with pagination
     */
    public function getConversations(Request $request): JsonResponse
    {
        $userId = Auth::id();
        $cacheKey = "user_conversations_{$userId}_" . md5(serialize($request->all()));
        
        // Try to get from cache first
        if (Cache::has($cacheKey)) {
            return response()->json(Cache::get($cacheKey));
        }
        
        // Cache the database query results for better performance
        $conversations = Cache::remember("conversations_query_{$userId}", 60, function () use ($userId, $request) {
            $query = Conversation::visibleTo($userId)
                ->with(['lastMessage.user', 'participants.user'])
                ->orderBy('last_activity_at', 'desc');

            // Apply filters
            if ($request->boolean('include_archived', false)) {
                $query->withTrashed();
            } else {
                $query->where('is_archived', false);
            }

            return $query->paginate($request->integer('limit', 20));
        });

        // Transform conversations for frontend and calculate unread counts efficiently
        $conversations->getCollection()->transform(function ($conversation) use ($userId) {
            $participant = $conversation->participants->where('user_id', '!=', $userId)->first();
            
            // Cache unread count calculation
            $unreadCount = Cache::remember("unread_count_{$conversation->id}_{$userId}", 30, function () use ($conversation, $userId) {
                return DB::table('messages')
                    ->where('conversation_id', $conversation->id)
                    ->where('is_deleted', false)
                    ->where('user_id', '!=', $userId)
                    ->whereNotExists(function ($query) use ($userId) {
                        $query->selectRaw(1)
                            ->from('message_receipts')
                            ->whereColumn('message_receipts.message_id', 'messages.id')
                            ->where('message_receipts.user_id', $userId)
                            ->where('message_receipts.type', 'seen');
                    })
                    ->count();
            });
            
            $currentUserParticipant = $conversation->participants->where('user_id', $userId)->first();
            
            return [
                'id' => $conversation->id,
                'type' => $conversation->type,
                'title' => $conversation->type === 'dm' && $participant 
                    ? $participant->user->full_name ?? $participant->user->username
                    : $conversation->title,
                'avatar' => $conversation->type === 'dm' && $participant 
                    ? getMedia($participant->user->avatar, 'avatar') 
                    : getMedia($conversation->avatar, 'avatar'),
                'last_message' => $conversation->lastMessage ? [
                    'content' => $conversation->lastMessage->content,
                    'created_at' => $conversation->lastMessage->created_at->toISOString(),
                    'user' => new UserShortResource($conversation->lastMessage->user),
                    'is_own' => $conversation->lastMessage->user_id === $userId,
                ] : null,
                'last_activity_at' => $conversation->last_activity_at->toISOString(),
                'unread_count' => $unreadCount,
                'is_group' => $conversation->type === 'group',
                'participants_count' => $conversation->participants->count(),
                'user_id' => $userId,
                'user_role' => $currentUserParticipant?->role ?? 'member',
            ];
        });

        $response = [
            'status' => 200,
            'message' => 'Conversations retrieved successfully',
            'data' => $conversations->items(),
            'pagination' => [
                'current_page' => $conversations->currentPage(),
                'last_page' => $conversations->lastPage(),
                'per_page' => $conversations->perPage(),
                'total' => $conversations->total(),
            ],
        ];

        // Cache for 30 seconds (conversations don't change frequently)
        Cache::put($cacheKey, $response, 30);

        return response()->json($response);
    }

    /**
     * Get conversations with a specific user
     */
    public function getConversationsWithUser($targetUserId): JsonResponse
    {
        $userId = Auth::id();
        
        // Validate target user ID
        if (!is_numeric($targetUserId)) {
            return response()->json([
                'status' => 400,
                'message' => 'Invalid user ID',
            ], 400);
        }
        
        // Check if target user exists and is active
        $targetUser = User::where('id', $targetUserId)
            ->where('status', 1)
            ->first();
            
        if (!$targetUser) {
            return response()->json([
                'status' => 404,
                'message' => 'User not found or inactive',
            ], 404);
        }
        
        // Find conversations where both users are participants
        $conversations = Conversation::whereHas('participants', function ($query) use ($userId) {
                $query->where('user_id', $userId)->whereNull('left_at');
            })
            ->whereHas('participants', function ($query) use ($targetUserId) {
                $query->where('user_id', $targetUserId)->whereNull('left_at');
            })
            ->with([
                'participants.user:id,username,first_name,last_name,name,avatar,status',
                'lastMessage.user:id,username,first_name,last_name,name,avatar'
            ])
            ->orderBy('last_activity_at', 'desc')
            ->get();

        // Format conversations
        $formattedConversations = $conversations->map(function ($conversation) use ($userId, $targetUserId) {
            $targetParticipant = $conversation->participants->where('user_id', $targetUserId)->first();
            $currentUserParticipant = $conversation->participants->where('user_id', $userId)->first();
            
            // Calculate unread count for current user
            $unreadCount = Cache::remember("unread_count_{$conversation->id}_{$userId}", 30, function () use ($conversation, $userId) {
                return Message::where('conversation_id', $conversation->id)
                    ->where('is_deleted', false)
                    ->where('user_id', '!=', $userId)
                    ->whereNotExists(function ($query) use ($userId) {
                        $query->selectRaw(1)
                            ->from('message_receipts')
                            ->whereColumn('message_receipts.message_id', 'messages.id')
                            ->where('message_receipts.user_id', $userId)
                            ->where('message_receipts.type', 'seen');
                    })
                    ->count();
            });
            
            return [
                'id' => $conversation->id,
                'type' => $conversation->type,
                'title' => $conversation->type === 'dm' 
                    ? $targetParticipant->user->full_name ?? $targetParticipant->user->username
                    : $conversation->title,
                'avatar' => $conversation->type === 'dm' 
                    ? getMedia($targetParticipant->user->avatar, 'avatar') 
                    : getMedia($conversation->avatar, 'avatar'),
                'last_message' => $conversation->lastMessage ? [
                    'content' => $conversation->lastMessage->content,
                    'created_at' => $conversation->lastMessage->created_at->toISOString(),
                    'user' => new UserShortResource($conversation->lastMessage->user),
                    'is_own' => $conversation->lastMessage->user_id === $userId,
                ] : null,
                'last_activity_at' => $conversation->last_activity_at->toISOString(),
                'unread_count' => $unreadCount,
                'is_group' => $conversation->type === 'group',
                'participants_count' => $conversation->participants->count(),
                'user_id' => $userId,
                'user_role' => $currentUserParticipant?->role ?? 'member',
                'target_user' => new UserShortResource($targetParticipant->user),
            ];
        });

        return response()->json([
            'status' => 200,
            'message' => 'Conversations with user retrieved successfully',
            'data' => $formattedConversations,
            'target_user' => new UserShortResource($targetUser),
            'total_conversations' => $formattedConversations->count(),
        ]);
    }

    /**
     * Create a new conversation (DM or group)
     */
    public function createConversation(Request $request): JsonResponse
    {
        $validator = Validator::make($request->all(), [
            'type' => 'required|in:dm,group',
            'participant_ids' => 'required|array|min:1',
            'participant_ids.*' => 'integer|exists:users,id',
            'title' => 'nullable|string|max:255',
            'description' => 'nullable|string|max:1000',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'status' => 400,
                'message' => 'Validation failed',
                'errors' => $validator->errors(),
            ], 400);
        }

        $userId = Auth::id();
        $participantIds = array_unique(array_merge($request->participant_ids, [$userId]));

        // Validate that all participant IDs are valid users
        $validUserIds = User::whereIn('id', $participantIds)
            ->where('status', 1) // Only active users
            ->pluck('id')
            ->toArray();

        if (count($validUserIds) !== count($participantIds)) {
            return response()->json([
                'status' => 400,
                'message' => 'One or more users not found or inactive',
                'errors' => ['participant_ids' => ['Invalid user IDs provided']],
            ], 400);
        }

        // Check if DM already exists
        if ($request->type === 'dm' && count($participantIds) === 2) {
            // Use a more efficient approach to check if DM exists
            $existingConversation = Conversation::where('type', 'dm')
                ->whereHas('participants', function ($query) use ($participantIds) {
                    $query->whereIn('user_id', $participantIds)
                          ->whereNull('left_at');
                })
                ->withCount(['participants' => function ($query) use ($participantIds) {
                    $query->whereIn('user_id', $participantIds)
                          ->whereNull('left_at');
                }])
                ->having('participants_count', '=', count($participantIds))
                ->first();

            if ($existingConversation) {
                return response()->json([
                    'status' => 200,
                    'message' => 'Conversation already exists',
                    'data' => $this->formatConversation($existingConversation, $userId),
                ]);
            }
        }

        DB::beginTransaction();
        try {
            $conversation = Conversation::create([
                'type' => $request->type,
                'title' => $request->title,
                'description' => $request->description,
                'created_by' => $userId,
                'last_activity_at' => now(),
            ]);

            // Add participants
            foreach ($participantIds as $participantId) {
                $role = $participantId === $userId ? 'owner' : 'member';
                ConversationParticipant::create([
                    'conversation_id' => $conversation->id,
                    'user_id' => $participantId,
                    'role' => $role,
                    'joined_at' => now(),
                ]);
            }

            DB::commit();

            // Clear cache for all participants
            foreach ($participantIds as $participantId) {
                $this->clearUserCache($participantId);
            }

            // Dispatch event for real-time updates
            $this->eventDispatcher->dispatch('conversation.created', [
                'conversation_id' => $conversation->id,
                'user_ids' => $participantIds,
            ]);

            return response()->json([
                'status' => 201,
                'message' => 'Conversation created successfully',
                'data' => $this->formatConversation($conversation, $userId),
            ], 201);

        } catch (\Exception $e) {
            DB::rollBack();
            
            // Log the error for debugging
            \Log::error('Failed to create conversation', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
                'user_id' => $userId,
                'request_data' => $request->all()
            ]);
            
            return response()->json([
                'status' => 500,
                'message' => 'Failed to create conversation',
                'error' => config('app.debug') ? $e->getMessage() : 'An error occurred while creating the conversation',
            ], 500);
        }
    }

    /**
     * Get messages from a conversation with cursor pagination
     */
    public function getMessages(Request $request, $conversationId): JsonResponse
    {
        try {
            $userId = Auth::id();
            
            // Validate conversation ID
            if (!is_numeric($conversationId)) {
                return response()->json([
                    'status' => 400,
                    'message' => 'Invalid conversation ID',
                ], 400);
            }
            
            // Check if user is participant
            $participant = ConversationParticipant::where('conversation_id', $conversationId)
                ->where('user_id', $userId)
                ->whereNull('left_at')
                ->first();

            if (!$participant) {
                return response()->json([
                    'status' => 403,
                    'message' => 'Access denied to this conversation',
                ], 403);
            }

            $query = Message::where('conversation_id', $conversationId)
                ->where('is_deleted', false)
                ->with(['user:id,username,first_name,last_name,name,avatar', 'attachments'])
                ->orderBy('created_at', 'desc');

            // Apply cursor pagination
            $limit = $request->integer('limit', 50);
            $cursor = $request->input('cursor');
            if (!empty($cursor) && is_numeric($cursor)) {
                $query->where('id', '<', $cursor);
            }

            $messages = $query->limit($limit + 1)->get();
            $hasMore = $messages->count() > $limit;
            
            if ($hasMore) {
                $messages = $messages->take($limit);
            }

            $nextCursor = $hasMore ? $messages->last()->id : null;

        // Transform messages for frontend
        $messages = $messages->map(function ($message) use ($userId) {
            return [
                'id' => $message->id,
                'conversation_id' => $message->conversation_id,
                'user' => new UserShortResource($message->user),
                'type' => $message->type,
                'content' => $message->content,
                'attachments' => $message->attachments->map(function ($attachment) {
                    return [
                        'id' => $attachment->id,
                        'type' => $attachment->type,
                        'url' => $attachment->metadata && isset($attachment->metadata['stored_path']) 
                            ? getMedia($attachment->metadata['stored_path'], $attachment->type)
                            : $attachment->url,
                        'filename' => $attachment->filename,
                        'mime_type' => $attachment->mime_type,
                        'file_size' => $attachment->file_size,
                    ];
                }),
                'reply_to' => $message->reply_to_message_id,
                'is_edited' => !is_null($message->edited_at),
                'is_deleted' => $message->is_deleted,
                'is_system' => false,
                'is_own' => $message->user_id === $userId,
                'client_generated_id' => $message->client_generated_id,
                'created_at' => $message->created_at->toISOString(),
                'edited_at' => $message->edited_at?->toISOString(),
            ];
        });

        // Mark messages as seen in background (don't wait for it)
        dispatch(function () use ($conversationId, $userId, $messages) {
            $this->markMessagesAsSeen($conversationId, $userId, $messages->pluck('id')->toArray());
        })->afterResponse();

            return response()->json([
                'status' => 200,
                'message' => 'Messages retrieved successfully',
                'data' => $messages->values(),
                'pagination' => [
                    'limit' => $limit,
                    'cursor' => $request->cursor,
                    'next_cursor' => $nextCursor,
                    'has_more' => $hasMore,
                ],
            ]);
        } catch (\Exception $e) {
            // Log the error for debugging
            \Log::error('Failed to get messages', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
                'conversation_id' => $conversationId,
                'user_id' => Auth::id(),
                'request_data' => $request->all()
            ]);
            
            return response()->json([
                'status' => 500,
                'message' => 'Failed to retrieve messages',
                'error' => config('app.debug') ? $e->getMessage() : 'An error occurred while retrieving messages',
            ], 500);
        }
    }

    /**
     * Send a message to a conversation
     */
    public function sendMessage(Request $request, $conversationId): JsonResponse
    {
        $validator = Validator::make($request->all(), [
            'content' => 'required_without_all:attachments,attachment_metadata|string|max:4000',
            'type' => 'nullable|in:text,image,video,file,audio,location,contact,sticker',
            'client_generated_id' => 'required|string',
            'reply_to_message_id' => 'nullable|integer|exists:messages,id',
            'attachments' => 'nullable|array',
            'attachments.*' => 'file|max:10240', // Accept file uploads (max 10MB)
            // Alternative for pre-uploaded file metadata
            'attachment_metadata' => 'nullable|array',
            'attachment_metadata.*.type' => 'required_with:attachment_metadata|string',
            'attachment_metadata.*.url' => 'required_with:attachment_metadata|string',
            'attachment_metadata.*.filename' => 'required_with:attachment_metadata|string',
            'attachment_metadata.*.mime_type' => 'required_with:attachment_metadata|string',
            'attachment_metadata.*.file_size' => 'required_with:attachment_metadata|integer',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'status' => 400,
                'message' => 'Validation failed',
                'errors' => $validator->errors(),
            ], 400);
        }

        $userId = Auth::id();
        
        // Check if user is participant
        $participant = ConversationParticipant::where('conversation_id', $conversationId)
            ->where('user_id', $userId)
            ->whereNull('left_at')
            ->first();

        if (!$participant) {
            return response()->json([
                'status' => 403,
                'message' => 'Access denied to this conversation',
            ], 403);
        }

        DB::beginTransaction();
        try {
            $message = Message::create([
                'conversation_id' => $conversationId,
                'user_id' => $userId,
                'type' => $request->get('type', 'text'),
                'content' => $request->content,
                'client_generated_id' => $request->client_generated_id,
                'reply_to_message_id' => $request->reply_to_message_id,
                'metadata' => $request->only(['attachments']),
            ]);

            // Handle file uploads using existing media system
            if ($request->has('attachments') && is_array($request->attachments)) {
                foreach ($request->attachments as $uploadedFile) {
                    if ($uploadedFile instanceof \Illuminate\Http\UploadedFile) {
                        // Use existing storeMedia function with messenger path
                        $fileType = $this->getFileType($uploadedFile->getMimeType());
                        $storedPath = storeMedia($uploadedFile, "messenger/{$fileType}");
                        
                        $message->attachments()->create([
                            'type' => $fileType,
                            'url' => getMedia($storedPath, $fileType),
                            'filename' => $uploadedFile->getClientOriginalName(),
                            'mime_type' => $uploadedFile->getMimeType(),
                            'file_size' => $uploadedFile->getSize(),
                            'metadata' => [
                                'original_name' => $uploadedFile->getClientOriginalName(),
                                'stored_path' => $storedPath
                            ],
                        ]);
                    }
                }
            }
            
            // Handle pre-uploaded file metadata
            if ($request->has('attachment_metadata') && is_array($request->attachment_metadata)) {
                foreach ($request->attachment_metadata as $attachment) {
                    $message->attachments()->create([
                        'type' => $attachment['type'],
                        'url' => $attachment['url'],
                        'filename' => $attachment['filename'],
                        'mime_type' => $attachment['mime_type'],
                        'file_size' => $attachment['file_size'],
                        'metadata' => $attachment['metadata'] ?? null,
                    ]);
                }
            }

            // Update conversation
            $conversation = Conversation::find($conversationId);
            $conversation->update([
                'last_message_id' => $message->id,
                'last_activity_at' => now(),
            ]);

            // Create receipts for other participants
            $otherParticipants = ConversationParticipant::where('conversation_id', $conversationId)
                ->where('user_id', '!=', $userId)
                ->whereNull('left_at')
                ->get();

            foreach ($otherParticipants as $participant) {
                MessageReceipt::create([
                    'message_id' => $message->id,
                    'user_id' => $participant->user_id,
                    'type' => 'delivered',
                    'delivered_at' => now(),
                ]);
            }

            DB::commit();

            // Clear cache for all participants
            $participantIds = ConversationParticipant::where('conversation_id', $conversationId)
                ->whereNull('left_at')
                ->pluck('user_id')
                ->toArray();
            
            foreach ($participantIds as $participantId) {
                $this->clearUserCache($participantId);
            }

            // Dispatch event for real-time updates
            $this->eventDispatcher->dispatch('message.sent', [
                'conversation_id' => $conversationId,
                'message_id' => $message->id,
                'user_id' => $userId,
            ]);

            return response()->json([
                'status' => 201,
                'message' => 'Message sent successfully',
                'data' => $this->formatMessage($message, $userId),
            ], 201);

        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json([
                'status' => 500,
                'message' => 'Failed to send message',
                'error' => $e->getMessage(),
            ], 500);
        }
    }

    /**
     * Get real-time updates via long polling
     */
    public function getUpdates(Request $request): JsonResponse
    {
        $userId = Auth::id();
        $since = $request->integer('since', 0);
        $timeout = min($request->integer('timeout', 25), 30); // Max 30 seconds
        
        $startTime = time();
        
        while (time() - $startTime < $timeout) {
            // Check for new events
            $events = UserEvent::where('user_id', $userId)
                ->where('id', '>', $since)
                ->orderBy('id', 'asc')
                ->get();

            if ($events->count() > 0) {
                $nextSince = $events->max('id');
                
                return response()->json([
                    'status' => 200,
                    'message' => 'Events found',
                    'data' => [
                        'since' => $since,
                        'next_since' => $nextSince,
                        'events' => $events->map(function ($event) {
                            return [
                                'id' => $event->id,
                                'type' => $event->type,
                                'data' => $event->data,
                                'created_at' => $event->created_at->toISOString(),
                            ];
                        }),
                    ],
                ]);
            }

            // Wait a bit before checking again
            sleep(1);
        }

        // No events found within timeout
        return response()->json([
            'status' => 200,
            'message' => 'No new events',
            'data' => [
                'since' => $since,
                'next_since' => $since,
                'events' => [],
            ],
        ], 200)->header('Content-Type', 'application/json');
    }

    /**
     * Search users for new conversations
     */
    public function searchUsers(Request $request): JsonResponse
    {
        $query = $request->get('query', '');
        $userId = Auth::id();
        
        if (strlen($query) < 2) {
            return response()->json([
                'status' => 400,
                'message' => 'Search query must be at least 2 characters',
            ], 400);
        }

        $cacheKey = "user_search_{$userId}_" . md5($query);
        
        // Try to get from cache first
        if (Cache::has($cacheKey)) {
            return response()->json(Cache::get($cacheKey));
        }

        // Use a more efficient search query
        $users = User::select(['id', 'username', 'first_name', 'last_name', 'name', 'avatar'])
            ->where('id', '!=', $userId)
            ->where('status', 1) // Active users only
            ->where(function ($q) use ($query) {
                $q->where('username', 'like', "{$query}%")  // Start with query (faster)
                  ->orWhere('first_name', 'like', "{$query}%")
                  ->orWhere('last_name', 'like', "{$query}%");
            })
            ->limit(20)
            ->get();

        $response = [
            'status' => 200,
            'message' => 'Users found',
            'data' => UserShortResource::collection($users),
        ];

        // Cache for 5 minutes (user data doesn't change frequently)
        Cache::put($cacheKey, $response, 300);

        return response()->json($response);
    }

    /**
     * Get conversation details
     */
    public function getConversation($conversationId): JsonResponse
    {
        $userId = Auth::id();
        
        $conversation = Conversation::visibleTo($userId)
            ->with(['lastMessage.user', 'participants.user'])
            ->find($conversationId);

        if (!$conversation) {
            return response()->json([
                'status' => 404,
                'message' => 'Conversation not found',
            ], 404);
        }

        return response()->json([
            'status' => 200,
            'message' => 'Conversation found',
            'data' => $this->formatConversation($conversation, $userId),
        ]);
    }

    /**
     * Get conversation participants
     */
    public function getParticipants($conversationId): JsonResponse
    {
        $userId = Auth::id();
        
        // Check if user is participant
        $participant = ConversationParticipant::where('conversation_id', $conversationId)
            ->where('user_id', $userId)
            ->whereNull('left_at')
            ->first();

        if (!$participant) {
            return response()->json([
                'status' => 403,
                'message' => 'Access denied to this conversation',
            ], 403);
        }

        $participants = ConversationParticipant::where('conversation_id', $conversationId)
            ->whereNull('left_at')
            ->with('user')
            ->get();

        return response()->json([
            'status' => 200,
            'message' => 'Participants found',
            'data' => $participants->map(function ($participant) use ($userId) {
                return [
                    'id' => $participant->user->id,
                    'user' => new UserShortResource($participant->user),
                    'role' => $participant->role,
                    'joined_at' => $participant->joined_at->toISOString(),
                    'is_current_user' => $participant->user_id === $userId,
                ];
            }),
        ]);
    }

    /**
     * Leave a conversation
     */
    public function leaveConversation($conversationId): JsonResponse
    {
        $userId = Auth::id();
        
        $participant = ConversationParticipant::where('conversation_id', $conversationId)
            ->where('user_id', $userId)
            ->whereNull('left_at')
            ->first();

        if (!$participant) {
            return response()->json([
                'status' => 404,
                'message' => 'Not a participant in this conversation',
            ], 404);
        }

        // Check if it's a DM (can't leave DMs)
        $conversation = Conversation::find($conversationId);
        if ($conversation->type === 'dm') {
            return response()->json([
                'status' => 400,
                'message' => 'Cannot leave direct message conversations',
            ], 400);
        }

        $participant->update([
            'left_at' => now(),
        ]);

        // Dispatch event
        $this->eventDispatcher->dispatch('conversation.left', [
            'conversation_id' => $conversationId,
            'user_id' => $userId,
        ]);

        return response()->json([
            'status' => 200,
            'message' => 'Successfully left conversation',
            'data' => null,
        ]);
    }

    /**
     * Get specific message
     */
    public function getMessage($conversationId, $messageId): JsonResponse
    {
        $userId = Auth::id();
        
        $message = Message::where('id', $messageId)
            ->where('conversation_id', $conversationId)
            ->where('is_deleted', false)
            ->with(['user', 'attachments'])
            ->first();

        if (!$message) {
            return response()->json([
                'status' => 404,
                'message' => 'Message not found',
            ], 404);
        }

        // Check if user has access to this conversation
        $participant = ConversationParticipant::where('conversation_id', $conversationId)
            ->where('user_id', $userId)
            ->whereNull('left_at')
            ->first();

        if (!$participant) {
            return response()->json([
                'status' => 403,
                'message' => 'Access denied to this conversation',
            ], 403);
        }

        return response()->json([
            'status' => 200,
            'message' => 'Message found',
            'data' => $this->formatMessage($message, $userId),
        ]);
    }

    /**
     * Update a message
     */
    public function updateMessage(Request $request, $conversationId, $messageId): JsonResponse
    {
        $validator = Validator::make($request->all(), [
            'content' => 'required|string|max:4000',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'status' => 400,
                'message' => 'Validation failed',
                'errors' => $validator->errors(),
            ], 400);
        }

        $userId = Auth::id();
        
        $message = Message::where('id', $messageId)
            ->where('conversation_id', $conversationId)
            ->where('user_id', $userId)
            ->where('is_deleted', false)
            ->first();

        if (!$message) {
            return response()->json([
                'status' => 404,
                'message' => 'Message not found or not editable',
            ], 404);
        }

        // Check if message is within edit window (e.g., 15 minutes)
        if ($message->created_at->diffInMinutes(now()) > 15) {
            return response()->json([
                'status' => 400,
                'message' => 'Message can only be edited within 15 minutes',
            ], 400);
        }

        $message->update([
            'content' => $request->content,
            'edited_at' => now(),
        ]);

        // Dispatch event
        $this->eventDispatcher->dispatch('message.updated', [
            'conversation_id' => $conversationId,
            'message_id' => $messageId,
            'user_id' => $userId,
        ]);

        return response()->json([
            'status' => 200,
            'message' => 'Message updated successfully',
            'data' => $this->formatMessage($message, $userId),
        ]);
    }

    /**
     * Delete a message
     */
    public function deleteMessage($conversationId, $messageId): JsonResponse
    {
        $userId = Auth::id();
        
        $message = Message::where('id', $messageId)
            ->where('conversation_id', $conversationId)
            ->where('user_id', $userId)
            ->where('is_deleted', false)
            ->first();

        if (!$message) {
            return response()->json([
                'status' => 404,
                'message' => 'Message not found or not deletable',
            ], 404);
        }

        $message->update([
            'is_deleted' => true,
            'deleted_at' => now(),
        ]);

        // Dispatch event
        $this->eventDispatcher->dispatch('message.deleted', [
            'conversation_id' => $conversationId,
            'message_id' => $messageId,
            'user_id' => $userId,
        ]);

        return response()->json([
            'status' => 200,
            'message' => 'Message deleted successfully',
            'data' => null,
        ]);
    }

    /**
     * Send typing indicator
     */
    public function sendTyping(Request $request): JsonResponse
    {
        $validator = Validator::make($request->all(), [
            'conversation_id' => 'required|integer|exists:conversations,id',
            'is_typing' => 'required|boolean',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'status' => 400,
                'message' => 'Validation failed',
                'errors' => $validator->errors(),
            ], 400);
        }

        $userId = Auth::id();
        
        // Check if user is participant
        $participant = ConversationParticipant::where('conversation_id', $request->conversation_id)
            ->where('user_id', $userId)
            ->whereNull('left_at')
            ->first();

        if (!$participant) {
            return response()->json([
                'status' => 403,
                'message' => 'Access denied to this conversation',
            ], 403);
        }

        // Dispatch typing event
        $this->eventDispatcher->dispatch('typing.updated', [
            'conversation_id' => $request->conversation_id,
            'user_id' => $userId,
            'is_typing' => $request->is_typing,
        ]);

        return response()->json([
            'status' => 200,
            'message' => 'Typing indicator sent',
            'data' => [
                'conversation_id' => $request->conversation_id,
                'is_typing' => $request->is_typing,
                'user_id' => $userId,
            ],
        ]);
    }

    /**
     * Get message receipts
     */
    public function getReceipts(Request $request): JsonResponse
    {
        $validator = Validator::make($request->all(), [
            'conversation_id' => 'required|integer|exists:conversations,id',
            'message_ids' => 'required|array',
            'message_ids.*' => 'integer|exists:messages,id',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'status' => 400,
                'message' => 'Validation failed',
                'errors' => $validator->errors(),
            ], 400);
        }

        $userId = Auth::id();
        
        // Check if user is participant
        $participant = ConversationParticipant::where('conversation_id', $request->conversation_id)
            ->where('user_id', $userId)
            ->whereNull('left_at')
            ->first();

        if (!$participant) {
            return response()->json([
                'status' => 403,
                'message' => 'Access denied to this conversation',
            ], 403);
        }

        $receipts = MessageReceipt::whereIn('message_id', $request->message_ids)
            ->where('user_id', $userId)
            ->get();

        return response()->json([
            'status' => 200,
            'message' => 'Receipts found',
            'data' => $receipts->map(function ($receipt) {
                return [
                    'message_id' => $receipt->message_id,
                    'type' => $receipt->type,
                    'delivered_at' => $receipt->delivered_at?->toISOString(),
                    'seen_at' => $receipt->seen_at?->toISOString(),
                ];
            }),
        ]);
    }

    /**
     * Mark messages as read
     */
    public function markAsRead(Request $request): JsonResponse
    {
        $validator = Validator::make($request->all(), [
            'conversation_id' => 'required|integer|exists:conversations,id',
            'message_ids' => 'required|array',
            'message_ids.*' => 'integer|exists:messages,id',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'status' => 400,
                'message' => 'Validation failed',
                'errors' => $validator->errors(),
            ], 400);
        }

        $userId = Auth::id();
        
        // Check if user is participant
        $participant = ConversationParticipant::where('conversation_id', $request->conversation_id)
            ->where('user_id', $userId)
            ->whereNull('left_at')
            ->first();

        if (!$participant) {
            return response()->json([
                'status' => 403,
                'message' => 'Access denied to this conversation',
            ], 403);
        }

        // Update or create receipts
        foreach ($request->message_ids as $messageId) {
            MessageReceipt::updateOrCreate(
                [
                    'message_id' => $messageId,
                    'user_id' => $userId,
                    'type' => 'seen',
                ],
                [
                    'seen_at' => now(),
                ]
            );
        }

        // Update participant's last seen
        $participant->update([
            'last_seen_at' => now(),
        ]);

        return response()->json([
            'status' => 200,
            'message' => 'Messages marked as read',
            'data' => null,
        ]);
    }

    /**
     * Search conversations
     */
    public function searchConversations(Request $request): JsonResponse
    {
        $query = $request->get('query', '');
        $type = $request->get('type', 'all');
        $limit = $request->integer('limit', 10);
        $userId = Auth::id();

        if (strlen($query) < 2) {
            return response()->json([
                'status' => 400,
                'message' => 'Search query must be at least 2 characters',
            ], 400);
        }

        $conversationsQuery = Conversation::visibleTo($userId)
            ->withUnreadCount($userId)
            ->with(['lastMessage.user', 'participants.user']);

        // Filter by type
        if ($type !== 'all') {
            $conversationsQuery->where('type', $type);
        }

        // Filter by query
        $conversationsQuery->where(function ($q) use ($query) {
            $q->where('title', 'like', "%{$query}%")
              ->orWhereHas('participants.user', function ($userQ) use ($query) {
                  $userQ->where('username', 'like', "%{$query}%")
                        ->orWhere('first_name', 'like', "%{$query}%")
                        ->orWhere('last_name', 'like', "%{$query}%")
                        ->orWhere('name', 'like', "%{$query}%");
              });
        });

        $conversations = $conversationsQuery->limit($limit)->get();

        // Transform conversations
        $conversations = $conversations->map(function ($conversation) use ($userId) {
            $participant = $conversation->participants->where('user_id', '!=', $userId)->first();
            
            return [
                'id' => $conversation->id,
                'type' => $conversation->type,
                'title' => $conversation->type === 'dm' && $participant 
                    ? $participant->user->full_name ?? $participant->user->username
                    : $conversation->title,
                'avatar' => $conversation->type === 'dm' && $participant 
                    ? getMedia($participant->user->avatar, 'avatar') 
                    : $conversation->avatar,
                'last_message' => $conversation->lastMessage ? [
                    'content' => $conversation->lastMessage->content,
                    'created_at' => $conversation->lastMessage->created_at->toISOString(),
                ] : null,
                'last_activity_at' => $conversation->last_activity_at->toISOString(),
                'unread_count' => $conversation->unread_count ?? 0,
                'is_group' => $conversation->type === 'group',
                'participants_count' => $conversation->participants->count(),
            ];
        });

        return response()->json([
            'status' => 200,
            'message' => 'Conversations found',
            'data' => $conversations,
        ]);
    }

    /**
     * Helper method to clear user conversation cache
     */
    protected function clearUserCache(int $userId): void
    {
        // Clear all conversation-related cache for this user
        $cacheKeys = [
            "user_conversations_{$userId}_*",
            "user_search_{$userId}_*",
        ];
        
        foreach ($cacheKeys as $pattern) {
            // Note: This is a simplified cache clearing. In production, you might want to use Redis SCAN
            // For now, we'll just clear the most common patterns
            Cache::forget("user_conversations_{$userId}_" . md5(serialize([])));
        }
    }

    /**
     * Helper method to determine file type from MIME type
     */
    private function getFileType($mimeType): string
    {
        if (str_starts_with($mimeType, 'image/')) {
            return 'image';
        } elseif (str_starts_with($mimeType, 'video/')) {
            return 'video';
        } elseif (str_starts_with($mimeType, 'audio/')) {
            return 'audio';
        } elseif (in_array($mimeType, [
            'application/pdf',
            'application/msword',
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'application/vnd.ms-excel',
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            'application/vnd.ms-powerpoint',
            'application/vnd.openxmlformats-officedocument.presentationml.presentation',
            'text/plain',
        ])) {
            return 'document';
        } else {
            return 'document';
        }
    }

    /**
     * Helper method to format conversation for response
     */
    protected function formatConversation($conversation, $userId)
    {
        $participant = $conversation->participants->where('user_id', '!=', $userId)->first();
        $currentUserParticipant = $conversation->participants->where('user_id', $userId)->first();
        
        return [
            'id' => $conversation->id,
            'type' => $conversation->type,
            'title' => $conversation->type === 'dm' && $participant 
                ? $participant->user->full_name ?? $participant->user->username
                : $conversation->title,
            'avatar' => $conversation->type === 'dm' && $participant 
                ? getMedia($participant->user->avatar, 'avatar')
                : getMedia($conversation->avatar, 'avatar'),
            'last_message' => $conversation->lastMessage ? [
                'content' => $conversation->lastMessage->content,
                'created_at' => $conversation->lastMessage->created_at->toISOString(),
                'user' => new UserShortResource($conversation->lastMessage->user),
                'is_own' => $conversation->lastMessage->user_id === $userId,
            ] : null,
            'last_activity_at' => $conversation->last_activity_at->toISOString(),
            'unread_count' => $conversation->unread_count ?? 0,
            'is_group' => $conversation->type === 'group',
            'participants_count' => $conversation->participants->count(),
            'user_id' => $userId,
            'user_role' => $currentUserParticipant?->role ?? 'member',
        ];
    }

    /**
     * Helper method to format message for response
     */
    protected function formatMessage($message, $userId)
    {
        return [
            'id' => $message->id,
            'conversation_id' => $message->conversation_id,
            'user' => new UserShortResource($message->user),
            'type' => $message->type,
            'content' => $message->content,
            'attachments' => $message->attachments->map(function ($attachment) {
                return [
                    'id' => $attachment->id,
                    'type' => $attachment->type,
                    'url' => $attachment->metadata && isset($attachment->metadata['stored_path']) 
                        ? getMedia($attachment->metadata['stored_path'], $attachment->type)
                        : $attachment->url,
                    'filename' => $attachment->filename,
                    'mime_type' => $attachment->mime_type,
                    'file_size' => $attachment->file_size,
                ];
            }),
            'reply_to' => $message->reply_to_message_id,
            'is_edited' => !is_null($message->edited_at),
            'is_deleted' => $message->is_deleted,
            'is_system' => false,
            'is_own' => $message->user_id === $userId,
            'client_generated_id' => $message->client_generated_id,
            'created_at' => $message->created_at->toISOString(),
            'edited_at' => $message->edited_at?->toISOString(),
        ];
    }

    /**
     * Helper method to mark messages as seen
     */
    protected function markMessagesAsSeen($conversationId, $userId, $messageIds)
    {
        foreach ($messageIds as $messageId) {
            MessageReceipt::updateOrCreate(
                [
                    'message_id' => $messageId,
                    'user_id' => $userId,
                    'type' => 'seen',
                ],
                [
                    'seen_at' => now(),
                ]
            );
        }

        // Update participant's last seen
        ConversationParticipant::where('conversation_id', $conversationId)
            ->where('user_id', $userId)
            ->update(['last_seen_at' => now()]);
    }
}
