<?php

use App\Enums\OwnerVerificationAction;
use App\Enums\OwnerVerificationStatus;
use App\Enums\Permission as PermissionEnum;
use App\Enums\ProductStockQuality;
use App\Models\OwnerVerificationRequest;
use App\Models\ProductVariant;
use App\Models\Restock;
use App\Models\RestockItem;
use App\Models\User;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

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

function createRestockUserWithPermission(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 createRestockOwnerUser(PermissionEnum ...$permissions): User
{
    $user = User::factory()->create();

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

    $user->forgetCachedPermissions();

    return $user;
}

function createRestockVerifierUser(): User
{
    $user = User::factory()->create();

    $user->givePermissionTo([
        PermissionEnum::DASHBOARD_VIEW->value,
        PermissionEnum::OWNER_VERIFICATIONS_VIEW->value,
        PermissionEnum::OWNER_VERIFICATIONS_VERIFY->value,
        PermissionEnum::OWNER_VERIFICATIONS_REJECT->value,
    ]);

    $user->forgetCachedPermissions();

    return $user;
}

function approveLatestRestockVerificationRequest(User $verifier): OwnerVerificationRequest
{
    $verificationRequest = OwnerVerificationRequest::query()->pending()->latest()->firstOrFail();

    test()->actingAs($verifier)
        ->post(route('admin.manage.owner_verifications.approve_request', $verificationRequest))
        ->assertRedirect();

    return $verificationRequest->fresh();
}

function getRestockItemsPayload(ProductVariant $variant, int $quantity = 2, int $unitPrice = 50000): array
{
    return [
        [
            'product_variant_id' => $variant->id,
            'quantity' => $quantity,
            'unit_price' => $unitPrice,
        ],
    ];
}

describe('Restock CRUD', function () {
    test('owner can create restock and stock is incremented immediately', function () {
        $user = createRestockOwnerUser(
            PermissionEnum::RESTOCKS_VIEW,
            PermissionEnum::RESTOCKS_CREATE,
        );

        $variant = ProductVariant::factory()->create(['stock' => 10]);
        $initialStock = $variant->stock;

        $this->actingAs($user)
            ->post(route('admin.manage.restocks.store'), [
                'notes' => 'Restock test',
                'stock_type' => 'good',
                'items' => getRestockItemsPayload($variant, 3),
            ])
            ->assertRedirect(route('admin.manage.restocks.index'));

        $restock = Restock::query()->latest()->first();

        expect($restock)->not->toBeNull();
        expect($restock->notes)->toBe('Restock test');
        expect($restock->stock_type)->toBe(ProductStockQuality::GOOD);

        expect($variant->fresh()->stock)->toBe($initialStock + 3);
    });

    test('owner can create restock with retail stock type', function () {
        $user = createRestockOwnerUser(
            PermissionEnum::RESTOCKS_VIEW,
            PermissionEnum::RESTOCKS_CREATE,
        );

        $variant = ProductVariant::factory()->create(['retail_stock' => 5]);

        $this->actingAs($user)
            ->post(route('admin.manage.restocks.store'), [
                'notes' => 'Restock eceran',
                'stock_type' => 'retail',
                'items' => getRestockItemsPayload($variant, 2),
            ])
            ->assertRedirect(route('admin.manage.restocks.index'));

        expect($variant->fresh()->retail_stock)->toBe(7);
    });

    test('owner can create restock with reject stock type', function () {
        $user = createRestockOwnerUser(
            PermissionEnum::RESTOCKS_VIEW,
            PermissionEnum::RESTOCKS_CREATE,
        );

        $variant = ProductVariant::factory()->create(['reject_stock' => 3]);

        $this->actingAs($user)
            ->post(route('admin.manage.restocks.store'), [
                'notes' => 'Restock reject',
                'stock_type' => 'reject',
                'items' => getRestockItemsPayload($variant, 4),
            ])
            ->assertRedirect(route('admin.manage.restocks.index'));

        expect($variant->fresh()->reject_stock)->toBe(7);
    });

    test('owner can delete restock and stock is decremented', function () {
        $user = createRestockOwnerUser(
            PermissionEnum::RESTOCKS_VIEW,
            PermissionEnum::RESTOCKS_CREATE,
            PermissionEnum::RESTOCKS_DELETE,
        );

        $variant = ProductVariant::factory()->create(['stock' => 10]);

        $this->actingAs($user)
            ->post(route('admin.manage.restocks.store'), [
                'stock_type' => 'good',
                'items' => getRestockItemsPayload($variant, 3),
            ])
            ->assertRedirect();

        expect($variant->fresh()->stock)->toBe(13);

        $restock = Restock::query()->latest()->first();

        $this->actingAs($user)
            ->delete(route('admin.manage.restocks.destroy', $restock))
            ->assertRedirect();

        expect($variant->fresh()->stock)->toBe(10);
    });

    test('index page returns ok', function () {
        $user = createRestockOwnerUser(PermissionEnum::RESTOCKS_VIEW);

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

    test('create page returns ok', function () {
        $user = createRestockOwnerUser(
            PermissionEnum::RESTOCKS_VIEW,
            PermissionEnum::RESTOCKS_CREATE,
        );

        $this->actingAs($user)
            ->get(route('admin.manage.restocks.create'))
            ->assertOk();
    });

    test('edit page returns ok', function () {
        $user = createRestockOwnerUser(
            PermissionEnum::RESTOCKS_VIEW,
            PermissionEnum::RESTOCKS_CREATE,
            PermissionEnum::RESTOCKS_UPDATE,
        );

        $variant = ProductVariant::factory()->create();

        $this->actingAs($user)
            ->post(route('admin.manage.restocks.store'), [
                'stock_type' => 'good',
                'items' => getRestockItemsPayload($variant),
            ])
            ->assertRedirect();

        $restock = Restock::query()->latest()->first();

        $this->actingAs($user)
            ->get(route('admin.manage.restocks.edit', $restock))
            ->assertOk();
    });
});

describe('Restock Owner Verification', function () {
    test('non-owner create restock submits verification without changing stock', function () {
        $user = createRestockUserWithPermission(
            PermissionEnum::RESTOCKS_VIEW,
            PermissionEnum::RESTOCKS_CREATE,
        );

        $variant = ProductVariant::factory()->create(['stock' => 10]);
        $initialStock = $variant->stock;

        $this->actingAs($user)
            ->post(route('admin.manage.restocks.store'), [
                'stock_type' => 'good',
                'items' => getRestockItemsPayload($variant, 3),
            ])
            ->assertRedirect(route('admin.manage.restocks.index'));

        $restock = Restock::query()->latest()->first();

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

        $this->assertDatabaseHas('owner_verification_requests', [
            'subject_type' => Restock::class,
            'subject_id' => $restock->id,
            'action' => OwnerVerificationAction::CREATE->value,
            'status' => OwnerVerificationStatus::PENDING->value,
        ]);

        expect($variant->fresh()->stock)->toBe($initialStock);
    });

    test('approving create restock increments stock', function () {
        $user = createRestockUserWithPermission(
            PermissionEnum::RESTOCKS_VIEW,
            PermissionEnum::RESTOCKS_CREATE,
        );
        $verifier = createRestockVerifierUser();

        $variant = ProductVariant::factory()->create(['stock' => 10]);

        $this->actingAs($user)
            ->post(route('admin.manage.restocks.store'), [
                'stock_type' => 'good',
                'items' => getRestockItemsPayload($variant, 3),
            ])
            ->assertRedirect();

        approveLatestRestockVerificationRequest($verifier);

        expect($variant->fresh()->stock)->toBe(13);
    });

    test('rejecting create restock does not change stock and deletes restock', function () {
        $user = createRestockUserWithPermission(
            PermissionEnum::RESTOCKS_VIEW,
            PermissionEnum::RESTOCKS_CREATE,
        );
        $verifier = createRestockVerifierUser();

        $variant = ProductVariant::factory()->create(['stock' => 10]);

        $this->actingAs($user)
            ->post(route('admin.manage.restocks.store'), [
                'stock_type' => 'good',
                'items' => getRestockItemsPayload($variant, 3),
            ])
            ->assertRedirect();

        $restock = Restock::query()->latest()->first();
        $restockId = $restock->id;

        $verificationRequest = OwnerVerificationRequest::query()->pending()->latest()->firstOrFail();

        $this->actingAs($verifier)
            ->post(route('admin.manage.owner_verifications.reject_request', $verificationRequest))
            ->assertRedirect();

        expect($variant->fresh()->stock)->toBe(10);
        expect(Restock::find($restockId))->toBeNull();
        expect(Restock::withTrashed()->find($restockId))->not->toBeNull();
    });

    test('non-owner delete restock submits verification request', function () {
        $user = createRestockUserWithPermission(
            PermissionEnum::RESTOCKS_VIEW,
            PermissionEnum::RESTOCKS_CREATE,
            PermissionEnum::RESTOCKS_DELETE,
        );
        $verifier = createRestockVerifierUser();

        $variant = ProductVariant::factory()->create(['stock' => 10]);

        $this->actingAs($user)
            ->post(route('admin.manage.restocks.store'), [
                'stock_type' => 'good',
                'items' => getRestockItemsPayload($variant, 3),
            ])
            ->assertRedirect();

        $restock = Restock::query()->latest()->first();

        approveLatestRestockVerificationRequest($verifier);

        expect($variant->fresh()->stock)->toBe(13);

        $this->actingAs($user)
            ->delete(route('admin.manage.restocks.destroy', $restock))
            ->assertRedirect();

        $this->assertDatabaseHas('owner_verification_requests', [
            'subject_type' => Restock::class,
            'subject_id' => $restock->id,
            'action' => OwnerVerificationAction::DELETE->value,
            'status' => OwnerVerificationStatus::PENDING->value,
        ]);

        expect($variant->fresh()->stock)->toBe(13);
    });

    test('approving delete restock decrements stock', function () {
        $user = createRestockUserWithPermission(
            PermissionEnum::RESTOCKS_VIEW,
            PermissionEnum::RESTOCKS_CREATE,
            PermissionEnum::RESTOCKS_DELETE,
        );
        $verifier = createRestockVerifierUser();

        $variant = ProductVariant::factory()->create(['stock' => 10]);

        $this->actingAs($user)
            ->post(route('admin.manage.restocks.store'), [
                'stock_type' => 'good',
                'items' => getRestockItemsPayload($variant, 3),
            ])
            ->assertRedirect();

        $restock = Restock::query()->latest()->first();

        approveLatestRestockVerificationRequest($verifier);

        expect($variant->fresh()->stock)->toBe(13);

        $this->actingAs($user)
            ->delete(route('admin.manage.restocks.destroy', $restock))
            ->assertRedirect();

        approveLatestRestockVerificationRequest($verifier);

        expect($variant->fresh()->stock)->toBe(10);
    });

    test('non-owner update restock submits verification request', function () {
        $user = createRestockUserWithPermission(
            PermissionEnum::RESTOCKS_VIEW,
            PermissionEnum::RESTOCKS_CREATE,
            PermissionEnum::RESTOCKS_UPDATE,
        );
        $verifier = createRestockVerifierUser();

        $variant = ProductVariant::factory()->create(['stock' => 10]);
        $otherVariant = ProductVariant::factory()->create(['stock' => 5]);

        $this->actingAs($user)
            ->post(route('admin.manage.restocks.store'), [
                'stock_type' => 'good',
                'items' => getRestockItemsPayload($variant, 2),
            ])
            ->assertRedirect();

        $restock = Restock::query()->latest()->first();

        approveLatestRestockVerificationRequest($verifier);

        expect($variant->fresh()->stock)->toBe(12);

        $this->actingAs($user)
            ->put(route('admin.manage.restocks.update', $restock), [
                'stock_type' => 'good',
                'items' => getRestockItemsPayload($otherVariant, 4),
            ])
            ->assertRedirect();

        $this->assertDatabaseHas('owner_verification_requests', [
            'subject_type' => Restock::class,
            'subject_id' => $restock->id,
            'action' => OwnerVerificationAction::UPDATE->value,
            'status' => OwnerVerificationStatus::PENDING->value,
        ]);
    });
});

describe('Restock Draft Items', function () {
    test('draft item can be created and deleted', function () {
        $user = createRestockOwnerUser(
            PermissionEnum::RESTOCKS_VIEW,
            PermissionEnum::RESTOCKS_CREATE,
        );

        $variant = ProductVariant::factory()->create();

        $this->actingAs($user)
            ->postJson(route('admin.manage.restocks.draft_items.store'), [
                'product_variant_id' => $variant->id,
                'quantity' => 5,
                'unit_price' => 50000,
            ])
            ->assertOk();

        $this->assertDatabaseHas('restock_items', [
            'user_id' => $user->id,
            'product_variant_id' => $variant->id,
            'quantity' => 5,
            'unit_price' => 50000,
            'restock_id' => null,
        ]);

        $itemId = RestockItem::where('user_id', $user->id)
            ->where('product_variant_id', $variant->id)
            ->whereNull('restock_id')
            ->value('id');

        $this->actingAs($user)
            ->deleteJson(route('admin.manage.restocks.draft_items.destroy', ['productVariant' => $variant->id]))
            ->assertOk();

        $this->assertDatabaseMissing('restock_items', ['id' => $itemId]);
    });

    test('draft items are cleared when opening create page', function () {
        $user = createRestockOwnerUser(
            PermissionEnum::RESTOCKS_VIEW,
            PermissionEnum::RESTOCKS_CREATE,
        );

        $variant = ProductVariant::factory()->create();

        $this->actingAs($user)
            ->postJson(route('admin.manage.restocks.draft_items.store'), [
                'product_variant_id' => $variant->id,
                'quantity' => 5,
                'unit_price' => 50000,
            ])
            ->assertOk();

        $itemId = RestockItem::where('user_id', $user->id)
            ->where('product_variant_id', $variant->id)
            ->whereNull('restock_id')
            ->value('id');

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

        $this->actingAs($user)
            ->get(route('admin.manage.restocks.create'))
            ->assertOk();

        expect(RestockItem::find($itemId))->toBeNull();
    });
});
