<?php

use App\Models\Group;
use App\Models\GroupMember;
use App\Models\User;
use App\Policies\GroupPolicy;
use App\Services\GroupMembershipService;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

function attachGroupMember(Group $group, User $user, string $role): GroupMember
{
    return GroupMember::query()->create([
        'group_id' => $group->id,
        'user_id' => $user->id,
        'invited_by_user_id' => $group->owner_user_id,
        'role' => $role,
        'status' => Group::STATUS_ACTIVE,
        'invited_at' => now(),
        'accepted_at' => now(),
    ]);
}

it('allows contributors into studio but not management or publishing policy actions', function () {
    $owner = User::factory()->create();
    $contributor = User::factory()->create();
    $group = Group::factory()->for($owner, 'owner')->create();

    app(GroupMembershipService::class)->ensureOwnerMembership($group);
    attachGroupMember($group, $contributor, Group::ROLE_MEMBER);

    $policy = app(GroupPolicy::class);

    expect($policy->viewStudio($contributor, $group))->toBeTrue()
        ->and($policy->update($contributor, $group))->toBeFalse()
        ->and($policy->manageMembers($contributor, $group))->toBeFalse()
        ->and($policy->publishArtworks($contributor, $group))->toBeFalse()
        ->and($policy->manageCollections($contributor, $group))->toBeFalse();
});

it('allows editors to publish artworks and manage collections without member administration', function () {
    $owner = User::factory()->create();
    $editor = User::factory()->create();
    $group = Group::factory()->for($owner, 'owner')->create();

    app(GroupMembershipService::class)->ensureOwnerMembership($group);
    attachGroupMember($group, $editor, Group::ROLE_EDITOR);

    $policy = app(GroupPolicy::class);

    expect($policy->viewStudio($editor, $group))->toBeTrue()
        ->and($policy->publishArtworks($editor, $group))->toBeTrue()
        ->and($policy->manageCollections($editor, $group))->toBeTrue()
        ->and($policy->manageMembers($editor, $group))->toBeFalse()
        ->and($policy->archive($editor, $group))->toBeFalse();
});

it('allows admins to manage group settings and members but not archive ownership actions', function () {
    $owner = User::factory()->create();
    $admin = User::factory()->create();
    $group = Group::factory()->for($owner, 'owner')->create();

    app(GroupMembershipService::class)->ensureOwnerMembership($group);
    attachGroupMember($group, $admin, Group::ROLE_ADMIN);

    $policy = app(GroupPolicy::class);

    expect($policy->viewStudio($admin, $group))->toBeTrue()
        ->and($policy->update($admin, $group))->toBeTrue()
        ->and($policy->manageMembers($admin, $group))->toBeTrue()
        ->and($policy->publishArtworks($admin, $group))->toBeTrue()
        ->and($policy->archive($admin, $group))->toBeFalse();
});

it('blocks studio access for suspended groups even for active non-owner members', function () {
    $owner = User::factory()->create();
    $editor = User::factory()->create();
    $group = Group::factory()->for($owner, 'owner')->create([
        'status' => Group::LIFECYCLE_SUSPENDED,
    ]);

    app(GroupMembershipService::class)->ensureOwnerMembership($group);
    attachGroupMember($group, $editor, Group::ROLE_EDITOR);

    $policy = app(GroupPolicy::class);

    expect($policy->viewStudio($editor, $group))->toBeFalse()
        ->and($policy->publishArtworks($editor, $group))->toBeFalse()
        ->and($policy->manageCollections($editor, $group))->toBeFalse();
});

it('keeps archive authority with the owner', function () {
    $owner = User::factory()->create();
    $group = Group::factory()->for($owner, 'owner')->create();

    app(GroupMembershipService::class)->ensureOwnerMembership($group);

    $policy = app(GroupPolicy::class);

    expect($policy->update($owner, $group))->toBeTrue()
        ->and($policy->manageMembers($owner, $group))->toBeTrue()
        ->and($policy->publishArtworks($owner, $group))->toBeTrue()
        ->and($policy->archive($owner, $group))->toBeTrue();
});

it('does not allow admins or editors to transfer ownership through policy update access alone', function () {
    $owner = User::factory()->create();
    $admin = User::factory()->create();
    $editor = User::factory()->create();
    $group = Group::factory()->for($owner, 'owner')->create();

    app(GroupMembershipService::class)->ensureOwnerMembership($group);
    attachGroupMember($group, $admin, Group::ROLE_ADMIN);
    attachGroupMember($group, $editor, Group::ROLE_EDITOR);

    $policy = app(GroupPolicy::class);

    expect($policy->update($admin, $group))->toBeTrue()
        ->and($policy->archive($admin, $group))->toBeFalse()
        ->and($policy->manageMembers($editor, $group))->toBeFalse();
});

it('exposes explicit v3 event and private access policy hooks', function () {
    $owner = User::factory()->create();
    $editor = User::factory()->create();
    $member = User::factory()->create();
    $group = Group::factory()->for($owner, 'owner')->create();

    app(GroupMembershipService::class)->ensureOwnerMembership($group);
    attachGroupMember($group, $editor, Group::ROLE_EDITOR);
    attachGroupMember($group, $member, Group::ROLE_MEMBER);

    $policy = app(GroupPolicy::class);

    expect($policy->publishEventUpdates($editor, $group))->toBeTrue()
        ->and($policy->viewInternalEvents($editor, $group))->toBeTrue()
        ->and($policy->viewPrivateProject($editor, $group))->toBeTrue()
        ->and($policy->participateInChallenge($member, $group))->toBeTrue()
        ->and($policy->publishEventUpdates($member, $group))->toBeFalse();
});

it('exposes explicit v4 release, milestone, badge, and trust policy hooks', function () {
    $owner = User::factory()->create();
    $admin = User::factory()->create();
    $editor = User::factory()->create();
    $member = User::factory()->create();
    $group = Group::factory()->for($owner, 'owner')->create();

    app(GroupMembershipService::class)->ensureOwnerMembership($group);
    attachGroupMember($group, $admin, Group::ROLE_ADMIN);
    attachGroupMember($group, $editor, Group::ROLE_EDITOR);
    attachGroupMember($group, $member, Group::ROLE_MEMBER);

    $policy = app(GroupPolicy::class);

    expect($policy->manageReleases($editor, $group))->toBeTrue()
        ->and($policy->publishReleases($editor, $group))->toBeTrue()
        ->and($policy->moveReleaseStage($editor, $group))->toBeTrue()
        ->and($policy->manageMilestones($editor, $group))->toBeTrue()
        ->and($policy->assignReleaseLead($editor, $group))->toBeTrue()
        ->and($policy->viewReputationDashboard($editor, $group))->toBeFalse()
        ->and($policy->manageBadges($admin, $group))->toBeTrue()
        ->and($policy->viewInternalTrustMetrics($admin, $group))->toBeTrue()
        ->and($policy->featureRelease($admin, $group))->toBeTrue()
        ->and($policy->manageReleases($member, $group))->toBeFalse()
        ->and($policy->viewReputationDashboard($member, $group))->toBeFalse();
});