<?php

namespace App\Http\Controllers\Api\Tenant;

use App\Http\Controllers\Controller;
use App\Http\Requests\Tenant\EventMemberInviteRequest;
use App\Http\Resources\Tenant\EventMemberResource;
use App\Models\Event;
use App\Models\EventMember;
use App\Models\Tenant;
use App\Models\User;
use App\Support\TenantMemberPermissions;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;

class EventMemberController extends Controller
{
    public function index(Request $request, Event $event): JsonResponse
    {
        $this->assertEventTenant($request, $event);
        TenantMemberPermissions::ensureEventPermission($request, $event, 'members:manage');

        /** @var LengthAwarePaginator $members */
        $members = $event->members()
            ->orderByDesc('created_at')
            ->paginate($request->integer('per_page', 25));

        return EventMemberResource::collection($members)->response();
    }

    public function store(EventMemberInviteRequest $request, Event $event): JsonResponse
    {
        $this->assertEventTenant($request, $event);
        TenantMemberPermissions::ensureEventPermission($request, $event, 'members:manage');

        $data = $request->validated();
        $tenant = $this->resolveTenantFromRequest($request);

        if (! $tenant) {
            throw ValidationException::withMessages([
                'tenant_id' => __('Tenant context missing for invitation.'),
            ]);
        }

        $email = Str::lower($data['email']);
        $role = $data['role'];
        $name = $data['name'] ?? null;

        $user = $this->findOrCreateUser($tenant, $email, $name, $role);

        if ((int) $user->tenant_id !== (int) $tenant->id) {
            throw ValidationException::withMessages([
                'email' => __('Dieser Benutzer gehört bereits zu einem anderen Mandanten.'),
            ]);
        }

        $existing = EventMember::query()
            ->where('event_id', $event->id)
            ->where('email', $email)
            ->first();

        $member = EventMember::updateOrCreate(
            [
                'event_id' => $event->id,
                'email' => $email,
            ],
            [
                'tenant_id' => $tenant->id,
                'user_id' => $user->id,
                'invited_by' => $request->user()?->id,
                'name' => $name ?? $user->name ?? $email,
                'role' => $role,
                'status' => $user->email_verified_at ? 'active' : 'invited',
                'invite_token' => $this->ensureInviteToken($event, $email),
                'invited_at' => $existing?->invited_at ?? now(),
                'joined_at' => $user->email_verified_at ? ($existing?->joined_at ?? now()) : null,
                'permissions' => $this->defaultPermissionsForRole($role),
            ]
        );

        $member->refresh();

        if (! $user->hasVerifiedEmail()) {
            $user->sendEmailVerificationNotification();
        }

        return (new EventMemberResource($member))->response()->setStatusCode(201);
    }

    public function destroy(Request $request, Event $event, EventMember $member): JsonResponse
    {
        $this->assertEventTenant($request, $event);
        TenantMemberPermissions::ensureEventPermission($request, $event, 'members:manage');

        if ((int) $member->event_id !== (int) $event->id) {
            throw ValidationException::withMessages([
                'member_id' => __('Dieses Mitglied gehört nicht zu diesem Event.'),
            ]);
        }

        $member->delete();

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

    private function assertEventTenant(Request $request, Event $event): void
    {
        $tenantId = $request->attributes->get('tenant_id');
        if ($tenantId === null || (int) $event->tenant_id !== (int) $tenantId) {
            abort(403, 'Event belongs to a different tenant.');
        }
    }

    private function resolveTenantFromRequest(Request $request): ?Tenant
    {
        $tenant = $request->attributes->get('tenant');

        if ($tenant instanceof Tenant) {
            return $tenant;
        }

        $tenantId = $request->attributes->get('tenant_id');

        return $tenantId ? Tenant::query()->find($tenantId) : null;
    }

    private function findOrCreateUser(Tenant $tenant, string $email, ?string $name, string $role): User
    {
        $user = User::query()->where('email', $email)->first();

        if (! $user) {
            $user = new User;
            $user->email = $email;
            $user->password = Hash::make(Str::random(32));
        }

        if ($user->tenant_id && (int) $user->tenant_id !== (int) $tenant->id && ! $user->isSuperAdmin()) {
            throw ValidationException::withMessages([
                'email' => __('Dieser Benutzer ist einem anderen Mandanten zugeordnet.'),
            ]);
        }

        $user->tenant_id = $tenant->id;

        if ($role === 'tenant_admin' && ! $user->isSuperAdmin()) {
            $user->role = 'tenant_admin';
        } elseif (! in_array($user->role, ['tenant_admin', 'super_admin', 'superadmin'], true)) {
            $user->role = 'member';
        }

        if ($name) {
            $user->name = $name;
            [$first, $last] = $this->splitName($name);
            $user->first_name = $first;
            $user->last_name = $last;
        }

        if (! $user->exists) {
            $user->email_verified_at = null;
        }

        $user->save();

        return $user;
    }

    private function splitName(string $name): array
    {
        $parts = preg_split('/\s+/', trim($name));
        $first = Arr::first($parts) ?? $name;
        $last = count($parts) > 1 ? Arr::last($parts) : null;

        return [$first, $last];
    }

    private function ensureInviteToken(Event $event, string $email): string
    {
        $existing = EventMember::query()
            ->where('event_id', $event->id)
            ->where('email', $email)
            ->value('invite_token');

        return $existing ?? Str::random(40);
    }

    private function defaultPermissionsForRole(string $role): array
    {
        if ($role === 'tenant_admin') {
            return ['*'];
        }

        return [
            'photos:moderate',
            'tasks:manage',
            'join-tokens:manage',
        ];
    }
}
