<?php

namespace App\Http\Controllers\Meet;

use App\Http\Controllers\Controller;
use App\Models\Participant;
use App\Models\Recording;
use App\Models\Room;
use App\Models\Session;
use App\Services\Meet\ChatService;
use App\Services\Meet\Media\MediaProviderInterface;
use App\Services\Meet\ParticipantPresenter;
use App\Services\Meet\RecordingService;
use App\Services\Meet\SecurityPolicyService;
use App\Services\Meet\SessionService;
use App\Services\Meet\SpaceService;
use App\Services\Meet\StageLayoutService;
use App\Services\Meet\TranscriptService;
use App\Services\Meet\WebinarService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class MeetingRoomController extends Controller
{
    public function __construct(
        protected SessionService $sessions,
        protected ChatService $chat,
        protected MediaProviderInterface $media,
        protected RecordingService $recordings,
        protected TranscriptService $transcripts,
        protected SecurityPolicyService $security,
    ) {}

    public function show(Request $request, Session $session): View|RedirectResponse
    {
        if (! $session->isLive()) {
            return redirect()->route('meet.ended', $session);
        }

        $room = $session->room;
        $kind = $room->isConference() ? 'conference' : 'meeting';

        $participantUuid = $request->session()->get("meet.participant.{$session->uuid}");
        abort_unless($participantUuid, 403, "Please join the {$kind} first.");

        $participant = Participant::where('uuid', $participantUuid)
            ->where('session_id', $session->id)
            ->firstOrFail();

        if ($participant->status === 'waiting') {
            return redirect()->route('meet.join.waiting', $session->room);
        }

        abort_unless($participant->status === 'joined', 403, "You are not in this {$kind}.");

        $token = $this->media->isConfigured()
            ? $this->sessions->generateMediaToken($participant)
            : '';

        $session->load(['room', 'participants']);

        $room = $session->room;
        $webinars = app(WebinarService::class);
        $spaces = app(SpaceService::class);
        $isAudioOnly = $room->isAudioOnly();
        $stageLayout = app(StageLayoutService::class);
        $authUser = $request->user();
        $isRoomHost = $authUser && $authUser->ownerRef() === $room->host_user_ref;
        $canManageConference = $participant->isHost() || $isRoomHost;

        return view('meet.room.show', [
            'session' => $session,
            'room' => $room,
            'participant' => $participant,
            'mediaToken' => $token,
            'mediaUrl' => $this->media->serverUrl(),
            'mediaConfigured' => $this->media->isConfigured(),
            'messages' => $this->chat->recentMessages($session),
            'activeRecording' => $session->recordings()->where('status', 'recording')->first(),
            'watermark' => $room->organization?->securitySetting('watermark_enabled', false),
            'isWebinar' => $room->isWebinar() || $room->isConference() || $room->isSpace(),
            'isConference' => $room->isConference(),
            'isGreenRoom' => $session->session_mode === 'green_room',
            'sessionMode' => $session->session_mode,
            'presenterRefs' => (array) $session->presenter_refs,
            'isAudioOnly' => $isAudioOnly,
            'isSpace' => $room->isSpace(),
            'canPublish' => $room->isSpace()
                ? $spaces->canPublish($room, $participant)
                : $webinars->canPublish($room, $participant),
            'isAttendee' => $participant->isAttendee() || $spaces->isListener($room, $participant),
            'usesStageLayout' => $stageLayout->usesStageLayout($room),
            'stageConfig' => $stageLayout->config($room),
            'canManageConference' => $canManageConference,
        ]);
    }

    public function end(Request $request, Session $session): RedirectResponse
    {
        $participantUuid = $request->session()->get("meet.participant.{$session->uuid}");
        $participant = Participant::where('uuid', $participantUuid)->first();

        abort_unless($participant?->isHost(), 403);

        $this->sessions->end($session, $participant->user_ref ?? $participant->uuid);

        $room = $session->room;

        return redirect()->route($this->roomShowRoute($room), $room)
            ->with('success', $this->roomEndedMessage($room));
    }

    protected function roomShowRoute(Room $room): string
    {
        if ($room->isWebinar()) {
            return 'meet.webinars.show';
        }

        if ($room->isConference()) {
            return 'meet.conferences.show';
        }

        if ($room->isSpace()) {
            return 'meet.spaces.show';
        }

        return 'meet.rooms.show';
    }

    protected function roomEndedMessage(Room $room): string
    {
        if ($room->isWebinar()) {
            return 'Webinar ended.';
        }

        if ($room->isConference()) {
            return 'Conference ended.';
        }

        if ($room->isSpace()) {
            return 'Room closed.';
        }

        return 'Meeting ended.';
    }

    public function leave(Request $request, Session $session): RedirectResponse
    {
        $participantUuid = $request->session()->get("meet.participant.{$session->uuid}");
        $participant = Participant::where('uuid', $participantUuid)->first();

        if ($participant) {
            $this->sessions->leaveParticipant($participant);

            $request->session()->put('meet.left.feedback', [
                'session_uuid' => $session->uuid,
                'participant_uuid' => $participant->uuid,
                'display_name' => $participant->display_name,
                'user_ref' => $participant->user_ref,
            ]);
        }

        $request->session()->forget("meet.participant.{$session->uuid}");

        return redirect()->route('meet.left', $session);
    }

    public function raiseHand(Request $request, Session $session): JsonResponse
    {
        $participant = $this->currentParticipant($request, $session);
        $participant->update(['hand_raised' => ! $participant->hand_raised]);

        return response()->json(['hand_raised' => $participant->hand_raised]);
    }

    public function sendMessage(Request $request, Session $session): JsonResponse
    {
        $participant = $this->currentParticipant($request, $session);

        $validated = $request->validate(['body' => ['required', 'string', 'max:2000']]);

        $message = $this->chat->sendPublicMessage(
            $session,
            $participant->display_name,
            $validated['body'],
            $participant->user_ref,
        );

        return response()->json([
            'message' => [
                'uuid' => $message->uuid,
                'sender_name' => $message->sender_name,
                'body' => $message->body,
                'created_at' => $message->created_at->toIso8601String(),
            ],
        ]);
    }

    public function sendReaction(Request $request, Session $session): JsonResponse
    {
        $participant = $this->currentParticipant($request, $session);

        $validated = $request->validate(['emoji' => ['required', 'string', 'max:32']]);

        $reaction = $this->chat->sendReaction(
            $session,
            $participant->display_name,
            $validated['emoji'],
            $participant->uuid,
        );

        return response()->json([
            'reaction' => [
                'sender_name' => $reaction->sender_name,
                'emoji' => $reaction->emoji,
                'created_at' => $reaction->created_at->toIso8601String(),
            ],
        ]);
    }

    public function poll(Request $request, Session $session): JsonResponse
    {
        if (! $session->isLive()) {
            return response()->json([
                'ended' => true,
                'redirect' => route('meet.ended', $session),
            ]);
        }

        $this->currentParticipant($request, $session);

        $session->load(['participants' => fn ($q) => $q->whereIn('status', ['joined', 'waiting'])]);

        $avatars = ParticipantPresenter::avatarMap($session->participants, $session->room->host_user_ref);

        $messages = $this->chat->recentMessages($session, 50);
        $reactions = $session->reactions()->where('created_at', '>=', now()->subMinutes(5))->latest()->limit(20)->get();
        $qa = app(\App\Services\Meet\QaService::class)->forSession($session, true);
        $polls = $session->polls()->where('status', 'open')->with('votes')->get();
        $breakouts = $session->breakoutRooms()->where('status', 'open')->get();
        $broadcasts = $session->messages()->where('type', 'breakout_broadcast')->where('created_at', '>=', now()->subMinutes(30))->latest()->limit(5)->get();
        $files = $session->sessionFiles()
            ->where(function ($q) {
                $q->whereNull('expires_at')->orWhere('expires_at', '>', now());
            })
            ->latest()
            ->limit(20)
            ->get();

        return response()->json(array_merge([
            'participants' => $session->participants->map(
                fn ($p) => ParticipantPresenter::toArray($p, $avatars, $session->room->host_user_ref)
            ),
            'waiting_count' => $session->participants->where('status', 'waiting')->count(),
            'messages' => $messages->map(fn ($m) => [
                'uuid' => $m->uuid,
                'sender_name' => $m->sender_name,
                'body' => $m->body,
                'type' => $m->type,
                'created_at' => $m->created_at->toIso8601String(),
            ]),
            'reactions' => $reactions->map(fn ($r) => [
                'sender_name' => $r->sender_name,
                'emoji' => $r->emoji,
                'created_at' => $r->created_at->toIso8601String(),
            ]),
            'qa' => $qa->map(fn ($q) => [
                'uuid' => $q->uuid,
                'asker_name' => $q->asker_name,
                'question' => $q->question,
                'status' => $q->status,
                'answer' => $q->answer,
                'upvotes' => $q->upvotes,
            ]),
            'polls' => $polls->map(fn ($p) => [
                'uuid' => $p->uuid,
                'question' => $p->question,
                'options' => $p->options,
                'votes' => $p->votes->count(),
            ]),
            'breakouts' => $breakouts->map(fn ($b) => [
                'uuid' => $b->uuid,
                'name' => $b->name,
                'closes_at' => $b->closes_at?->toIso8601String(),
            ]),
            'broadcasts' => $broadcasts->map(fn ($m) => [
                'body' => $m->body,
                'sender_name' => $m->sender_name,
                'created_at' => $m->created_at->toIso8601String(),
            ]),
            'files' => $files->map(fn ($f) => [
                'uuid' => $f->uuid,
                'original_name' => $f->original_name,
                'file_size' => $f->file_size,
            ]),
        ], app(StageLayoutService::class)->config($session->room)));
    }

    public function startRecording(Request $request, Session $session): JsonResponse
    {
        $participant = $this->currentParticipant($request, $session);
        abort_unless($participant->isHost(), 403);
        abort_if($session->room->organization?->securitySetting('recording_host_only') && ! $participant->isHost(), 403);

        $host = $request->user() ?? \App\Models\User::where('public_id', $session->room->host_user_ref)->first();
        abort_unless($host, 403);

        $recording = $this->recordings->start($session, $host);

        return response()->json(['recording_uuid' => $recording->uuid, 'status' => $recording->status]);
    }

    public function stopRecording(Request $request, Session $session): JsonResponse
    {
        $participant = $this->currentParticipant($request, $session);
        abort_unless($participant->isHost(), 403);

        $recording = $session->recordings()->where('status', 'recording')->firstOrFail();
        $recording = $this->recordings->stop($recording, $participant->user_ref ?? $participant->uuid);

        return response()->json([
            'status' => 'processing',
            'recording_uuid' => $recording->uuid,
        ]);
    }

    public function uploadRecording(Request $request, Session $session, Recording $recording): JsonResponse
    {
        $participant = $this->currentParticipant($request, $session);
        abort_unless($participant->isHost(), 403);
        abort_unless($recording->session_id === $session->id, 404);

        $maxKb = max(1, (int) config('meet.recordings.max_mb', 512)) * 1024;

        $request->validate([
            'recording' => ['required', 'file', 'mimetypes:video/webm,video/mp4,video/x-matroska,video/quicktime', 'max:'.$maxKb],
        ]);

        $this->recordings->storeUpload($recording, $request->file('recording'));

        return response()->json(['status' => 'ready', 'recording_uuid' => $recording->uuid]);
    }

    public function lock(Request $request, Session $session): JsonResponse
    {
        $participant = $this->currentParticipant($request, $session);
        abort_unless($participant->isHost(), 403);
        $this->sessions->lock($session, $participant->user_ref ?? $participant->uuid);

        return response()->json(['locked' => true]);
    }

    public function unlock(Request $request, Session $session): JsonResponse
    {
        $participant = $this->currentParticipant($request, $session);
        abort_unless($participant->isHost(), 403);
        $this->sessions->unlock($session, $participant->user_ref ?? $participant->uuid);

        return response()->json(['locked' => false]);
    }

    public function caption(Request $request, Session $session): JsonResponse
    {
        $participant = $this->currentParticipant($request, $session);
        $validated = $request->validate(['text' => ['required', 'string', 'max:500']]);

        if (! $session->room->setting('live_captions', false)) {
            return response()->json(['ok' => false], 422);
        }

        $transcript = $session->transcripts()->latest()->first()
            ?? $this->transcripts->ensureForSession($session);

        $this->transcripts->appendSegment($transcript, $participant->display_name, $validated['text']);

        return response()->json(['ok' => true]);
    }

    public function admit(Request $request, Session $session, Participant $participant): JsonResponse
    {
        $current = $this->currentParticipant($request, $session);
        abort_unless($current->isHost(), 403);
        abort_unless($participant->session_id === $session->id, 404);

        $this->sessions->admitParticipant($participant);

        return response()->json(['ok' => true, 'status' => 'joined']);
    }

    public function deny(Request $request, Session $session, Participant $participant): JsonResponse
    {
        $current = $this->currentParticipant($request, $session);
        abort_unless($current->isHost(), 403);
        abort_unless($participant->session_id === $session->id, 404);

        $this->sessions->denyParticipant($participant);

        return response()->json(['ok' => true, 'status' => 'removed']);
    }

    protected function currentParticipant(Request $request, Session $session): Participant
    {
        $participantUuid = $request->session()->get("meet.participant.{$session->uuid}");
        abort_unless($participantUuid, 403);

        return Participant::where('uuid', $participantUuid)
            ->where('session_id', $session->id)
            ->firstOrFail();
    }
}
