<?php

declare(strict_types=1);

namespace ImovelZapAi\Authentication\Application\Actions;

use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use ImovelZapAi\Authentication\Domain\Enums\SystemRole;
use ImovelZapAi\Organizations\Application\Actions\SetOrganizationOwnerAction;
use ImovelZapAi\Tenancy\Application\Actions\CreateTenantAction;
use ImovelZapAi\Tenancy\Domain\Enums\OrganizationType;
use ImovelZapAi\Tenancy\Support\TenantContext;
use ImovelZapAi\Users\Application\Actions\CreateUserAction;

final class RegisterAccountAction
{
    public function __construct(
        private readonly CreateTenantAction $createTenant,
        private readonly CreateUserAction $createUser,
        private readonly SetOrganizationOwnerAction $setOwner,
    ) {}

    /**
     * @return array{user: User, tenant_id: string}
     */
    public function execute(
        string $accountType,
        string $name,
        string $email,
        string $password,
        ?string $organizationName = null,
        bool $loginAfter = true,
    ): array {
        $type = match (strtolower(trim($accountType))) {
            'corretor', 'solo' => OrganizationType::Solo,
            'imobiliaria', 'agency' => OrganizationType::Agency,
            default => throw ValidationException::withMessages([
                'account_type' => ['Escolha Corretor ou Imobiliária.'],
            ]),
        };

        $email = strtolower(trim($email));

        if (User::query()->whereRaw('lower(email) = ?', [$email])->exists()) {
            throw ValidationException::withMessages([
                'email' => ['Este e-mail já possui uma conta. Faça login.'],
            ]);
        }

        $orgName = trim((string) ($organizationName ?: $name));
        if ($orgName === '') {
            throw ValidationException::withMessages([
                'organization_name' => ['Informe o nome da organização.'],
            ]);
        }

        if ($type === OrganizationType::Agency && blank($organizationName)) {
            throw ValidationException::withMessages([
                'organization_name' => ['Informe o nome da imobiliária.'],
            ]);
        }

        $slug = $this->uniqueSlug($orgName);

        /** @var User $user */
        $user = DB::transaction(function () use ($type, $orgName, $slug, $name, $email, $password): User {
            $tenant = $this->createTenant->execute($orgName, $slug, $type);

            $user = $this->createUser->execute(
                tenantId: (string) $tenant->id,
                name: $name,
                email: $email,
                password: $password,
                role: SystemRole::Admin,
                enforcePlanLimits: false,
                markEmailVerified: false,
            );

            $this->setOwner->execute((string) $tenant->id, (string) $user->id);

            return $user;
        });

        $user->sendEmailVerificationNotification();

        if ($loginAfter) {
            TenantContext::set((string) $user->tenant_id);
            Auth::guard('web')->login($user);
        }

        return [
            'user' => $user,
            'tenant_id' => (string) $user->tenant_id,
        ];
    }

    private function uniqueSlug(string $name): string
    {
        $base = Str::slug($name);
        if ($base === '') {
            $base = 'conta';
        }

        $slug = $base;
        $i = 1;
        while (\ImovelZapAi\Tenancy\Infrastructure\Persistence\Eloquent\TenantModel::query()->where('slug', $slug)->exists()) {
            $slug = $base.'-'.$i;
            $i++;
        }

        return $slug;
    }
}
