<?php

namespace Database\Factories;

use App\Models\User;
use Database\Factories\Concerns\CopiesSeederImages;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;

/**
 * @extends Factory<User>
 */
class UserFactory extends Factory
{
    use CopiesSeederImages;

    /**
     * The hashed password shared by every seeded user.
     *
     * Cached as a pre-computed hash so the User model's "hashed" cast sees an
     * already-hashed value and skips running bcrypt on every factory create -
     * that per-row hash (~150ms each) was the dominant cost of seeding, since
     * nearly every seeder spins up a batch of demo users.
     */
    protected static ?string $password;

    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        $address = $this->randomAddress();

        return [
            'first_name' => fake()->firstName(),
            'last_name' => fake()->lastName(),
            'email' => fake()->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => static::$password ??= Hash::make('password'),
            'remember_token' => Str::random(10),
            'phone' => fake()->numerify('###-###-####'),
            'address_street' => $address['street'],
            'address_city' => $address['city'],
            'address_postal' => $address['postal'],
            'profile_picture' => null,
            'timezone' => 'America/Toronto',
            'two_factor_secret' => null,
            'two_factor_recovery_codes' => null,
            'two_factor_confirmed_at' => null,
            'is_admin' => false,
            'banned' => false,
            'receive_new_user_alerts' => true,
            'receive_new_adoption_alerts' => true,
            'receive_new_fostering_alerts' => true,
            'receive_draw_result_alerts' => true,
            'receive_note_alerts' => false,
            'note_alert_preference' => 'both',
        ];
    }

    /**
     * Indicate that the model's email address should be unverified.
     */
    public function unverified(): static
    {
        return $this->state(fn (array $attributes) => [
            'email_verified_at' => null,
        ]);
    }

    /**
     * Indicate that the model has two-factor authentication configured.
     */
    public function withTwoFactor(): static
    {
        return $this->state(fn (array $attributes) => [
            'two_factor_secret' => encrypt('test-secret'),
            'two_factor_recovery_codes' => encrypt(json_encode(['code1', 'code2'])),
            'two_factor_confirmed_at' => now(),
        ]);
    }

    /**
     * Indicate that the model does not have two-factor authentication configured.
     */
    public function withoutTwoFactor(): static
    {
        return $this->state(fn (array $attributes) => [
            'two_factor_secret' => null,
            'two_factor_recovery_codes' => null,
            'two_factor_confirmed_at' => null,
        ]);
    }

    /**
     * Indicate that the model is an admin user.
     */
    public function admin(): static
    {
        return $this->state(fn (array $attributes) => [
            'is_admin' => true,
        ]);
    }

    /**
     * Indicate that the admin receives notification alerts.
     */
    public function receivesNotifications(): static
    {
        return $this->state(fn (array $attributes) => [
            'receive_new_user_alerts' => true,
            'receive_new_adoption_alerts' => true,
            'receive_new_fostering_alerts' => true,
            'receive_draw_result_alerts' => true,
        ]);
    }

    /**
     * Indicate that the user has an incomplete profile (no phone or address).
     */
    public function incompleteProfile(): static
    {
        return $this->state(fn (array $attributes) => [
            'phone' => null,
            'address_street' => null,
            'address_city' => null,
            'address_postal' => null,
        ]);
    }

    /**
     * Indicate that the user has a complete profile (with phone and address).
     */
    public function withCompleteProfile(): static
    {
        return $this->state(function () {
            $address = $this->randomAddress();

            return [
                'phone' => fake()->numerify('###-###-####'),
                'address_street' => $address['street'],
                'address_city' => $address['city'],
                'address_postal' => $address['postal'],
            ];
        });
    }

    /**
     * @return array{street: string, city: string, postal: string}
     */
    private function randomAddress(): array
    {
        return fake()->randomElement([
            ['street' => '123 Main St', 'city' => 'Toronto', 'postal' => 'M5V 2T6'],
            ['street' => '456 Queen St W', 'city' => 'Toronto', 'postal' => 'M5V 2B3'],
            ['street' => '789 Yonge St', 'city' => 'Toronto', 'postal' => 'M4W 2G8'],
            ['street' => '321 King St W', 'city' => 'Toronto', 'postal' => 'M5H 1K2'],
            ['street' => '1000 Bay St', 'city' => 'Toronto', 'postal' => 'M5S 2A5'],
            ['street' => '250 Bloor St W', 'city' => 'Toronto', 'postal' => 'M5S 1V6'],
            ['street' => '88 College St', 'city' => 'Toronto', 'postal' => 'M5G 1L4'],
            ['street' => '500 Dundas St W', 'city' => 'Toronto', 'postal' => 'M5T 1H2'],
            ['street' => '12 Elm St', 'city' => 'Toronto', 'postal' => 'M5G 1G7'],
            ['street' => '200 Front St W', 'city' => 'Toronto', 'postal' => 'M5V 3K2'],
            ['street' => '100 Rideau St', 'city' => 'Ottawa', 'postal' => 'K1N 5X6'],
            ['street' => '350 Sparks St', 'city' => 'Ottawa', 'postal' => 'K1R 7S8'],
            ['street' => '222 Elgin St', 'city' => 'Ottawa', 'postal' => 'K2P 1L9'],
            ['street' => '75 Albert St', 'city' => 'Ottawa', 'postal' => 'K1P 5E7'],
            ['street' => '411 Laurier Ave W', 'city' => 'Ottawa', 'postal' => 'K1R 7Y3'],
            ['street' => '630 René-Lévesque Blvd W', 'city' => 'Montreal', 'postal' => 'H3B 1S6'],
            ['street' => '1000 Sherbrooke St W', 'city' => 'Montreal', 'postal' => 'H3A 1G4'],
            ['street' => '425 St-Catherine St W', 'city' => 'Montreal', 'postal' => 'H3B 1T7'],
            ['street' => '800 De Maisonneuve Blvd W', 'city' => 'Montreal', 'postal' => 'H3A 1L3'],
            ['street' => '3575 St Laurent Blvd', 'city' => 'Montreal', 'postal' => 'H2X 2T7'],
            ['street' => '789 Granville St', 'city' => 'Vancouver', 'postal' => 'V6Z 1K3'],
            ['street' => '1200 Burrard St', 'city' => 'Vancouver', 'postal' => 'V6Z 1Z6'],
            ['street' => '400 Robson St', 'city' => 'Vancouver', 'postal' => 'V6B 2B5'],
            ['street' => '625 Davie St', 'city' => 'Vancouver', 'postal' => 'V6B 2G3'],
            ['street' => '1050 Alberni St', 'city' => 'Vancouver', 'postal' => 'V6E 1A3'],
            ['street' => '905 King St', 'city' => 'Kingston', 'postal' => 'K7L 2H4'],
            ['street' => '200 Princess St', 'city' => 'Kingston', 'postal' => 'K7L 1B2'],
            ['street' => '75 Brock St', 'city' => 'Kingston', 'postal' => 'K7L 1S2'],
            ['street' => '340 Division St', 'city' => 'Kingston', 'postal' => 'K7K 4A9'],
            ['street' => '145 Ontario St', 'city' => 'Kingston', 'postal' => 'K7L 2Y7'],
            ['street' => '233 E 42nd St', 'city' => 'New York', 'postal' => '10017'],
            ['street' => '350 5th Ave', 'city' => 'New York', 'postal' => '10118'],
            ['street' => '100 Broadway', 'city' => 'New York', 'postal' => '10005'],
            ['street' => '425 Lexington Ave', 'city' => 'New York', 'postal' => '10017'],
            ['street' => '1600 Pennsylvania Ave NW', 'city' => 'Washington', 'postal' => '20500'],
            ['street' => '200 Massachusetts Ave NW', 'city' => 'Washington', 'postal' => '20001'],
            ['street' => '400 N Michigan Ave', 'city' => 'Chicago', 'postal' => '60611'],
            ['street' => '875 N Michigan Ave', 'city' => 'Chicago', 'postal' => '60611'],
            ['street' => '233 S Wacker Dr', 'city' => 'Chicago', 'postal' => '60606'],
            ['street' => '1 Infinite Loop', 'city' => 'Cupertino', 'postal' => '95014'],
            ['street' => '1600 Amphitheatre Pkwy', 'city' => 'Mountain View', 'postal' => '94043'],
            ['street' => '1 Microsoft Way', 'city' => 'Redmond', 'postal' => '98052'],
            ['street' => '410 Terry Ave N', 'city' => 'Seattle', 'postal' => '98109'],
            ['street' => '700 5th Ave', 'city' => 'Seattle', 'postal' => '98104'],
            ['street' => '100 Universal City Plaza', 'city' => 'Los Angeles', 'postal' => '91608'],
            ['street' => '6801 Hollywood Blvd', 'city' => 'Los Angeles', 'postal' => '90028'],
            ['street' => '1 Lincoln Memorial Cir NW', 'city' => 'Washington', 'postal' => '20037'],
            ['street' => '1060 W Addison St', 'city' => 'Chicago', 'postal' => '60613'],
            ['street' => '800 N Shoreline Blvd', 'city' => 'Mountain View', 'postal' => '94043'],
            ['street' => '123 Portage Ave', 'city' => 'Winnipeg', 'postal' => 'R3B 2E9'],
        ]);
    }

    /**
     * Indicate that the user is banned.
     */
    public function banned(): static
    {
        return $this->state(fn (array $attributes) => [
            'banned' => true,
        ]);
    }

    /**
     * Indicate that the user has a profile picture.
     */
    public function withProfilePicture(): static
    {
        return $this->state(fn (array $attributes) => [
            'profile_picture' => $this->copyRandomSeederImage('profile_samples', 'profile-pictures'),
        ]);
    }
}
