<?php

namespace App\Filament\Pages\Auth;

use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;
use Filament\Auth\Pages\PasswordReset\RequestPasswordReset as BaseRequestPasswordReset;
use Filament\Facades\Filament;
use Filament\Forms\Components\TextInput;
use Filament\Notifications\Notification;
use Filament\Schemas\Schema;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Illuminate\Validation\ValidationException;
use Throwable;

class RequestPasswordReset extends BaseRequestPasswordReset
{
    protected static ?string $title = 'Forgot Password';
    protected static ?string $navigationLabel = 'Forgot Password';

    public function form(Schema $schema): Schema
    {
        return $schema
            ->components([
                TextInput::make('identity')
                    ->label('Email or Username')
                    ->required()
                    ->autocomplete()
                    ->autofocus(),
            ]);
    }

    public function request(): void
    {
        try {
            $this->rateLimit(2);
        } catch (TooManyRequestsException $exception) {
            $this->getRateLimitedNotification($exception)?->send();

            return;
        }

        $data = $this->form->getState();
        $identity = trim($data['identity'] ?? '');

        if ($identity === '') {
            throw ValidationException::withMessages([
                'data.identity' => 'Please enter your username or email.',
            ]);
        }

        $provider = Filament::auth()->getProvider(); /** @phpstan-ignore-line */
        $model = $provider->getModel();

        $user = $model::query()
            ->where('username', $identity)
            ->orWhere('email', $identity)
            ->first();

        if (! $user) {
            Notification::make()
                ->danger()
                ->title('No user found with these credentials.')
                ->send();

            return;
        }

        if (blank($user->email)) {
            Notification::make()
                ->danger()
                ->title('No email registered for this account.')
                ->body('Please complete your email in the "Edit User Profile" section inside the panel.')
                ->send();

            return;
        }

        $panelId = Filament::getCurrentPanel()?->getId() ?? '';
        if ($panelId !== 'admin') {
            Notification::make()
                ->danger()
                ->title('Password reset is only available for admins.')
                ->send();

            return;
        }

        $panelLabel = 'Admin Panel';
        $userType = 'admin';
        $code = str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
        $expiresMinutes = 10;

        // Remove previous pending tokens for this user.
        \App\Models\PasswordResetToken::where('user_type', $userType)
            ->where('user_id', $user->getKey())
            ->whereNull('used_at')
            ->delete();

        $token = \App\Models\PasswordResetToken::create([
            'user_type' => $userType,
            'user_id' => $user->getKey(),
            'email' => $user->email,
            'code' => $code,
            'code_hash' => Hash::make($code),
            'expires_at' => now()->addMinutes($expiresMinutes),
        ]);

        try {
            Mail::to($user->email)->send(new \App\Mail\PasswordResetCodeMail(
                name: $user->name ?? $user->username,
                username: $user->username,
                panelLabel: $panelLabel,
                code: $code,
                expiresMinutes: $expiresMinutes,
            ));
        } catch (Throwable $exception) {
            report($exception);

            Notification::make()
                ->danger()
                ->title('Email sending failed.')
                ->body('Please try again or contact support.')
                ->send();

            return;
        }

        Notification::make()
            ->success()
            ->title('Verification code sent.')
            ->body('We emailed a 6-digit code. Please enter it to continue.')
            ->send();

        $resetUrl = Filament::getResetPasswordUrl($token->id, $user);
        $this->redirect($resetUrl, navigate: false);
    }
}
