<?php

declare(strict_types=1);

namespace Builder\Core\Extensions\Account;

use Builder\Core\Foundation\Account\AccountProfileDefinition;
use Builder\Core\Foundation\Definition\Field;
use Builder\Core\Foundation\Definition\FieldBuilder;
use Builder\Core\Foundation\Definition\Hook;
use Builder\Core\Foundation\Definition\Section;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;

final class AccountProfile implements AccountProfileDefinition
{
    public function fields(): FieldBuilder
    {
        return FieldBuilder::make()
            ->add(
                Section::make('profile')
                    ->label((string) __('builder::builder.account.profile'))
                    ->priority(10)
                    ->fields([
                        Field::text('name')->label((string) __('builder::builder.account.name')),
                        Field::email('email')->label((string) __('builder::builder.account.email')),
                        Field::select('locale')
                            ->label((string) __('builder::builder.account.language'))
                            ->options(config('builder.locales', ['en' => 'English', 'nl' => 'Nederlands']))
                            ->default('en'),
                    ]),
                Section::make('password')
                    ->label((string) __('builder::builder.account.password'))
                    ->priority(20)
                    ->fields([
                        Field::password('current_password')->label((string) __('builder::builder.account.current_password')),
                        Field::password('password')
                            ->label((string) __('builder::builder.account.new_password'))
                            ->hint((string) __('builder::builder.account.new_password_hint')),
                        Field::password('password_confirmation')->label((string) __('builder::builder.account.confirm_password')),
                    ]),
                Section::make('security')
                    ->label((string) __('builder::builder.account.security'))
                    ->priority(30)
                    ->fields([
                        Field::make('mfa_settings')
                            ->component('MfaSettings')
                            ->label((string) __('builder::builder.mfa.title')),
                    ]),
            );
    }

    /** @return array<string, mixed> */
    public function rules(): array
    {
        $userId = auth()->id();
        /** @var class-string<Model> $model */
        $model = config('auth.providers.users.model', User::class);
        $table = (new $model)->getTable();

        return [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'email', 'max:255', Rule::unique($table, 'email')->ignore($userId)],
            'locale' => ['nullable', 'string', Rule::in(array_keys(config('builder.locales', ['en' => 'English', 'nl' => 'Nederlands'])))],
            'current_password' => ['nullable', 'string'],
            'password' => ['nullable', 'string', 'min:8', 'confirmed'],
            'password_confirmation' => ['nullable', 'string'],
        ];
    }

    /** @return list<Hook> */
    public function hooks(): array
    {
        return [
            /**
             * Hash new password if provided, and verify current password.
             * Email + password changes both require current_password proof so a
             * hijacked session cannot pivot into a full account takeover.
             * Also persist locale directly to bypass $fillable restrictions.
             */
            Hook::on('saving', function (User $user, array $data): void {
                if (array_key_exists('locale', $data)) {
                    $user->setAttribute('locale', $data['locale'] ?? null);
                }

                $emailChanged = array_key_exists('email', $data)
                    && (string) $data['email'] !== (string) $user->getOriginal('email');

                $passwordChanging = ! empty($data['password']);

                if ($emailChanged || $passwordChanging) {
                    $currentPassword = (string) ($user->getAttribute('password') ?? '');

                    if (empty($data['current_password']) || ! Hash::check((string) $data['current_password'], $currentPassword)) {
                        throw ValidationException::withMessages([
                            'current_password' => [(string) __('builder::builder.validation.current_password_incorrect')],
                        ]);
                    }
                }

                if ($passwordChanging) {
                    $user->setAttribute('password', Hash::make($data['password']));
                }
            }),
        ];
    }
}
