<?php

use App\Models\Application;
use App\Models\AuthenticationToken;
use App\Models\Invitation;
use App\Models\User;

uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);

test('ApplicationFactory states work correctly', function () {
    // Test default factory
    $app = Application::factory()->create();
    expect($app->name)->not->toBeNull();
    expect($app->client_id)->not->toBeNull();
    expect($app->client_secret)->not->toBeNull();
    expect($app->redirect_uri)->not->toBeNull();
    expect($app->icon)->not->toBeNull();

    // Test withoutIcon state
    $appNoIcon = Application::factory()->withoutIcon()->create();
    expect($appNoIcon->icon)->toBeNull();

    // Test localhost state
    $appLocalhost = Application::factory()->localhost(3000)->create();
    expect($appLocalhost->redirect_uri)->toContain('localhost:3000');

    // Test secure state
    $appSecure = Application::factory()->secure()->create();
    expect($appSecure->redirect_uri)->toStartWith('https://');

    // Test specific app states
    $grafana = Application::factory()->grafana()->create();
    expect($grafana->name)->toBe('Grafana');
    expect($grafana->redirect_uri)->toContain('grafana.');

    $nextcloud = Application::factory()->nextcloud()->create();
    expect($nextcloud->name)->toBe('Nextcloud');
    expect($nextcloud->redirect_uri)->toContain('cloud.');

    // Test testApp state
    $testApp = Application::factory()->testApp()->create();
    expect($testApp->name)->toBe('Test Application');
    expect($testApp->client_id)->toBe('test-client-id');
    expect($testApp->client_secret)->toBe('test-client-secret');
    expect($testApp->icon)->toBeNull();
});

test('UserFactory states work correctly', function () {
    // Test default factory
    $user = User::factory()->create();
    expect($user->name)->not->toBeNull();
    expect($user->email)->not->toBeNull();
    expect($user->email_verified_at)->not->toBeNull();
    expect($user->is_admin)->toBe(false);
    expect($user->uuid)->not->toBeNull();

    // Test unverified state
    $unverifiedUser = User::factory()->unverified()->create();
    expect($unverifiedUser->email_verified_at)->toBeNull();

    // Test admin state
    $adminUser = User::factory()->admin()->create();
    expect($adminUser->is_admin)->toBe(true);

    // Test autoApprove state
    $autoApproveUser = User::factory()->autoApprove()->create();
    expect($autoApproveUser->auto_approve_apps)->toBe(true);

    // Test noAutoApprove state
    $noAutoApproveUser = User::factory()->noAutoApprove()->create();
    expect($noAutoApproveUser->auto_approve_apps)->toBe(false);

    // Test withPreferredUsername state
    $userWithUsername = User::factory()->withPreferredUsername('testuser')->create();
    expect($userWithUsername->preferred_username)->toBe('testuser');

    // Test withAvatar state
    $userWithAvatar = User::factory()->withAvatar()->create();
    expect($userWithAvatar->avatar)->not->toBeNull();
    expect($userWithAvatar->avatar)->toStartWith('avatars/');

    // Test complete state
    $completeUser = User::factory()->complete()->create();
    expect($completeUser->preferred_username)->not->toBeNull();
    expect($completeUser->avatar)->not->toBeNull();
    expect($completeUser->auto_approve_apps)->toBe(true);

    // Test withPassword state
    $userWithPassword = User::factory()->withPassword('test123')->create();
    expect(\Hash::check('test123', $userWithPassword->password))->toBe(true);
});

test('AuthenticationTokenFactory states work correctly', function () {
    $user = User::factory()->create();
    $app = Application::factory()->create();

    // Test default factory
    $token = AuthenticationToken::factory()->create();
    expect($token->token)->not->toBeNull();
    expect($token->issued_at)->not->toBeNull();
    expect($token->expires_at)->not->toBeNull();
    expect($token->ip)->not->toBeNull();
    expect($token->user_agent)->not->toBeNull();

    // Test expired state
    $expiredToken = AuthenticationToken::factory()->expired()->create();
    expect($expiredToken->expires_at)->toBeLessThan(now());

    // Test expiringSoon state
    $expiringSoonToken = AuthenticationToken::factory()->expiringSoon()->create();
    expect($expiringSoonToken->expires_at)->toBeGreaterThan(now());
    expect($expiredToken->expires_at)->toBeLessThan(now()->addDay());

    // Test forUser state
    $userToken = AuthenticationToken::factory()->forUser($user)->create();
    expect($userToken->user_id)->toBe($user->id);

    // Test forApplication state
    $appToken = AuthenticationToken::factory()->forApplication($app)->create();
    expect($appToken->application_id)->toBe($app->id);

    // Test withToken state
    $specificToken = AuthenticationToken::factory()->withToken('test-token')->create();
    expect($specificToken->token)->toBe('test-token');

    // Test mobile state
    $mobileToken = AuthenticationToken::factory()->mobile()->create();
    expect($mobileToken->user_agent)->toContain('Mobile');

    // Test desktop state
    $desktopToken = AuthenticationToken::factory()->desktop()->create();
    expect($desktopToken->user_agent)->not->toContain('Mobile');

    // Test expiresIn state
    $customExpiryToken = AuthenticationToken::factory()->expiresIn('2 weeks')->create();
    expect($customExpiryToken->expires_at)->toBeGreaterThan(now()->addDays(13));
});

test('InvitationFactory states work correctly', function () {
    $inviter = User::factory()->create();
    $acceptedUser = User::factory()->create();

    // Test default factory
    $invitation = Invitation::factory()->create();
    expect($invitation->code)->not->toBeNull();
    expect($invitation->email)->not->toBeNull();
    expect($invitation->invited_by)->not->toBeNull();
    expect($invitation->expires_at)->not->toBeNull();
    expect($invitation->accepted_at)->toBeNull();
    expect($invitation->user_id)->toBeNull();

    // Test expired state
    $expiredInvitation = Invitation::factory()->expired()->create();
    expect($expiredInvitation->expires_at)->toBeLessThan(now());

    // Test expiringSoon state
    $expiringSoonInvitation = Invitation::factory()->expiringSoon()->create();
    expect($expiringSoonInvitation->expires_at)->toBeGreaterThan(now());
    expect($expiringSoonInvitation->expires_at)->toBeLessThan(now()->addDay());

    // Test accepted state
    $acceptedInvitation = Invitation::factory()->accepted()->create();
    expect($acceptedInvitation->accepted_at)->not->toBeNull();
    expect($acceptedInvitation->user_id)->not->toBeNull();

    // Test acceptedBy state
    $userAcceptedInvitation = Invitation::factory()->acceptedBy($acceptedUser)->create();
    expect($userAcceptedInvitation->user_id)->toBe($acceptedUser->id);
    expect($userAcceptedInvitation->accepted_at)->not->toBeNull();

    // Test emailSent state
    $emailSentInvitation = Invitation::factory()->emailSent()->create();
    expect($emailSentInvitation->email_sent)->toBe(true);

    // Test emailNotSent state
    $emailNotSentInvitation = Invitation::factory()->emailNotSent()->create();
    expect($emailNotSentInvitation->email_sent)->toBe(false);

    // Test from state
    $fromInvitation = Invitation::factory()->from($inviter)->create();
    expect($fromInvitation->invited_by)->toBe($inviter->id);

    // Test forEmail state
    $emailInvitation = Invitation::factory()->forEmail('test@example.com')->create();
    expect($emailInvitation->email)->toBe('test@example.com');

    // Test withCode state
    $codeInvitation = Invitation::factory()->withCode('TEST123')->create();
    expect($codeInvitation->code)->toBe('TEST123');

    // Test pending state
    $pendingInvitation = Invitation::factory()->pending()->create();
    expect($pendingInvitation->accepted_at)->toBeNull();
    expect($pendingInvitation->user_id)->toBeNull();

    // Test expiresIn state
    $customExpiryInvitation = Invitation::factory()->expiresIn('2 weeks')->create();
    expect($customExpiryInvitation->expires_at)->toBeGreaterThan(now()->addDays(13));
});

test('Factory relationships work correctly', function () {
    // Test that tokens can be created with existing users and apps
    $user = User::factory()->admin()->create();
    $app = Application::factory()->grafana()->create();

    $token = AuthenticationToken::factory()
        ->forUser($user)
        ->forApplication($app)
        ->mobile()
        ->create();

    expect($token->user->id)->toBe($user->id);
    expect($token->application->id)->toBe($app->id);
    expect($token->user->is_admin)->toBe(true);
    expect($token->application->name)->toBe('Grafana');

    // Test that invitations can be created with existing users
    $inviter = User::factory()->admin()->create();
    $invitation = Invitation::factory()
        ->from($inviter)
        ->forEmail('new@example.com')
        ->emailSent()
        ->create();

    expect($invitation->creator->id)->toBe($inviter->id);
    expect($invitation->email)->toBe('new@example.com');
    expect($invitation->email_sent)->toBe(true);
});

test('Factory combinations work correctly', function () {
    // Test complex user states
    $complexUser = User::factory()
        ->admin()
        ->autoApprove()
        ->withPreferredUsername('admin')
        ->withAvatar()
        ->established()
        ->create();

    expect($complexUser->is_admin)->toBe(true);
    expect($complexUser->auto_approve_apps)->toBe(true);
    expect($complexUser->preferred_username)->toBe('admin');
    expect($complexUser->avatar)->not->toBeNull();
    expect($complexUser->created_at)->toBeLessThan(now()->subDays(29));

    // Test complex application states
    $complexApp = Application::factory()
        ->localhost(8080)
        ->withCustomIcon('https://example.com/icon.png')
        ->create();

    expect($complexApp->redirect_uri)->toContain('localhost:8080');
    expect($complexApp->icon)->toBe('https://example.com/icon.png');

    // Test complex token states
    $complexToken = AuthenticationToken::factory()
        ->forUser($complexUser)
        ->forApplication($complexApp)
        ->mobile()
        ->localNetwork()
        ->expiringSoon()
        ->create();

    expect($complexToken->user_id)->toBe($complexUser->id);
    expect($complexToken->application_id)->toBe($complexApp->id);
    expect($complexToken->user_agent)->toContain('Mobile');
    expect($complexToken->ip)->toMatch('/^(192\.168\.|10\.|172\.(1[6-9]|2[0-9]|3[01])\.)/');
    expect($complexToken->expires_at)->toBeLessThan(now()->addDay());

    // Test complex invitation states
    $complexInvitation = Invitation::factory()
        ->from($complexUser)
        ->forEmail('complex@example.com')
        ->emailSent()
        ->expiringSoon()
        ->recent()
        ->create();

    expect($complexInvitation->invited_by)->toBe($complexUser->id);
    expect($complexInvitation->email)->toBe('complex@example.com');
    expect($complexInvitation->email_sent)->toBe(true);
    expect($complexInvitation->expires_at)->toBeLessThan(now()->addDay());
    expect($complexInvitation->created_at)->toBeGreaterThan(now()->subHour());
});
