<?php

namespace Database\Factories;

use App\Models\Application;
use App\Models\AuthenticationToken;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\AuthenticationToken>
 */
class AuthenticationTokenFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        return [
            'user_id' => User::factory(),
            'application_id' => Application::factory(),
            'token' => Str::random(64),
            'issued_at' => now(),
            'expires_at' => now()->addMonth(),
            'ip' => fake()->ipv4(),
            'user_agent' => fake()->userAgent(),
        ];
    }

    /**
     * Indicate that the token is expired.
     */
    public function expired(): static
    {
        return $this->state(fn (array $attributes) => [
            'expires_at' => now()->subDay(),
        ]);
    }

    /**
     * Indicate that the token expires soon.
     */
    public function expiringSoon(): static
    {
        return $this->state(fn (array $attributes) => [
            'expires_at' => now()->addHours(fake()->numberBetween(1, 24)),
        ]);
    }

    /**
     * Indicate that the token was issued recently.
     */
    public function recent(): static
    {
        return $this->state(fn (array $attributes) => [
            'issued_at' => now()->subMinutes(fake()->numberBetween(1, 60)),
        ]);
    }

    /**
     * Indicate that the token was issued long ago.
     */
    public function old(): static
    {
        return $this->state(fn (array $attributes) => [
            'issued_at' => now()->subDays(fake()->numberBetween(7, 30)),
        ]);
    }

    /**
     * Create a token for a specific user.
     */
    public function forUser(User $user): static
    {
        return $this->state(fn (array $attributes) => [
            'user_id' => $user->id,
        ]);
    }

    /**
     * Create a token for a specific application.
     */
    public function forApplication(Application $application): static
    {
        return $this->state(fn (array $attributes) => [
            'application_id' => $application->id,
        ]);
    }

    /**
     * Create a token with a specific token value for testing.
     */
    public function withToken(string $token): static
    {
        return $this->state(fn (array $attributes) => [
            'token' => $token,
        ]);
    }

    /**
     * Create a token with a mobile user agent.
     */
    public function mobile(): static
    {
        $mobileAgents = [
            'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1',
            'Mozilla/5.0 (Linux; Android 11; SM-G991B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.120 Mobile Safari/537.36',
            'Mozilla/5.0 (iPad; CPU OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1',
        ];

        return $this->state(fn (array $attributes) => [
            'user_agent' => fake()->randomElement($mobileAgents),
        ]);
    }

    /**
     * Create a token with a desktop user agent.
     */
    public function desktop(): static
    {
        $desktopAgents = [
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
        ];

        return $this->state(fn (array $attributes) => [
            'user_agent' => fake()->randomElement($desktopAgents),
        ]);
    }

    /**
     * Create a token from a local IP address.
     */
    public function localNetwork(): static
    {
        $localIPs = [
            fake()->localIpv4(),
            '192.168.1.' . fake()->numberBetween(2, 254),
            '10.0.0.' . fake()->numberBetween(2, 254),
            '172.16.0.' . fake()->numberBetween(2, 254),
        ];

        return $this->state(fn (array $attributes) => [
            'ip' => fake()->randomElement($localIPs),
        ]);
    }

    /**
     * Create a token that expires in a specific timeframe.
     */
    public function expiresIn(string $timeframe): static
    {
        return $this->state(fn (array $attributes) => [
            'expires_at' => now()->add($timeframe),
        ]);
    }
}