<?php

namespace App\Services\Meet;

use App\Models\Participant;
use App\Models\Room;
use App\Models\User;

class SpaceService
{
    /**
     * Resolve participant role for an audio-only room join.
     */
    public function resolveJoinRole(Room $room, ?User $user, string $fallbackRole = 'guest'): string
    {
        if ($room->type !== 'space') {
            return $fallbackRole;
        }

        if ($user && $user->ownerRef() === $room->host_user_ref) {
            return 'host';
        }

        $speakerRefs = (array) $room->setting('speaker_refs', []);

        if ($user && in_array($user->ownerRef(), $speakerRefs, true)) {
            return 'panelist';
        }

        return 'attendee';
    }

    public function canPublish(Room $room, Participant $participant): bool
    {
        if ($room->type !== 'space') {
            return true;
        }

        return in_array($participant->role, ['host', 'co_host', 'panelist'], true);
    }

    public function isListener(Room $room, Participant $participant): bool
    {
        return $room->type === 'space' && $participant->role === 'attendee';
    }

    public function isAudioOnly(Room $room): bool
    {
        return $room->isSpace() || (bool) $room->setting('audio_only', false);
    }

    /**
     * Whether a live-room feature is available for this room type.
     * Audio rooms (Spaces) mirror Twitter Spaces — no breakouts, Q&A, etc.
     */
    public function allowsRoomFeature(Room $room, string $feature): bool
    {
        if (! $room->isSpace()) {
            return true;
        }

        return ! in_array($feature, [
            'breakouts',
            'qa',
            'polls',
            'files',
            'whiteboard',
            'programme',
            'live_stream',
            'lock',
        ], true);
    }

    public function inviteToSpeak(Room $room, Participant $participant): Participant
    {
        abort_unless($room->isSpace(), 422);
        abort_unless(in_array($participant->role, ['attendee', 'guest', 'participant'], true), 422);
        abort_unless(! $participant->speak_requested, 422, 'An invitation is already pending.');

        $participant->update([
            'speak_requested' => true,
            'hand_raised' => false,
        ]);

        return $participant->fresh();
    }

    public function acceptSpeakInvitation(Room $room, Participant $participant): Participant
    {
        abort_unless($room->isSpace(), 422);
        abort_unless(in_array($participant->role, ['attendee', 'guest', 'participant'], true), 422);
        abort_unless($participant->speak_requested, 422, 'No speak invitation to accept.');

        return $this->promoteToSpeaker($room, $participant);
    }

    public function declineSpeakInvitation(Room $room, Participant $participant): Participant
    {
        abort_unless($room->isSpace(), 422);
        abort_unless($participant->speak_requested, 422);

        $participant->update(['speak_requested' => false]);

        return $participant->fresh();
    }

    public function dismissSpeakInvitation(Room $room, Participant $participant): Participant
    {
        abort_unless($room->isSpace(), 422);
        abort_unless($participant->speak_requested, 422);

        $participant->update(['speak_requested' => false]);

        return $participant->fresh();
    }

    public function promoteToSpeaker(Room $room, Participant $participant): Participant
    {
        abort_unless($room->isSpace(), 422);
        abort_unless(in_array($participant->role, ['attendee', 'guest', 'participant'], true), 422);

        $participant->update([
            'role' => 'panelist',
            'hand_raised' => false,
            'speak_requested' => false,
        ]);

        if ($participant->user_ref) {
            $speakerRefs = collect((array) $room->setting('speaker_refs', []))
                ->push($participant->user_ref)
                ->unique()
                ->values()
                ->all();

            $room->update([
                'settings' => array_merge($room->settings ?? [], [
                    'speaker_refs' => $speakerRefs,
                ]),
            ]);
        }

        return $participant->fresh();
    }

    public function promoteToCoHost(Room $room, Participant $participant): Participant
    {
        abort_unless($room->isSpace(), 422);
        abort_unless($participant->role === 'panelist', 422);

        $participant->update(['role' => 'co_host']);

        return $participant->fresh();
    }

    public function demoteFromSpeaker(Room $room, Participant $participant): Participant
    {
        abort_unless($room->isSpace(), 422);
        abort_unless(in_array($participant->role, ['panelist', 'co_host'], true), 422);

        $participant->update([
            'role' => 'attendee',
            'speak_requested' => false,
        ]);

        if ($participant->user_ref) {
            $speakerRefs = collect((array) $room->setting('speaker_refs', []))
                ->reject(fn (string $ref) => $ref === $participant->user_ref)
                ->values()
                ->all();

            $room->update([
                'settings' => array_merge($room->settings ?? [], [
                    'speaker_refs' => $speakerRefs,
                ]),
            ]);
        }

        return $participant->fresh();
    }
}
