<?php

namespace App\Services\Meet;

use App\Models\Member;
use App\Models\Organization;
use App\Models\Room;
use App\Models\User;
use Illuminate\Support\Str;

class SecurityPolicyService
{
    public function canJoin(Room $room, ?User $user, ?string $email, ?string $ip = null): array
    {
        $org = $room->organization;
        $policy = $this->mergedPolicy($org, $room);
        $kind = $room->isConference() ? 'conference' : ($room->isSpace() ? 'room' : 'meeting');

        if ($policy['org_only'] && ! $user) {
            return [false, "This {$kind} requires a Ladill account."];
        }

        if ($policy['authenticated_only'] && ! $user) {
            return [false, "Please sign in to join this {$kind}."];
        }

        if (! empty($policy['allowed_domains']) && $email) {
            $domain = Str($email)->after('@')->lower()->toString();
            $allowed = collect($policy['allowed_domains'])->map(fn ($d) => strtolower(trim($d)))->filter();
            if ($allowed->isNotEmpty() && ! $allowed->contains($domain)) {
                return [false, "Your email domain is not allowed to join this {$kind}."];
            }
        }

        if ($room->isSpace() && $room->setting('invite_only', false) && ! $this->canJoinInviteOnlyRoom($room, $user, $email)) {
            return [false, 'This room is private. Ask the host for an invitation.'];
        }

        if ($room->passcode && ! session()->get("meet.passcode.{$room->uuid}")) {
            return [false, 'passcode_required'];
        }

        if ($room->activeSession()?->is_locked && ! $this->isHost($room, $user)) {
            return [false, "This {$kind} is locked."];
        }

        if ($policy['recording_host_only'] && ! $this->isHost($room, $user)) {
            // enforced at recording start
        }

        return [true, null];
    }

    public function canJoinInviteOnlyRoom(Room $room, ?User $user, ?string $email): bool
    {
        if ($user && $this->isHost($room, $user)) {
            return true;
        }

        if ($user && $room->organization_id) {
            $isMember = Member::query()
                ->where('organization_id', $room->organization_id)
                ->where('user_ref', $user->ownerRef())
                ->exists();

            if ($isMember) {
                return true;
            }

            $speakerRefs = (array) $room->setting('speaker_refs', []);
            if (in_array($user->ownerRef(), $speakerRefs, true)) {
                return true;
            }
        }

        $email = strtolower(trim((string) ($email ?? $user?->email ?? '')));
        if ($email === '') {
            return false;
        }

        return $room->invitations()
            ->whereRaw('LOWER(email) = ?', [$email])
            ->whereIn('status', ['pending', 'accepted'])
            ->exists();
    }

    public function isHost(Room $room, ?User $user): bool
    {
        return $user && $user->ownerRef() === $room->host_user_ref;
    }

    /**
     * @param  array<string, mixed>  $policy
     */
    public function updateOrganizationPolicy(Organization $org, array $policy): Organization
    {
        $org->update([
            'security_policy' => array_merge($org->security_policy ?? [], $policy),
        ]);

        AuditLogger::record($org->owner_ref, 'organization.security_updated', $org->id, auth()->user()?->ownerRef());

        return $org->fresh();
    }

    /**
     * @return array<string, mixed>
     */
    protected function mergedPolicy(?Organization $org, Room $room): array
    {
        return array_merge(
            config('meet.security_defaults'),
            $org?->security_policy ?? [],
            $room->settings['security'] ?? [],
        );
    }
}
