<?php

declare(strict_types=1);

namespace ImovelZapAi\Organizations\Application\Actions;

use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
use ImovelZapAi\Organizations\Application\Services\PlanLimitGuard;
use ImovelZapAi\Organizations\Domain\Enums\InvitationStatus;
use ImovelZapAi\Organizations\Infrastructure\Persistence\Eloquent\OrganizationInvitationModel;
use ImovelZapAi\Tenancy\Infrastructure\Persistence\Eloquent\TenantModel;
use ImovelZapAi\Users\Application\Actions\CreateUserAction;

final class AcceptInvitationAction
{
    public function __construct(
        private readonly CreateUserAction $createUser,
        private readonly PlanLimitGuard $planLimitGuard,
        private readonly EnsureOrganizationBootstrapAction $bootstrap,
    ) {}

    /**
     * @return array{user: User, invitation: OrganizationInvitationModel, tenant: TenantModel}
     */
    public function execute(string $token, string $name, string $password): array
    {
        /** @var OrganizationInvitationModel|null $invitation */
        $invitation = OrganizationInvitationModel::query()
            ->where('token', $token)
            ->first();

        if ($invitation === null || ! $invitation->isPending()) {
            throw ValidationException::withMessages([
                'token' => ['Convite inválido ou expirado.'],
            ]);
        }

        $tenantId = (string) $invitation->tenant_id;
        $this->planLimitGuard->assertCanAddUser($tenantId);
        $this->bootstrap->execute($tenantId);

        return DB::transaction(function () use ($invitation, $name, $password, $tenantId): array {
            $user = $this->createUser->execute(
                tenantId: $tenantId,
                name: $name,
                email: (string) $invitation->email,
                password: $password,
                role: $invitation->role,
                markEmailVerified: true,
            );

            $invitation->forceFill([
                'status' => InvitationStatus::Accepted,
                'accepted_at' => now(),
            ])->save();

            $tenant = TenantModel::query()->findOrFail($tenantId);

            if ($tenant->owner_user_id === null) {
                $tenant->forceFill(['owner_user_id' => $user->id])->save();
            }

            return [
                'user' => $user,
                'invitation' => $invitation->fresh() ?? $invitation,
                'tenant' => $tenant->fresh() ?? $tenant,
            ];
        });
    }
}
