<?php

namespace App\Filament\Pages\Auth;

use App\Models\Admin;
use App\Models\PasswordResetToken;
use Filament\Facades\Filament;
use Filament\Notifications\Notification;
use Filament\Pages\SimplePage;
use Filament\Schemas\Components\Actions;
use Filament\Schemas\Components\Component;
use Filament\Schemas\Components\EmbeddedSchema;
use Filament\Schemas\Components\Form;
use Filament\Schemas\Schema;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;

class ResetPassword extends SimplePage
{
    public ?array $data = [];

    protected static bool $shouldRegisterNavigation = false;

    protected ?PasswordResetToken $tokenRecord = null;
    public ?string $tokenId = null;
    public string $stage = 'code'; // code | password

    public function defaultForm(Schema $schema): Schema
    {
        return $schema->statePath('data');
    }

    public function form(Schema $schema): Schema
    {
        return $schema->components(
            $this->stage === 'code'
                ? $this->codeSchema()
                : $this->passwordSchema()
        );
    }

    public function mount(): void
    {
        $tokenId = request()->query('token');
        $this->tokenId = $tokenId;

        $this->tokenRecord = $this->resolveToken();

        if (! $this->tokenRecord || $this->tokenRecord->used_at || $this->tokenRecord->isExpired()) {
            Notification::make()
                ->danger()
                ->title('This reset link is invalid or expired. Please request a new code.')
                ->send();

            $this->redirect(filament()->getRequestPasswordResetUrl(), navigate: false);

            return;
        }

        if ($this->tokenRecord->verified_at) {
            $this->stage = 'password';
        }
    }

    public function getTitle(): string|Htmlable
    {
        return $this->stage === 'code' ? 'Verify Code' : 'Set New Password';
    }

    public function content(Schema $schema): Schema
    {
        return $schema->components([
            $this->getFormContentComponent(),
        ]);
    }

    protected function getFormContentComponent(): Component
    {
        return Form::make([EmbeddedSchema::make('form')])
            ->id('form')
            ->livewireSubmitHandler($this->stage === 'code' ? 'verifyCode' : 'updatePassword')
            ->footer([
                Actions::make($this->stage === 'code' ? $this->codeActions() : $this->passwordActions())
                    ->alignment('start')
                    ->fullWidth(true),
            ]);
    }

    protected function codeSchema(): array
    {
        return [
            \Filament\Forms\Components\TextInput::make('code')
                ->label('Verification Code')
                ->required()
                ->length(6)
                ->rule('regex:/^[0-9]{6}$/')
                ->autocomplete('one-time-code')
                ->autofocus(),
        ];
    }

    protected function passwordSchema(): array
    {
        return [
            \Filament\Forms\Components\TextInput::make('password')
                ->label('New Password')
                ->password()
                ->revealable()
                ->required()
                ->minLength(8),
            \Filament\Forms\Components\TextInput::make('passwordConfirmation')
                ->label('Confirm New Password')
                ->password()
                ->revealable()
                ->same('password')
                ->required(),
        ];
    }

    protected function codeActions(): array
    {
        return [
            \Filament\Actions\Action::make('verify')
                ->label('Verify Code')
                ->submit('verifyCode'),
            \Filament\Actions\Action::make('back')
                ->label('Back to request')
                ->color('gray')
                ->url(Filament::getRequestPasswordResetUrl()),
        ];
    }

    protected function passwordActions(): array
    {
        return [
            \Filament\Actions\Action::make('update')
                ->label('Change Password')
                ->submit('updatePassword'),
        ];
    }

    public function verifyCode(): void
    {
        $this->ensureTokenIsValid();

        $data = $this->form->getState();

        $this->validateCode($data['code'] ?? '');

        $this->tokenRecord->forceFill([
            'verified_at' => now(),
        ])->save();

        $this->stage = 'password';
        $this->cachedSchemas = [];
        $this->data = [];

        Notification::make()
            ->success()
            ->title('Code verified. Please set a new password.')
            ->send();
    }

    public function updatePassword(): void
    {
        $this->ensureTokenIsValid();

        if (! $this->tokenRecord->verified_at) {
            throw ValidationException::withMessages([
                'data.code' => 'Please verify the code first.',
            ]);
        }

        $data = $this->form->getState();

        $this->validatePassword($data);

        $user = $this->resolveUser();

        if (! $user) {
            $this->redirectToRequestWithError('User not found. Please request a new code.');

            return;
        }

        $user->forceFill([
            'password' => Hash::make($data['password']),
        ])->save();

        $this->tokenRecord->forceFill([
            'used_at' => now(),
        ])->save();

        Notification::make()
            ->success()
            ->title('Password changed successfully.')
            ->send();

        $this->redirect(filament()->getLoginUrl(), navigate: false);
    }

    protected function validateCode(string $code): void
    {
        if ($code === '' || ! preg_match('/^[0-9]{6}$/', $code)) {
            throw ValidationException::withMessages([
                'data.code' => 'Enter the 6-digit code.',
            ]);
        }

        if ($this->tokenRecord->isExpired()) {
            $this->redirectToRequestWithError('This code has expired. Please request a new one.');
        }

        if (! Hash::check($code, $this->tokenRecord->code_hash)) {
            throw ValidationException::withMessages([
                'data.code' => 'Invalid code.',
            ]);
        }
    }

    protected function validatePassword(array $data): void
    {
        $password = $data['password'] ?? '';
        $confirm = $data['passwordConfirmation'] ?? '';

        if (strlen((string) $password) < 8) {
            throw ValidationException::withMessages([
                'data.password' => 'Password must be at least 8 characters.',
            ]);
        }

        if ($password !== $confirm) {
            throw ValidationException::withMessages([
                'data.passwordConfirmation' => 'Password confirmation does not match.',
            ]);
        }
    }

    protected function resolveUser(): Admin|null
    {
        if ($this->tokenRecord->user_type !== 'admin') {
            return null;
        }

        return Admin::find($this->tokenRecord->user_id);
    }

    protected function ensureTokenIsValid(): void
    {
        $this->tokenRecord = $this->resolveToken();

        if (! $this->tokenRecord || $this->tokenRecord->used_at) {
            throw ValidationException::withMessages([
                'data.code' => 'This reset link is invalid or expired. Please request a new code.',
            ]);
        }

        if ($this->tokenRecord->user_type !== 'admin') {
            throw ValidationException::withMessages([
                'data.code' => 'Password reset is only available for admins.',
            ]);
        }

        if ($this->tokenRecord->isExpired()) {
            throw ValidationException::withMessages([
                'data.code' => 'This code has expired. Please request a new one.',
            ]);
        }
    }

    protected function resolveToken(): ?PasswordResetToken
    {
        if ($this->tokenRecord) {
            return $this->tokenRecord;
        }

        if (! $this->tokenId) {
            return null;
        }

        return PasswordResetToken::find($this->tokenId);
    }
}
