<?php

namespace App\Services\Meet;

use App\Models\BreakoutAssignment;
use App\Models\BreakoutRoom;
use App\Models\Message;
use App\Models\Participant;
use App\Models\Session;
use App\Services\Meet\Media\MediaProviderInterface;
use Illuminate\Support\Str;

class BreakoutService
{
    public function __construct(
        protected MediaProviderInterface $media,
    ) {}

    /**
     * @param  array<int, array{participant_id: int, breakout_room_id?: int}>|null  $assignments
     * @return \Illuminate\Support\Collection<int, BreakoutRoom>
     */
    public function createRooms(Session $session, int $count, ?int $timerMinutes = null, ?array $assignments = null)
    {
        $timerMinutes ??= config('meet.breakouts.default_timer_minutes', 15);
        $closesAt = now()->addMinutes($timerMinutes);
        $rooms = collect();

        for ($i = 1; $i <= $count; $i++) {
            $mediaName = $session->media_room_name.'-breakout-'.Str::random(8);
            $this->media->createRoom($mediaName);

            $rooms->push(BreakoutRoom::create([
                'owner_ref' => $session->owner_ref,
                'session_id' => $session->id,
                'name' => 'Breakout '.$i,
                'media_room_name' => $mediaName,
                'status' => 'open',
                'closes_at' => $closesAt,
            ]));
        }

        $participants = $session->participants()
            ->where('status', 'joined')
            ->whereNotIn('role', ['host', 'co_host'])
            ->get();

        if ($assignments) {
            foreach ($assignments as $assignment) {
                $this->assignParticipant(
                    $rooms->firstWhere('id', $assignment['breakout_room_id']) ?? $rooms->first(),
                    Participant::findOrFail($assignment['participant_id']),
                );
            }
        } else {
            $participants->values()->each(function (Participant $participant, int $index) use ($rooms) {
                $this->assignParticipant($rooms[$index % $rooms->count()], $participant);
            });
        }

        return $rooms;
    }

    public function assignParticipant(BreakoutRoom $breakout, Participant $participant): void
    {
        BreakoutAssignment::updateOrCreate(
            ['breakout_room_id' => $breakout->id, 'participant_id' => $participant->id],
        );
        $participant->update(['breakout_room_id' => $breakout->id]);
    }

    public function moveParticipant(Participant $participant, BreakoutRoom $breakout): void
    {
        $this->assignParticipant($breakout, $participant);
    }

    public function returnToMain(Participant $participant): void
    {
        $participant->update(['breakout_room_id' => null]);
        BreakoutAssignment::where('participant_id', $participant->id)->delete();
    }

    public function broadcast(Session $session, string $message, string $senderName): Message
    {
        return Message::create([
            'owner_ref' => $session->owner_ref,
            'session_id' => $session->id,
            'sender_name' => $senderName,
            'type' => 'breakout_broadcast',
            'body' => $message,
        ]);
    }

    public function closeAll(Session $session): void
    {
        $session->breakoutRooms()->update(['status' => 'closed']);
        $session->participants()->whereNotNull('breakout_room_id')->update(['breakout_room_id' => null]);
        BreakoutAssignment::whereIn('breakout_room_id', $session->breakoutRooms()->pluck('id'))->delete();
    }

    public function hasActiveBreakouts(Session $session): bool
    {
        return $session->breakoutRooms()->where('status', 'open')->exists();
    }

    public function closeExpired(Session $session): bool
    {
        if (! $session->breakoutRooms()->where('status', 'open')->where('closes_at', '<=', now())->exists()) {
            return false;
        }

        $this->closeAll($session);

        return true;
    }

    public function isInActiveBreakout(Participant $participant): bool
    {
        if (! $participant->breakout_room_id) {
            return false;
        }

        return BreakoutRoom::query()
            ->where('id', $participant->breakout_room_id)
            ->where('status', 'open')
            ->exists();
    }

    public function mediaRoomForParticipant(Participant $participant): string
    {
        if ($participant->breakout_room_id) {
            return $participant->breakoutRoom?->media_room_name ?? $participant->session->media_room_name;
        }

        return $participant->session->media_room_name;
    }
}
