<?php

namespace App\Http\Requests\Auth;

use App\Models\Company;
use App\Models\User;
use Illuminate\Auth\Events\Lockout;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;

class LoginRequest extends FormRequest
{
    /**
     * Reserved company code that identifies a platform-wide Super Admin
     * login (users with company_id = null) rather than a real company —
     * no company can be created with this code (see CompanyRequest).
     */
    public const SUPER_ADMIN_CODE = '123';

    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, ValidationRule|array<mixed>|string>
     */
    public function rules(): array
    {
        return [
            'company_code' => ['required', 'string'],
            'username' => ['required', 'string'],
            'password' => ['required', 'string'],
        ];
    }

    /**
     * Attempt to authenticate the request's credentials.
     *
     * The company code is part of identity here, not just a filter: typing
     * the reserved code 123 means "log in as a platform-wide Super Admin"
     * (users with company_id = null), matching how every company-scoped
     * resource in this app already treats a null company_id. Any other code
     * must match a real, active company; a code that matches nothing fails
     * before the username/password are even checked, so a wrong code can't
     * be used to probe whether a username exists in a *different* company.
     *
     * @throws ValidationException
     */
    public function authenticate(): void
    {
        $this->ensureIsNotRateLimited();

        $code = $this->string('company_code')->trim()->toString();

        if ($code === self::SUPER_ADMIN_CODE) {
            $companyId = null;
        } else {
            $companyId = Company::where('code', $code)->where('is_active', true)->value('id');

            if (! $companyId) {
                RateLimiter::hit($this->throttleKey());

                throw ValidationException::withMessages([
                    'company_code' => 'That company code was not recognized.',
                ]);
            }
        }

        $user = User::where('username', $this->string('username'))
            ->where('status', 'active')
            ->when($companyId, fn ($q) => $q->where('company_id', $companyId), fn ($q) => $q->whereNull('company_id'))
            ->first();

        if (! $user || ! Hash::check($this->string('password'), $user->password)) {
            RateLimiter::hit($this->throttleKey());

            throw ValidationException::withMessages([
                'username' => trans('auth.failed'),
            ]);
        }

        Auth::login($user, $this->boolean('remember'));
        $user->forceFill(['last_login_at' => now()])->save();

        RateLimiter::clear($this->throttleKey());
    }

    /**
     * Ensure the login request is not rate limited.
     *
     * @throws ValidationException
     */
    public function ensureIsNotRateLimited(): void
    {
        if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
            return;
        }

        event(new Lockout($this));

        $seconds = RateLimiter::availableIn($this->throttleKey());

        throw ValidationException::withMessages([
            'username' => trans('auth.throttle', [
                'seconds' => $seconds,
                'minutes' => ceil($seconds / 60),
            ]),
        ]);
    }

    /**
     * Get the rate limiting throttle key for the request.
     */
    public function throttleKey(): string
    {
        return Str::transliterate(Str::lower($this->string('company_code').'|'.$this->string('username')).'|'.$this->ip());
    }
}
