<?php

declare(strict_types=1);

use App\Models\AuditLog;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Models\Workspace;
use App\Models\WorkspaceMembership;
use App\Filament\Pages\EnvironmentDashboard;
use App\Support\Audit\AuditActionId;
use App\Support\Workspaces\WorkspaceContext;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

// --- T005: it_skips_chooser_when_single_workspace_membership ---

it('skips chooser when single workspace membership', function (): void {
    $user = User::factory()->create();
    $workspace = Workspace::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspace->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'owner',
    ]);

    $response = $this->actingAs($user)->get('/admin/_test/workspace-context');

    // Should redirect via tenant branching (not to chooser).
    $response->assertRedirect();

    $location = $response->headers->get('Location');
    expect($location)->not->toContain('choose-workspace');
});

// --- T006: it_emits_audit_event_on_auto_selection_single_membership ---

it('emits audit event on auto selection single membership', function (): void {
    $user = User::factory()->create();
    $workspace = Workspace::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspace->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'owner',
    ]);

    $this->actingAs($user)->get('/admin/_test/workspace-context');

    $auditLog = AuditLog::query()
        ->where('action', AuditActionId::WorkspaceAutoSelected->value)
        ->where('workspace_id', $workspace->getKey())
        ->first();

    expect($auditLog)->not->toBeNull();
    expect($auditLog->metadata)->toMatchArray([
        'method' => 'auto',
        'reason' => 'single_membership',
    ]);
    expect($auditLog->resource_type)->toBe('workspace');
    expect($auditLog->resource_id)->toBe((string) $workspace->getKey());
});

// --- T007: it_redirects_via_tenant_count_branching_after_single_auto_resume ---

it('redirects to managed tenants index when single workspace has zero tenants', function (): void {
    $user = User::factory()->create();
    $workspace = Workspace::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspace->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'owner',
    ]);

    $response = $this->actingAs($user)->get('/admin/_test/workspace-context');

    $expectedRoute = route('admin.workspace.managed-environments.index', [
        'workspace' => $workspace->slug ?? $workspace->getKey(),
    ]);

    $response->assertRedirect($expectedRoute);
});

it('redirects to managed tenants index when single workspace only has non-operable tenants', function (): void {
    $user = User::factory()->create();
    $workspace = Workspace::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspace->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'owner',
    ]);

    $onboardingTenant = ManagedEnvironment::factory()->onboarding()->create([
        'workspace_id' => $workspace->getKey(),
    ]);
    $archivedTenant = ManagedEnvironment::factory()->archived()->create([
        'workspace_id' => $workspace->getKey(),
    ]);

    $user->tenants()->syncWithoutDetaching([
        $onboardingTenant->getKey() => ['role' => 'owner'],
        $archivedTenant->getKey() => ['role' => 'owner'],
    ]);

    $response = $this->actingAs($user)->get('/admin/_test/workspace-context');

    $expectedRoute = route('admin.workspace.managed-environments.index', [
        'workspace' => $workspace->slug ?? $workspace->getKey(),
    ]);

    $response->assertRedirect($expectedRoute);
});

it('redirects to tenant dashboard when single workspace has one active tenant', function (): void {
    $user = User::factory()->create();
    $workspace = Workspace::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspace->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'owner',
    ]);

    $tenant = ManagedEnvironment::factory()->create([
        'workspace_id' => $workspace->getKey(),
        'status' => 'active',
    ]);

    $user->tenants()->syncWithoutDetaching([
        $tenant->getKey() => ['role' => 'owner'],
    ]);

    $response = $this->actingAs($user)->get('/admin/_test/workspace-context');

    $response->assertRedirect();
    $location = $response->headers->get('Location');
    expect($location)->toBe(EnvironmentDashboard::getUrl(tenant: $tenant));
});

// --- T008: it_allows_request_when_session_workspace_is_valid ---

it('allows request when session workspace is valid', function (): void {
    $user = User::factory()->create();
    $workspace = Workspace::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspace->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'owner',
    ]);

    $response = $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
        ->get('/admin/_test/workspace-context');

    // Should pass through (200) since session is valid.
    $response->assertOk();
    $response->assertJson(['workspace_id' => (int) $workspace->getKey()]);
});

// --- T010: it_auto_resumes_to_last_used_workspace_when_membership_valid ---

it('auto resumes to last used workspace when membership valid', function (): void {
    $user = User::factory()->create();
    $workspaceA = Workspace::factory()->create();
    $workspaceB = Workspace::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspaceA->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'owner',
    ]);

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspaceB->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'operator',
    ]);

    // Set last_workspace_id to workspaceB.
    $user->forceFill(['last_workspace_id' => (int) $workspaceB->getKey()])->save();

    $response = $this->actingAs($user)->get('/admin/_test/workspace-context');

    // Should redirect via tenant branching (not to chooser).
    $response->assertRedirect();
    $location = $response->headers->get('Location');
    expect($location)->not->toContain('choose-workspace');
});

// --- T011: it_emits_audit_event_on_auto_selection_last_used ---

it('emits audit event on auto selection last used', function (): void {
    $user = User::factory()->create();
    $workspaceA = Workspace::factory()->create();
    $workspaceB = Workspace::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspaceA->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'owner',
    ]);

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspaceB->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'operator',
    ]);

    $user->forceFill(['last_workspace_id' => (int) $workspaceB->getKey()])->save();

    $this->actingAs($user)->get('/admin/_test/workspace-context');

    $auditLog = AuditLog::query()
        ->where('action', AuditActionId::WorkspaceAutoSelected->value)
        ->where('workspace_id', $workspaceB->getKey())
        ->first();

    expect($auditLog)->not->toBeNull();
    expect($auditLog->metadata)->toMatchArray([
        'method' => 'auto',
        'reason' => 'last_used',
    ]);
});

// --- T012: it_falls_back_to_chooser_when_multiple_workspaces_and_no_last_used ---

it('falls back to chooser when multiple workspaces and no last used', function (): void {
    $user = User::factory()->create();
    $user->forceFill(['last_workspace_id' => null])->save();
    $workspaceA = Workspace::factory()->create();
    $workspaceB = Workspace::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspaceA->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'owner',
    ]);

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspaceB->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'operator',
    ]);

    $response = $this->actingAs($user)->get('/admin/_test/workspace-context');

    $response->assertRedirect('/admin/choose-workspace');
});

it('redirects to the chooser when switching is explicitly requested with choose=1', function (): void {
    $user = User::factory()->create();
    $workspaceA = Workspace::factory()->create();
    $workspaceB = Workspace::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspaceA->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'owner',
    ]);

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspaceB->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'operator',
    ]);

    $response = $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspaceA->getKey()])
        ->get('/admin/_test/workspace-context?choose=1');

    $response->assertRedirect('/admin/choose-workspace');
});

// --- T023: it_clears_session_when_active_workspace_membership_revoked ---

it('clears session when active workspace membership revoked', function (): void {
    $user = User::factory()->create();
    $workspace = Workspace::factory()->create();

    // Set session but don't create membership — simulates revoked access.
    $response = $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
        ->get('/admin/_test/workspace-context');

    // Active workspace IDs that no longer belong to the user are denied as not found.
    $response->assertNotFound();
});

// --- T024: it_redirects_to_chooser_when_last_workspace_membership_revoked_and_shows_warning ---

it('redirects to chooser when last workspace membership revoked', function (): void {
    $user = User::factory()->create();
    $workspaceA = Workspace::factory()->create();
    $workspaceB = Workspace::factory()->create();
    $workspaceC = Workspace::factory()->create();

    // User is member of A and C but NOT B. last_workspace_id points to B.
    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspaceA->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'owner',
    ]);

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspaceC->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'operator',
    ]);

    $user->forceFill(['last_workspace_id' => (int) $workspaceB->getKey()])->save();

    $response = $this->actingAs($user)->get('/admin/_test/workspace-context');

    // last_workspace_id should be cleared.
    $user->refresh();
    expect($user->last_workspace_id)->toBeNull();

    // Should redirect to chooser since user has 2 valid workspaces and last_workspace was invalid.
    $response->assertRedirect('/admin/choose-workspace');
});

it('redirects to chooser when last workspace is archived', function (): void {
    $user = User::factory()->create();
    $workspaceA = Workspace::factory()->create();
    $workspaceB = Workspace::factory()->create(['archived_at' => now()]);

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspaceA->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'owner',
    ]);

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspaceB->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'operator',
    ]);

    $user->forceFill(['last_workspace_id' => (int) $workspaceB->getKey()])->save();

    $response = $this->actingAs($user)->get('/admin/_test/workspace-context');

    // Step 5 auto-resumes to workspaceA (only selectable). setCurrentWorkspace updates last_workspace_id.
    $user->refresh();
    expect($user->last_workspace_id)->toBe((int) $workspaceA->getKey());

    // Only workspaceA is selectable → single membership auto-resume.
    $response->assertRedirect();
    $location = $response->headers->get('Location');
    expect($location)->not->toContain('choose-workspace');
});

// --- T025: it_handles_archived_workspace_in_session ---

it('handles archived workspace in session', function (): void {
    $user = User::factory()->create();
    $workspace = Workspace::factory()->create(['archived_at' => now()]);

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspace->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'owner',
    ]);

    $response = $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
        ->get('/admin/_test/workspace-context');

    // Session with archived workspace should be treated as stale.
    $response->assertRedirect();
    $location = $response->headers->get('Location');
    // Should redirect to chooser or no-access.
    expect($location)->toMatch('/choose-workspace|no-access/');
});

// --- T030: it_forces_chooser_with_choose_param ---

it('forces chooser with choose param', function (): void {
    $user = User::factory()->create();
    $workspace = Workspace::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspace->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'owner',
    ]);

    $response = $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
        ->get('/admin/_test/workspace-context?choose=1');

    $response->assertRedirect('/admin/choose-workspace');
});

it('forces chooser with choose param even when single workspace', function (): void {
    $user = User::factory()->create();
    $workspace = Workspace::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => $workspace->getKey(),
        'user_id' => $user->getKey(),
        'role' => 'owner',
    ]);

    // No session set — normally would auto-resume, but ?choose=1 forces chooser.
    $response = $this->actingAs($user)->get('/admin/_test/workspace-context?choose=1');

    $response->assertRedirect('/admin/choose-workspace');
});
