<?php

use App\Enums\Permission as PermissionEnum;
use App\Models\Category;
use App\Models\User;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Spatie\Activitylog\Models\Activity;

uses(RefreshDatabase::class);

beforeEach(function () {
    $this->seed(RolePermissionSeeder::class);
});

// ─── Helper ───────────────────────────────────────────────

function createActivityLogUserWithPermission(PermissionEnum ...$permissions): User
{
    $user = User::factory()->create();

    $user->givePermissionTo(
        array_merge(
            [PermissionEnum::DASHBOARD_VIEW->value],
            array_map(fn (PermissionEnum $p) => $p->value, $permissions)
        )
    );

    $user->forgetCachedPermissions();

    return $user;
}

function createActivityLogs(User $causer, int $count = 3): void
{
    for ($i = 0; $i < $count; $i++) {
        $category = Category::factory()->create(['name' => "Kategori {$i}"]);

        activity()
            ->causedBy($causer)
            ->performedOn($category)
            ->event('created')
            ->withProperties(['attributes' => ['name' => "Kategori {$i}"]])
            ->log("Kategori {$i} ditambahkan");
    }
}

// ─── Index ────────────────────────────────────────────────

describe('Activity Log Index', function () {
    test('authenticated user with permission can view activity log index', function () {
        $user = createActivityLogUserWithPermission(PermissionEnum::ACTIVITY_LOGS_VIEW);

        $this->actingAs($user)
            ->get(route('admin.system.activity_logs.index'))
            ->assertOk();
    });

    test('guest is redirected to login', function () {
        $this->get(route('admin.system.activity_logs.index'))
            ->assertRedirect(route('login'));
    });

    test('user without permission is forbidden', function () {
        $user = User::factory()->create();

        $this->actingAs($user)
            ->get(route('admin.system.activity_logs.index'))
            ->assertForbidden();
    });

    test('index displays activity logs', function () {
        $user = createActivityLogUserWithPermission(PermissionEnum::ACTIVITY_LOGS_VIEW);

        createActivityLogs($user, 3);

        $this->actingAs($user)
            ->get(route('admin.system.activity_logs.index'))
            ->assertOk();
    });

    test('index returns pagination data', function () {
        $user = createActivityLogUserWithPermission(PermissionEnum::ACTIVITY_LOGS_VIEW);

        createActivityLogs($user, 15);

        $this->actingAs($user)
            ->get(route('admin.system.activity_logs.index'))
            ->assertOk();
    });

    test('index can search activity logs by description', function () {
        $user = createActivityLogUserWithPermission(PermissionEnum::ACTIVITY_LOGS_VIEW);

        activity()
            ->causedBy($user)
            ->event('created')
            ->log('Produk baru ditambahkan');

        activity()
            ->causedBy($user)
            ->event('updated')
            ->log('Supplier diperbarui');

        $this->actingAs($user)
            ->get(route('admin.system.activity_logs.index', ['search' => 'Produk']))
            ->assertOk();
    });

    test('index can filter by event type', function () {
        $user = createActivityLogUserWithPermission(PermissionEnum::ACTIVITY_LOGS_VIEW);

        activity()->causedBy($user)->event('created')->log('Test created');
        activity()->causedBy($user)->event('updated')->log('Test updated');

        $this->actingAs($user)
            ->get(route('admin.system.activity_logs.index', ['event' => 'created']))
            ->assertOk();
    });

    test('index can filter by subject type', function () {
        $user = createActivityLogUserWithPermission(PermissionEnum::ACTIVITY_LOGS_VIEW);

        Category::factory()->create();
        $user->update(['email' => 'new@example.com']);

        $this->actingAs($user)
            ->get(route('admin.system.activity_logs.index', ['subject_type' => Category::class]))
            ->assertOk();
    });

    test('index can sort by created_at', function () {
        $user = createActivityLogUserWithPermission(PermissionEnum::ACTIVITY_LOGS_VIEW);

        createActivityLogs($user, 3);

        $this->actingAs($user)
            ->get(route('admin.system.activity_logs.index', ['sort' => 'created_at', 'direction' => 'asc']))
            ->assertOk();
    });

    test('index can sort by event', function () {
        $user = createActivityLogUserWithPermission(PermissionEnum::ACTIVITY_LOGS_VIEW);

        createActivityLogs($user, 3);

        $this->actingAs($user)
            ->get(route('admin.system.activity_logs.index', ['sort' => 'event', 'direction' => 'desc']))
            ->assertOk();
    });

    test('index can sort by description', function () {
        $user = createActivityLogUserWithPermission(PermissionEnum::ACTIVITY_LOGS_VIEW);

        createActivityLogs($user, 3);

        $this->actingAs($user)
            ->get(route('admin.system.activity_logs.index', ['sort' => 'description', 'direction' => 'asc']))
            ->assertOk();
    });
});

// ─── Activity Log Creation via Model ──────────────────────

describe('Activity Log Creation', function () {
    test('activity log is created when category is created', function () {
        $user = createActivityLogUserWithPermission(PermissionEnum::CATEGORIES_VIEW, PermissionEnum::CATEGORIES_CREATE);

        $this->actingAs($user)
            ->post(route('admin.master.categories.store'), [
                'name' => 'Test Kategori',
            ]);

        $this->assertDatabaseHas('activity_log', [
            'event' => 'created',
            'subject_type' => Category::class,
        ]);
    });

    test('activity log is created when category is updated', function () {
        $user = createActivityLogUserWithPermission(PermissionEnum::CATEGORIES_VIEW, PermissionEnum::CATEGORIES_UPDATE);

        $category = Category::factory()->create();

        $this->actingAs($user)
            ->put(route('admin.master.categories.update', $category), [
                'name' => 'Updated Name',
            ]);

        $this->assertDatabaseHas('activity_log', [
            'event' => 'updated',
            'subject_type' => Category::class,
            'subject_id' => $category->id,
        ]);
    });

    test('activity log is created when category is deleted', function () {
        $user = createActivityLogUserWithPermission(PermissionEnum::CATEGORIES_VIEW, PermissionEnum::CATEGORIES_DELETE);

        $category = Category::factory()->create();

        $this->actingAs($user)
            ->delete(route('admin.master.categories.destroy', $category));

        $this->assertDatabaseHas('activity_log', [
            'event' => 'deleted',
            'subject_type' => Category::class,
            'subject_id' => $category->id,
        ]);
    });

    test('activity log stores causer information', function () {
        $user = createActivityLogUserWithPermission(PermissionEnum::ACTIVITY_LOGS_VIEW);

        $activity = activity()
            ->causedBy($user)
            ->event('created')
            ->log('Test log entry');

        expect($activity)->not->toBeNull();
        expect($activity->causer)->not->toBeNull();
        expect($activity->causer->id)->toBe($user->id);
    });

    test('activity log stores properties correctly', function () {
        $user = createActivityLogUserWithPermission(PermissionEnum::ACTIVITY_LOGS_VIEW);

        $activity = activity()
            ->causedBy($user)
            ->event('updated')
            ->withProperties([
                'attributes' => ['name' => 'New Value'],
                'old' => ['name' => 'Old Value'],
            ])
            ->log('Test with properties');

        expect($activity)->not->toBeNull();

        $props = $activity->properties ?? $activity->attribute_changes;
        expect($props)->not->toBeNull();
        expect($props->get('attributes')['name'])->toBe('New Value');
        expect($props->get('old')['name'])->toBe('Old Value');
    });
});
