<?php

namespace App\Http\Controllers\Meet;

use App\Http\Controllers\Controller;
use App\Http\Controllers\Meet\Concerns\BuildsMeetIndexPage;
use App\Http\Controllers\Meet\Concerns\ScopesToAccount;
use App\Models\Member;
use App\Models\Room;
use App\Services\Meet\CalendarService;
use App\Services\Meet\InvitationService;
use App\Services\Meet\RoomService;
use App\Services\Meet\SessionService;
use App\Services\Meet\SpaceService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class SpaceController extends Controller
{
    use BuildsMeetIndexPage, ScopesToAccount;

    public function __construct(
        protected RoomService $rooms,
        protected SessionService $sessions,
        protected InvitationService $invitations,
        protected CalendarService $calendar,
        protected SpaceService $spaces,
    ) {}

    public function index(Request $request): View
    {
        $this->authorizeAbility($request, 'meetings.view');
        $organization = $this->organization($request);
        $owner = $this->ownerRef($request);

        $query = Room::owned($owner)
            ->where('organization_id', $organization->id)
            ->where('type', 'space');

        $this->scopeToBranch($request, $query);

        $spaces = $query->orderByDesc('updated_at')->paginate(20);

        $stats = $this->meetIndexStats($request, $query, 'Rooms', 'Live now');
        $items = $this->meetIndexItems($spaces, fn (Room $room) => route('meet.spaces.show', $room));

        return view('meet.spaces.index', compact('spaces', 'organization', 'stats', 'items'));
    }

    public function create(Request $request): View
    {
        $this->authorizeAbility($request, 'meetings.create');
        $organization = $this->organization($request);
        $owner = $this->ownerRef($request);

        $members = Member::owned($owner)
            ->where('organization_id', $organization->id)
            ->where('user_ref', '!=', $owner)
            ->orderBy('user_ref')
            ->get();

        return view('meet.spaces.create', [
            'organization' => $organization,
            'members' => $members,
            'timezones' => timezone_identifiers_list(),
            'defaultSettings' => config('meet.default_settings'),
        ]);
    }

    public function store(Request $request): RedirectResponse
    {
        $this->authorizeAbility($request, 'meetings.create');
        $organization = $this->organization($request);

        $validated = $request->validate([
            'title' => ['required', 'string', 'max:255'],
            'description' => ['nullable', 'string', 'max:2000'],
            'scheduled_at' => ['nullable', 'date'],
            'duration_minutes' => ['nullable', 'integer', 'min:15', 'max:480'],
            'timezone' => ['nullable', 'timezone'],
            'passcode' => ['nullable', 'string', 'max:20'],
            'invite_emails' => ['nullable', 'string', 'max:2000', function (string $attribute, mixed $value, \Closure $fail): void {
                if (! is_string($value) || trim($value) === '') {
                    return;
                }

                foreach (preg_split('/[\s,;]+/', $value) as $email) {
                    $email = trim($email);
                    if ($email !== '' && ! filter_var($email, FILTER_VALIDATE_EMAIL)) {
                        $fail("Invalid email address: {$email}");
                    }
                }
            }],
            'speaker_refs' => ['nullable', 'array'],
            'speaker_refs.*' => ['string', 'max:64'],
            'invite_only' => ['sometimes', 'boolean'],
            'mute_listeners' => ['sometimes', 'boolean'],
        ]);

        $speakerRefs = collect($validated['speaker_refs'] ?? [])
            ->filter()
            ->unique()
            ->values()
            ->all();

        $settings = [
            'waiting_room' => false,
            'join_before_host' => false,
            'mute_on_join' => $request->boolean('mute_listeners', true),
            'video_off_on_join' => true,
            'audio_only' => true,
            'space_mode' => true,
            'stage_mode' => true,
            'invite_only' => $request->boolean('invite_only', true),
            'speaker_refs' => $speakerRefs,
        ];

        $data = array_merge($validated, [
            'settings' => $settings,
            'timezone' => $validated['timezone'] ?? $organization->timezone,
            'type' => 'space',
            'status' => empty($validated['scheduled_at']) ? 'scheduled' : 'scheduled',
        ]);

        unset($data['speaker_refs'], $data['invite_only'], $data['mute_listeners']);

        $room = $this->rooms->create($request->user(), $organization, $data);

        if ($emails = trim((string) ($validated['invite_emails'] ?? ''))) {
            $invites = collect(preg_split('/[\s,;]+/', $emails))
                ->filter()
                ->map(fn ($email) => ['email' => $email, 'role' => 'attendee'])
                ->all();
            $this->invitations->inviteToRoom($room, $invites, $request->user());
        }

        if (! empty($room->scheduled_at)) {
            $this->calendar->syncRoomCreated($room, $request->user());
        }

        return redirect()->route('meet.spaces.show', $room)->with('success', $this->spaceCreatedMessage($room, $validated['invite_emails'] ?? ''));
    }

    protected function spaceCreatedMessage(Room $room, string $inviteEmails): string
    {
        $count = count(array_filter(preg_split('/[\s,;]+/', trim($inviteEmails)) ?: []));

        if ($count === 0) {
            return 'Room created.';
        }

        return "Room created. {$count} listener invitation(s) sent.";
    }

    public function show(Request $request, Room $room): View
    {
        $this->authorizeAbility($request, 'meetings.view');
        $this->authorizeOwner($request, $room);
        abort_unless($room->isSpace(), 404);

        $room->load(['sessions.participants', 'invitations' => fn ($q) => $q->orderByDesc('created_at'), 'sessions.recordings', 'sessions.aiSummaries', 'sessionFiles']);

        $speakerRefs = (array) $room->setting('speaker_refs', []);
        $speakers = Member::owned($this->ownerRef($request))
            ->where('organization_id', $room->organization_id)
            ->whereIn('user_ref', $speakerRefs)
            ->get();

        return view('meet.spaces.show', compact('room', 'speakers'));
    }

    public function start(Request $request, Room $room): RedirectResponse
    {
        $this->authorizeAbility($request, 'meetings.host');
        $this->authorizeOwner($request, $room);
        abort_unless($room->isSpace(), 404);

        if ($room->status === 'cancelled') {
            return redirect()->route('meet.spaces.show', $room)
                ->withErrors(['start' => 'Room was closed.']);
        }

        $session = $this->sessions->start($room, $request->user());

        $participant = $this->sessions->joinedParticipantForUser($session, $request->user());
        abort_unless($participant, 500, 'Unable to open room as host.');

        $this->sessions->rememberParticipantSession($request, $participant, $session);

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