<?php

use App\Models\AuditLog;
use App\Models\TenantMembership;
use App\Models\User;
use App\Services\Auth\TenantMembershipManager;
use App\Support\Audit\AuditActionId;

it('writes canonical audit action IDs for membership mutations', function () {
    [$owner, $tenant] = createUserWithTenant(role: 'owner');

    $member = User::factory()->create();

    /** @var TenantMembershipManager $manager */
    $manager = app(TenantMembershipManager::class);

    $membership = $manager->addMember(
        tenant: $tenant,
        actor: $owner,
        member: $member,
        role: 'readonly',
        source: 'manual',
    );

    $manager->changeRole(
        tenant: $tenant,
        actor: $owner,
        membership: $membership,
        newRole: 'operator',
    );

    $manager->removeMember(
        tenant: $tenant,
        actor: $owner,
        membership: $membership,
    );

    $logs = AuditLog::query()
        ->where('tenant_id', $tenant->id)
        ->whereIn('action', [
            AuditActionId::TenantMembershipAdd->value,
            AuditActionId::TenantMembershipRoleChange->value,
            AuditActionId::TenantMembershipRemove->value,
        ])
        ->get()
        ->keyBy('action');

    expect($logs)->toHaveCount(3);

    $addLog = $logs->get(AuditActionId::TenantMembershipAdd->value);
    $roleChangeLog = $logs->get(AuditActionId::TenantMembershipRoleChange->value);
    $removeLog = $logs->get(AuditActionId::TenantMembershipRemove->value);

    expect($addLog)->not->toBeNull();
    expect($roleChangeLog)->not->toBeNull();
    expect($removeLog)->not->toBeNull();

    expect($addLog->status)->toBe('success');
    expect($roleChangeLog->status)->toBe('success');
    expect($removeLog->status)->toBe('success');

    expect($addLog->metadata)
        ->toHaveKey('member_user_id', $member->id)
        ->toHaveKey('role', 'readonly')
        ->toHaveKey('source', 'manual')
        ->not->toHaveKey('member_email')
        ->not->toHaveKey('member_name');

    expect($roleChangeLog->metadata)
        ->toHaveKey('member_user_id', $member->id)
        ->toHaveKey('from_role', 'readonly')
        ->toHaveKey('to_role', 'operator')
        ->not->toHaveKey('member_email')
        ->not->toHaveKey('member_name');

    expect($removeLog->metadata)
        ->toHaveKey('member_user_id', $member->id)
        ->toHaveKey('role', 'operator')
        ->not->toHaveKey('member_email')
        ->not->toHaveKey('member_name');
});

it('writes a last-owner-blocked audit log when demoting or removing the last owner', function () {
    [$owner, $tenant] = createUserWithTenant(role: 'owner');

    $membership = TenantMembership::query()
        ->where('tenant_id', $tenant->id)
        ->where('user_id', $owner->id)
        ->firstOrFail();

    /** @var TenantMembershipManager $manager */
    $manager = app(TenantMembershipManager::class);

    expect(fn () => $manager->changeRole(
        tenant: $tenant,
        actor: $owner,
        membership: $membership,
        newRole: 'manager',
    ))->toThrow(DomainException::class);

    expect(fn () => $manager->removeMember(
        tenant: $tenant,
        actor: $owner,
        membership: $membership,
    ))->toThrow(DomainException::class);

    $blockedLogs = AuditLog::query()
        ->where('tenant_id', $tenant->id)
        ->where('action', AuditActionId::TenantMembershipLastOwnerBlocked->value)
        ->where('status', 'blocked')
        ->get();

    expect($blockedLogs->count())->toBeGreaterThanOrEqual(2);

    expect($blockedLogs->contains(fn (AuditLog $log): bool => (
        ($log->metadata['member_user_id'] ?? null) === $owner->id
        && ($log->metadata['attempted_to_role'] ?? null) === 'manager'
    )))->toBeTrue();

    expect($blockedLogs->contains(fn (AuditLog $log): bool => (
        ($log->metadata['member_user_id'] ?? null) === $owner->id
        && ($log->metadata['attempted_action'] ?? null) === 'remove'
    )))->toBeTrue();

    foreach ($blockedLogs as $log) {
        expect($log->metadata)
            ->not->toHaveKey('member_email')
            ->not->toHaveKey('member_name');
    }
});
