<?php

namespace App\Http\Controllers;

use App\Models\ChatCategory;
use App\Models\ChatConversation;
use App\Models\ChatMessage;
use App\Models\ChatAgent;
use App\Services\ChatService;
use Illuminate\Http\Request;

class ChatController extends Controller
{
    public function __construct(
        protected ChatService $chatService
    ) {}

    /**
     * Public chat start page for visitors.
     */
    public function publicStart(Request $request)
    {
        $categories = $this->chatService->getCategories(true);
        $name = $request->query('name', '');
        $email = $request->query('email', '');
        $message = $request->query('message', '');

        return view('chat.public.start', compact('categories', 'name', 'email', 'message'));
    }

    /**
     * Create a new public chat conversation.
     */
    public function publicCreate(Request $request)
    {
        $validated = $request->validate([
            'visitor_name'    => 'nullable|string|max:191',
            'visitor_email'   => 'nullable|email|max:191',
            'initial_message' => 'required|string|max:2000',
            'category_id'     => 'nullable|exists:chat_categories,id',
        ]);

        // Reuse an existing open session for this browser if one exists
        $existingSessionId = $request->cookie('chat_session_id');
        if ($existingSessionId) {
            $existing = ChatConversation::where('session_id', $existingSessionId)->open()->first();
            if ($existing) {
                return redirect()->route('chat.public.session', $existingSessionId);
            }
        }

        $sessionId = \Illuminate\Support\Str::random(32);

        $conversation = $this->chatService->startConversation(
            [
                'session_id' => $sessionId,
                'name'       => $validated['visitor_name'] ?? null,
                'email'      => $validated['visitor_email'] ?? null,
                'ip'         => $request->ip(),
                'user_agent' => $request->userAgent(),
            ],
            $validated['category_id'] ?? null,
            $validated['initial_message']
        );

        return redirect()
            ->route('chat.public.session', $conversation->session_id)
            ->withCookie(cookie('chat_session_id', $conversation->session_id, 60 * 24)); // 24 h
    }

    /**
     * Show public chat session.
     */
    public function publicSession(string $sessionId)
    {
        $conversation = ChatConversation::where('session_id', $sessionId)->firstOrFail();
        $conversation->load(['category', 'messages']);

        return view('chat.public.session', compact('conversation'));
    }

    /**
     * Send message in public chat session.
     */
    public function publicMessage(Request $request, string $sessionId)
    {
        $conversation = ChatConversation::where('session_id', $sessionId)->firstOrFail();

        $validated = $request->validate([
            'message' => 'required|string|max:10000',
        ]);

        $this->chatService->addVisitorMessage($conversation, $validated['message']);

        return redirect()->route('chat.public.session', $sessionId);
    }

    /**
     * End public chat session.
     */
    public function publicEnd(string $sessionId)
    {
        $conversation = ChatConversation::where('session_id', $sessionId)->firstOrFail();
        $this->chatService->endConversation($conversation, null, ChatConversation::END_REASON_VISITOR_CLOSED);

        return redirect()->route('chat.public.start')->with('success', 'Chat session ended. Thank you!');
    }

    /**
     * Create a new conversation via AJAX (JSON response for the widget).
     * No auth required — public visitors use this.
     */
    public function publicCreateJson(Request $request)
    {
        $validated = $request->validate([
            'visitor_name'    => 'nullable|string|max:191',
            'visitor_email'   => 'nullable|email|max:191',
            'initial_message' => 'required|string|max:2000',
            'category_id'     => 'nullable|exists:chat_categories,id',
        ]);

        // Reuse an existing open session for this browser if one exists
        $existingSessionId = $request->cookie('chat_session_id');
        if ($existingSessionId) {
            $existing = ChatConversation::where('session_id', $existingSessionId)->open()->first();
            if ($existing) {
                $existing->load(['messages' => fn($q) => $q->where('is_internal', false)->orderBy('id')]);
                return response()->json([
                    'success'    => true,
                    'session_id' => $existing->session_id,
                    'status'     => $existing->status,
                    'agent'      => $existing->agent?->name,
                    'messages'   => $existing->messages->map(fn($m) => [
                        'id'          => $m->id,
                        'sender_type' => $m->sender_type,
                        'sender_name' => $m->sender_display_name,
                        'message'     => $m->message,
                        'time'        => $m->created_at->format('H:i'),
                        'is_system'   => $m->isSystemMessage(),
                    ]),
                ])->withCookie(cookie('chat_session_id', $existing->session_id, 60 * 24));
            }
        }

        $sessionId = \Illuminate\Support\Str::random(32);

        $conversation = $this->chatService->startConversation(
            [
                'session_id' => $sessionId,
                'name'       => $validated['visitor_name'] ?? null,
                'email'      => $validated['visitor_email'] ?? null,
                'ip'         => $request->ip(),
                'user_agent' => $request->userAgent(),
            ],
            $validated['category_id'] ?? null,
            $validated['initial_message']
        );

        $conversation->load(['messages' => fn($q) => $q->where('is_internal', false)->orderBy('id')]);

        return response()->json([
            'success'    => true,
            'session_id' => $conversation->session_id,
            'status'     => $conversation->status,
            'agent'      => $conversation->agent?->name,
            'messages'   => $conversation->messages->map(fn($m) => [
                'id'          => $m->id,
                'sender_type' => $m->sender_type,
                'sender_name' => $m->sender_display_name,
                'message'     => $m->message,
                'time'        => $m->created_at->format('H:i'),
                'is_system'   => $m->isSystemMessage(),
            ]),
        ])->withCookie(cookie('chat_session_id', $conversation->session_id, 60 * 24));
    }

    /**
     * Send a visitor message via AJAX (JSON response for the widget).
     */
    public function publicMessageJson(Request $request, string $sessionId)
    {
        $conversation = ChatConversation::where('session_id', $sessionId)->firstOrFail();

        $validated = $request->validate([
            'message' => 'required|string|max:10000',
        ]);

        $msg = $this->chatService->addVisitorMessage($conversation, $validated['message']);

        return response()->json([
            'success' => true,
            'message' => [
                'id'          => $msg->id,
                'sender_type' => $msg->sender_type,
                'sender_name' => $msg->sender_display_name,
                'message'     => $msg->message,
                'time'        => $msg->created_at->format('H:i'),
                'is_system'   => false,
            ],
        ]);
    }

    /**
     * End a session via AJAX (JSON response for the widget).
     */
    public function publicEndJson(string $sessionId)
    {
        $conversation = ChatConversation::where('session_id', $sessionId)->firstOrFail();
        $this->chatService->endConversation($conversation, null, ChatConversation::END_REASON_VISITOR_CLOSED);

        return response()->json(['success' => true])
            ->withoutCookie('chat_session_id');
    }

    /**
     * Poll for new messages in a public session (JSON, no auth required).
     * Called every few seconds by the visitor's browser.
     */
    public function publicPoll(Request $request, string $sessionId)
    {
        $conversation = ChatConversation::where('session_id', $sessionId)->firstOrFail();

        $since = $request->query('since', 0); // last message ID the client already has

        $messages = ChatMessage::where('conversation_id', $conversation->id)
            ->where('id', '>', (int) $since)
            ->where('is_internal', false)
            ->orderBy('id')
            ->get()
            ->map(fn($m) => [
                'id'           => $m->id,
                'sender_type'  => $m->sender_type,
                'sender_name'  => $m->sender_display_name,
                'message'      => $m->message,
                'time'         => $m->created_at->format('H:i'),
                'is_system'    => $m->isSystemMessage(),
            ]);

        return response()->json([
            'status'   => $conversation->status,
            'agent'    => $conversation->agent?->name,
            'messages' => $messages,
        ]);
    }

    /**
     * Poll for new messages in an agent conversation (JSON, auth required).
     * Called every few seconds by the agent's browser.
     */
    public function agentPoll(Request $request, ChatConversation $conversation)
    {
        abort_unless(auth()->user()->can('handle chats'), 403);

        $since = $request->query('since', 0);

        $messages = ChatMessage::where('conversation_id', $conversation->id)
            ->where('id', '>', (int) $since)
            ->orderBy('id')
            ->get()
            ->map(fn($m) => [
                'id'           => $m->id,
                'sender_type'  => $m->sender_type,
                'sender_name'  => $m->sender_display_name,
                'message'      => $m->message,
                'time'         => $m->created_at->format('H:i'),
                'is_internal'  => $m->is_internal,
                'is_system'    => $m->isSystemMessage(),
            ]);

        // Mark visitor messages as read
        ChatMessage::where('conversation_id', $conversation->id)
            ->where('sender_type', 'visitor')
            ->where('is_read', false)
            ->update(['is_read' => true, 'read_at' => now()]);

        return response()->json([
            'status'   => $conversation->status,
            'messages' => $messages,
        ]);
    }

    /**
     * Display chat dashboard.
     */
    public function index(Request $request)
    {
        abort_unless(auth()->user()->can('view chats'), 403);

        $statistics = $this->chatService->getStatistics();
        $categories = $this->chatService->getCategories(true);
        $waitingConversations = $this->chatService->getWaitingConversations();

        return view('chat.index', compact('statistics', 'categories', 'waitingConversations'));
    }

    /**
     * Display categories management.
     */
    public function categories()
    {
        abort_unless(auth()->user()->can('manage chat categories'), 403);

        $categories = $this->chatService->getCategories();

        return view('chat.categories', compact('categories'));
    }

    /**
     * Store new category.
     */
    public function storeCategory(Request $request)
    {
        abort_unless(auth()->user()->can('manage chat categories'), 403);

        $validated = $request->validate([
            'name' => 'required|string|max:191',
            'description' => 'nullable|string',
            'icon' => 'required|string|max:100',
            'color' => 'required|string|max:20',
            'expected_response_time' => 'nullable|integer|min:1',
            'sort_order' => 'nullable|integer',
        ]);

        $this->chatService->createCategory($validated, auth()->id());

        return redirect()
            ->route('chat.categories')
            ->with('success', 'Category created successfully!');
    }

    /**
     * Update category.
     */
    public function updateCategory(Request $request, ChatCategory $category)
    {
        abort_unless(auth()->user()->can('manage chat categories'), 403);

        $validated = $request->validate([
            'name' => 'required|string|max:191',
            'description' => 'nullable|string',
            'icon' => 'required|string|max:100',
            'color' => 'required|string|max:20',
            'expected_response_time' => 'nullable|integer|min:1',
            'sort_order' => 'nullable|integer',
            'is_active' => 'nullable|boolean',
        ]);

        $this->chatService->updateCategory($category->id, $validated, auth()->id());

        return redirect()
            ->route('chat.categories')
            ->with('success', 'Category updated successfully!');
    }

    /**
     * Delete category.
     */
    public function destroyCategory(ChatCategory $category)
    {
        abort_unless(auth()->user()->can('manage chat categories'), 403);

        try {
            $this->chatService->deleteCategory($category->id, auth()->id());
            return redirect()
                ->route('chat.categories')
                ->with('success', 'Category deleted successfully!');
        } catch (\Exception $e) {
            return back()->with('error', $e->getMessage());
        }
    }

    /**
     * Display agents management.
     */
    public function agents(Request $request)
    {
        abort_unless(auth()->user()->can('appoint chat agents'), 403);

        $filters = $request->only(['status', 'is_active', 'search', 'per_page']);
        $agents = $this->chatService->getAgents($filters);
        $categories = $this->chatService->getCategories(true);

        return view('chat.agents', compact('agents', 'categories'));
    }

    /**
     * Appoint a new chat agent.
     */
    public function appointAgent(Request $request)
    {
        abort_unless(auth()->user()->can('appoint chat agents'), 403);

        $validated = $request->validate([
            'user_id' => 'required|exists:users,id',
            'categories' => 'nullable|array',
            'categories.*' => 'exists:chat_categories,id',
            'max_chats' => 'nullable|integer|min:1|max:10',
        ]);

        $this->chatService->appointAgent(
            $validated['user_id'],
            auth()->id(),
            [
                'categories' => $validated['categories'] ?? null,
                'max_chats' => $validated['max_chats'] ?? 3,
            ]
        );

        return redirect()
            ->route('chat.agents')
            ->with('success', 'Agent appointed successfully!');
    }

    /**
     * Remove chat agent.
     */
    public function removeAgent(ChatAgent $agent)
    {
        abort_unless(auth()->user()->can('appoint chat agents'), 403);

        try {
            $this->chatService->removeAgent($agent->id, auth()->id());
            return redirect()
                ->route('chat.agents')
                ->with('success', 'Agent removed successfully!');
        } catch (\Exception $e) {
            return back()->with('error', $e->getMessage());
        }
    }

    /**
     * Display conversations.
     */
    public function conversations(Request $request)
    {
        abort_unless(auth()->user()->can('handle chats'), 403);

        $user = auth()->user();

        // Initialize as empty collection — will be populated if user is an agent/manager
        $agentConversations = collect();

        // If user is a chat agent, show their conversations
        if ($user->hasAnyRole(['Super Manager', 'Manager']) || ChatAgent::where('user_id', $user->id)->active()->exists()) {
            $agentConversations = $this->chatService->getAgentConversations($user->id);
        }

        $filters = $request->only(['status', 'agent_id', 'category_id', 'search', 'date_from', 'date_to', 'per_page']);
        $history = $this->chatService->getConversationHistory($filters);
        $categories = $this->chatService->getCategories();

        return view('chat.conversations', compact('agentConversations', 'history', 'categories'));
    }

    /**
     * Show single conversation.
     */
    public function showConversation(ChatConversation $conversation)
    {
        abort_unless(auth()->user()->can('handle chats'), 403);

        $conversation->load(['category', 'agent', 'messages.sender']);

        // Mark messages as read
        $this->chatService->markMessagesAsRead($conversation, 'agent');

        return view('chat.show', compact('conversation'));
    }

    /**
     * Assign agent to conversation.
     */
    public function assignAgent(Request $request, ChatConversation $conversation)
    {
        abort_unless(auth()->user()->can('handle chats'), 403);

        $validated = $request->validate([
            'agent_id' => 'required|exists:users,id',
        ]);

        $this->chatService->assignAgent($conversation, $validated['agent_id']);

        return redirect()
            ->route('chat.conversation.show', $conversation)
            ->with('success', 'Agent assigned!');
    }

    /**
     * Send message in conversation.
     */
    public function sendMessage(Request $request, ChatConversation $conversation)
    {
        abort_unless(auth()->user()->can('handle chats'), 403);

        $validated = $request->validate([
            'message' => 'required|string|max:10000',
            'is_internal' => 'nullable|boolean',
        ]);

        $this->chatService->addAgentMessage(
            $conversation,
            auth()->id(),
            $validated['message'],
            $validated['is_internal'] ?? false
        );

        return redirect()
            ->route('chat.conversation.show', $conversation)
            ->with('success', 'Message sent!');
    }

    /**
     * End conversation.
     */
    public function endConversation(Request $request, ChatConversation $conversation)
    {
        abort_unless(auth()->user()->can('handle chats'), 403);

        $validated = $request->validate([
            'notes' => 'nullable|string|max:1000',
        ]);

        $this->chatService->endConversation(
            $conversation,
            auth()->id(),
            ChatConversation::END_REASON_AGENT_CLOSED,
            $validated['notes'] ?? null
        );

        return redirect()
            ->route('chat.conversations')
            ->with('success', 'Conversation ended.');
    }

    /**
     * Transfer conversation.
     */
    public function transferConversation(Request $request, ChatConversation $conversation)
    {
        abort_unless(auth()->user()->can('handle chats'), 403);

        $validated = $request->validate([
            'agent_id' => 'required|exists:users,id',
            'reason' => 'nullable|string|max:500',
        ]);

        $this->chatService->transferConversation(
            $conversation,
            $validated['agent_id'],
            auth()->id(),
            $validated['reason'] ?? null
        );

        return redirect()
            ->route('chat.conversations')
            ->with('success', 'Conversation transferred.');
    }

    /**
     * Update agent status.
     */
    public function updateStatus(Request $request)
    {
        abort_unless(auth()->user()->can('handle chats'), 403);

        $validated = $request->validate([
            'status' => 'required|in:online,away,offline,busy',
        ]);

        $agent = $this->chatService->updateAgentStatus(auth()->id(), $validated['status']);

        return response()->json([
            'success' => true,
            'status' => $agent->status,
        ]);
    }

    /**
     * Display agent-specific dashboard.
     */
    public function agentDashboard(Request $request)
    {
        abort_unless(auth()->user()->can('handle chats'), 403);

        $agent = ChatAgent::where('user_id', auth()->id())->first();

        $agentConversations = collect();
        $latestConversation = null;
        $resolvedToday = 0;

        if ($agent) {
            $agentConversations = ChatConversation::where('agent_id', $agent->user_id)
                ->with(['category', 'messages' => fn($q) => $q->latest()->limit(1)])
                ->orderByDesc('updated_at')
                ->get();

            $latestConversation = $agentConversations
                ->whereIn('status', ['active', 'waiting'])
                ->first();

            $resolvedToday = ChatConversation::where('agent_id', $agent->user_id)
                ->where('status', 'resolved')
                ->whereDate('updated_at', today())
                ->count();
        }

        return view('chat.agent-dashboard', compact('agent', 'agentConversations', 'latestConversation', 'resolvedToday'));
    }
}
