<?php

use App\Domains\Webhook\Models\Webhook;
use App\Domains\Workspace\Models\Workspace;
use App\Domains\Workspace\Models\WorkspaceInvitation;
use App\Domains\Workspace\Models\WorkspaceMember;
use App\Livewire\Modals\ConfirmAction;
use App\Livewire\Modals\CreateApiToken;
use App\Livewire\Modals\CreateWebhook;
use App\Livewire\Modals\CreateWorkspace;
use App\Livewire\Modals\DeleteWebhook;
use App\Livewire\Modals\DeleteWorkspace;
use App\Livewire\Modals\EditMemberRole;
use App\Livewire\Modals\InviteMember;
use App\Livewire\Modals\RemoveMember;
use App\Livewire\Modals\RevokeApiToken;
use App\Models\User;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Laravel\Sanctum\PersonalAccessToken;
use Livewire\Livewire;

function makeWsUser(): array
{
    $user = User::factory()->create();
    $ws = Workspace::factory()->create(['owner_id' => $user->id]);
    WorkspaceMember::create([
        'workspace_id' => $ws->id,
        'user_id' => $user->id,
        'role' => 'owner',
        'joined_at' => now(),
    ]);

    return [$user, $ws];
}

// ConfirmAction

it('renders confirm modal', function () {
    [$user] = makeWsUser();

    Livewire::actingAs($user)->test(ConfirmAction::class, [
        'title' => 'Are you sure?',
        'message' => 'This cannot be undone.',
        'confirmEvent' => 'testConfirmed',
    ])->assertSee('Are you sure?');
});

it('dispatches confirmEvent on confirm', function () {
    [$user] = makeWsUser();

    Livewire::actingAs($user)->test(ConfirmAction::class, [
        'confirmEvent' => 'testConfirmed',
    ])->call('confirm')
        ->assertDispatched('testConfirmed');
});

// InviteMember

it('renders invite member modal', function () {
    [$user, $ws] = makeWsUser();
    app()->instance('current_workspace', $ws);

    Livewire::actingAs($user)->test(InviteMember::class)
        ->assertSee('Invite Team Member');
});

it('validates email on invite member', function () {
    [$user, $ws] = makeWsUser();
    app()->instance('current_workspace', $ws);

    Livewire::actingAs($user)->test(InviteMember::class)
        ->set('email', 'not-an-email')
        ->call('invite')
        ->assertHasErrors(['email']);
});

it('creates invitation and dispatches memberInvited', function () {
    [$user, $ws] = makeWsUser();
    app()->instance('current_workspace', $ws);

    Livewire::actingAs($user)->test(InviteMember::class)
        ->set('email', 'new@example.com')
        ->set('role', 'editor')
        ->call('invite')
        ->assertDispatched('memberInvited');

    expect(WorkspaceInvitation::where('workspace_id', $ws->id)->where('email', 'new@example.com')->exists())->toBeTrue();
});

// EditMemberRole

it('renders edit member role and updates role', function () {
    [$owner, $ws] = makeWsUser();
    $member = User::factory()->create();
    $membership = WorkspaceMember::create([
        'workspace_id' => $ws->id,
        'user_id' => $member->id,
        'role' => 'editor',
        'joined_at' => now(),
    ]);
    app()->instance('current_workspace', $ws);

    Livewire::actingAs($owner)->test(EditMemberRole::class, ['memberId' => $membership->id])
        ->assertSee('Change Role')
        ->set('role', 'admin')
        ->call('save')
        ->assertDispatched('memberRoleUpdated');

    expect($membership->fresh()->role)->toBe('admin');
});

it('rejects edit member role for wrong workspace', function () {
    [$owner, $ws] = makeWsUser();
    app()->instance('current_workspace', $ws);

    $otherOwner = User::factory()->create();
    $otherWs = Workspace::factory()->create(['owner_id' => $otherOwner->id]);
    $membership = WorkspaceMember::create([
        'workspace_id' => $otherWs->id,
        'user_id' => $otherOwner->id,
        'role' => 'owner',
        'joined_at' => now(),
    ]);

    expect(fn () => Livewire::actingAs($owner)->test(EditMemberRole::class, ['memberId' => $membership->id]))
        ->toThrow(ModelNotFoundException::class);
});

// RemoveMember

it('removes member and dispatches memberRemoved', function () {
    [$owner, $ws] = makeWsUser();
    $member = User::factory()->create();
    $membership = WorkspaceMember::create([
        'workspace_id' => $ws->id,
        'user_id' => $member->id,
        'role' => 'editor',
        'joined_at' => now(),
    ]);
    app()->instance('current_workspace', $ws);

    Livewire::actingAs($owner)->test(RemoveMember::class, ['memberId' => $membership->id])
        ->call('remove')
        ->assertDispatched('memberRemoved');

    expect(WorkspaceMember::find($membership->id))->toBeNull();
});

// CreateApiToken

it('renders create api token modal', function () {
    [$user] = makeWsUser();

    Livewire::actingAs($user)->test(CreateApiToken::class)
        ->assertSee('Create API Token');
});

it('creates token and shows it once', function () {
    [$user] = makeWsUser();

    $component = Livewire::actingAs($user)->test(CreateApiToken::class)
        ->set('name', 'Test token')
        ->call('create')
        ->assertDispatched('apiTokenCreated');

    expect($component->get('plainTextToken'))->not->toBeNull();
});

it('validates token name is required', function () {
    [$user] = makeWsUser();

    Livewire::actingAs($user)->test(CreateApiToken::class)
        ->call('create')
        ->assertHasErrors(['name' => 'required']);
});

// RevokeApiToken

it('revokes token and dispatches apiTokenRevoked', function () {
    [$user] = makeWsUser();
    $token = $user->createToken('My token');
    $pat = $token->accessToken;

    Livewire::actingAs($user)->test(RevokeApiToken::class, ['tokenId' => $pat->id])
        ->call('revoke')
        ->assertDispatched('apiTokenRevoked');

    expect(PersonalAccessToken::find($pat->id))->toBeNull();
});

// CreateWebhook

it('creates webhook and dispatches webhookCreated', function () {
    [$user, $ws] = makeWsUser();
    app()->instance('current_workspace', $ws);

    Livewire::actingAs($user)->test(CreateWebhook::class)
        ->set('url', 'https://example.com/hook')
        ->set('selectedEvents', ['link.clicked'])
        ->call('save')
        ->assertDispatched('webhookCreated');

    expect(Webhook::where('workspace_id', $ws->id)->where('url', 'https://example.com/hook')->exists())->toBeTrue();
});

it('validates url and events on create webhook', function () {
    [$user, $ws] = makeWsUser();
    app()->instance('current_workspace', $ws);

    Livewire::actingAs($user)->test(CreateWebhook::class)
        ->call('save')
        ->assertHasErrors(['url', 'selectedEvents']);
});

// DeleteWebhook

it('deletes webhook and dispatches webhookDeleted', function () {
    [$user, $ws] = makeWsUser();
    app()->instance('current_workspace', $ws);

    $webhook = Webhook::factory()->create(['workspace_id' => $ws->id]);

    Livewire::actingAs($user)->test(DeleteWebhook::class, ['webhookId' => $webhook->id])
        ->call('delete')
        ->assertDispatched('webhookDeleted');

    expect(Webhook::find($webhook->id))->toBeNull();
});

// CreateWorkspace

it('creates workspace and dispatches workspaceCreated', function () {
    [$user] = makeWsUser();

    Livewire::actingAs($user)->test(CreateWorkspace::class)
        ->set('name', 'New Workspace')
        ->call('create')
        ->assertDispatched('workspaceCreated');

    expect(Workspace::where('owner_id', $user->id)->where('name', 'New Workspace')->exists())->toBeTrue();
});

it('validates workspace name is required', function () {
    [$user] = makeWsUser();

    Livewire::actingAs($user)->test(CreateWorkspace::class)
        ->call('create')
        ->assertHasErrors(['name' => 'required']);
});

// DeleteWorkspace

it('requires confirmation to delete workspace', function () {
    [$user, $ws] = makeWsUser();

    Livewire::actingAs($user)->test(DeleteWorkspace::class, ['workspaceUlid' => $ws->ulid])
        ->set('confirmation', 'Wrong name')
        ->call('delete')
        ->assertHasErrors(['confirmation']);

    expect(Workspace::find($ws->id))->not->toBeNull();
});

it('soft-deletes workspace with correct confirmation', function () {
    [$user, $ws] = makeWsUser();

    Livewire::actingAs($user)->test(DeleteWorkspace::class, ['workspaceUlid' => $ws->ulid])
        ->set('confirmation', $ws->name)
        ->call('delete')
        ->assertDispatched('workspaceDeleted');

    expect(Workspace::withTrashed()->find($ws->id)->deleted_at)->not->toBeNull();
});
