<?php

use App\Enums\OwnerVerificationAction;
use App\Enums\OwnerVerificationStatus;
use App\Enums\Permission as PermissionEnum;
use App\Models\OwnerVerificationRequest;
use App\Models\Purchase;
use App\Models\PurchaseItem;
use App\Models\RawMaterialPrice;
use App\Models\Supplier;
use App\Models\User;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

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

function createPurchaseUserWithPermission(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 createPurchaseVerifierUser(): 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 approveLatestPurchaseVerificationRequest(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();
}

describe('Purchase Owner Verification', function () {
    test('create purchase submits verification without changing stock', function () {
        $user = createPurchaseUserWithPermission(
            PermissionEnum::PURCHASES_VIEW,
            PermissionEnum::PURCHASES_CREATE,
        );

        $supplier = Supplier::factory()->create();
        $price = RawMaterialPrice::factory()->create(['stock' => 10]);
        $initialStock = (float) $price->stock;

        $this->actingAs($user)
            ->postJson(route('admin.manage.purchases.draft_items.store'), [
                'raw_material_price_id' => $price->id,
                'quantity' => 2,
            ])
            ->assertOk();

        $this->actingAs($user)
            ->post(route('admin.manage.purchases.store'), [
                'supplier_id' => $supplier->id,
                'discount' => 0,
                'shipping_cost' => 0,
                'notes' => 'Belanja test',
            ])
            ->assertRedirect(route('admin.manage.purchases.index'));

        $purchase = Purchase::query()->latest()->first();

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

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

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

    test('approving create purchase increments stock', function () {
        $user = createPurchaseUserWithPermission(
            PermissionEnum::PURCHASES_VIEW,
            PermissionEnum::PURCHASES_CREATE,
        );
        $verifier = createPurchaseVerifierUser();

        $supplier = Supplier::factory()->create();
        $price = RawMaterialPrice::factory()->create(['stock' => 10]);

        $this->actingAs($user)
            ->postJson(route('admin.manage.purchases.draft_items.store'), [
                'raw_material_price_id' => $price->id,
                'quantity' => 2,
            ])
            ->assertOk();

        $this->actingAs($user)
            ->post(route('admin.manage.purchases.store'), [
                'supplier_id' => $supplier->id,
                'discount' => 0,
                'shipping_cost' => 0,
            ])
            ->assertRedirect(route('admin.manage.purchases.index'));

        approveLatestPurchaseVerificationRequest($verifier);

        expect((float) $price->fresh()->stock)->toBe(12.0);
    });

    test('update purchase submits verification request only', function () {
        $user = createPurchaseUserWithPermission(
            PermissionEnum::PURCHASES_VIEW,
            PermissionEnum::PURCHASES_CREATE,
            PermissionEnum::PURCHASES_UPDATE,
        );
        $verifier = createPurchaseVerifierUser();

        $supplier = Supplier::factory()->create();
        $price = RawMaterialPrice::factory()->create(['stock' => 10, 'price' => 50_000]);
        $otherPrice = RawMaterialPrice::factory()->create(['stock' => 5, 'price' => 30_000]);

        $this->actingAs($user)
            ->postJson(route('admin.manage.purchases.draft_items.store'), [
                'raw_material_price_id' => $price->id,
                'quantity' => 2,
            ])
            ->assertOk();

        $this->actingAs($user)
            ->post(route('admin.manage.purchases.store'), [
                'supplier_id' => $supplier->id,
                'discount' => 0,
                'shipping_cost' => 0,
            ])
            ->assertRedirect(route('admin.manage.purchases.index'));

        approveLatestPurchaseVerificationRequest($verifier);

        $purchase = Purchase::query()->latest()->firstOrFail();
        $originalTotal = $purchase->total;

        $this->actingAs($user)
            ->put(route('admin.manage.purchases.update', $purchase), [
                'supplier_id' => $supplier->id,
                'discount' => 1000,
                'shipping_cost' => 0,
                'notes' => 'Ubah belanja',
                'items' => [
                    ['raw_material_price_id' => $otherPrice->id, 'quantity' => 1],
                ],
            ])
            ->assertRedirect(route('admin.manage.purchases.index'));

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

        expect($purchase->fresh()->total)->toBe($originalTotal);
    });

    test('delete purchase submits verification request only', function () {
        $user = createPurchaseUserWithPermission(
            PermissionEnum::PURCHASES_VIEW,
            PermissionEnum::PURCHASES_CREATE,
            PermissionEnum::PURCHASES_DELETE,
        );
        $verifier = createPurchaseVerifierUser();

        $supplier = Supplier::factory()->create();
        $price = RawMaterialPrice::factory()->create(['stock' => 10]);

        $this->actingAs($user)
            ->postJson(route('admin.manage.purchases.draft_items.store'), [
                'raw_material_price_id' => $price->id,
                'quantity' => 2,
            ])
            ->assertOk();

        $this->actingAs($user)
            ->post(route('admin.manage.purchases.store'), [
                'supplier_id' => $supplier->id,
                'discount' => 0,
                'shipping_cost' => 0,
            ])
            ->assertRedirect(route('admin.manage.purchases.index'));

        approveLatestPurchaseVerificationRequest($verifier);

        $purchase = Purchase::query()->latest()->firstOrFail();

        $this->actingAs($user)
            ->delete(route('admin.manage.purchases.destroy', $purchase))
            ->assertRedirect(route('admin.manage.purchases.index'));

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

        expect(Purchase::query()->whereKey($purchase->id)->exists())->toBeTrue();
        expect((float) $price->fresh()->stock)->toBe(12.0);
    });

    test('rejecting create purchase deletes purchase without changing stock', function () {
        $user = createPurchaseUserWithPermission(
            PermissionEnum::PURCHASES_VIEW,
            PermissionEnum::PURCHASES_CREATE,
        );
        $verifier = createPurchaseVerifierUser();

        $supplier = Supplier::factory()->create();
        $price = RawMaterialPrice::factory()->create(['stock' => 10]);

        $this->actingAs($user)
            ->postJson(route('admin.manage.purchases.draft_items.store'), [
                'raw_material_price_id' => $price->id,
                'quantity' => 2,
            ])
            ->assertOk();

        $this->actingAs($user)
            ->post(route('admin.manage.purchases.store'), [
                'supplier_id' => $supplier->id,
                'discount' => 0,
                'shipping_cost' => 0,
            ])
            ->assertRedirect(route('admin.manage.purchases.index'));

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

        $this->actingAs($verifier)
            ->post(route('admin.manage.owner_verifications.reject_request', $verificationRequest), [
                'reason' => 'Tidak sesuai',
            ])
            ->assertRedirect();

        expect(Purchase::query()->whereKey($purchase->id)->exists())->toBeFalse();
        expect(PurchaseItem::query()->where('purchase_id', $purchase->id)->exists())->toBeFalse();
        expect((float) $price->fresh()->stock)->toBe(10.0);
    });

    test('pending purchase blocks edit route', function () {
        $user = createPurchaseUserWithPermission(
            PermissionEnum::PURCHASES_VIEW,
            PermissionEnum::PURCHASES_CREATE,
            PermissionEnum::PURCHASES_UPDATE,
        );

        $supplier = Supplier::factory()->create();
        $price = RawMaterialPrice::factory()->create(['stock' => 10]);

        $this->actingAs($user)
            ->postJson(route('admin.manage.purchases.draft_items.store'), [
                'raw_material_price_id' => $price->id,
                'quantity' => 2,
            ])
            ->assertOk();

        $this->actingAs($user)
            ->post(route('admin.manage.purchases.store'), [
                'supplier_id' => $supplier->id,
                'discount' => 0,
                'shipping_cost' => 0,
            ])
            ->assertRedirect(route('admin.manage.purchases.index'));

        $purchase = Purchase::query()->latest()->firstOrFail();

        $this->actingAs($user)
            ->get(route('admin.manage.purchases.edit', $purchase))
            ->assertRedirect(route('admin.manage.purchases.index'));
    });

    test('purchase index exposes pending verification state', function () {
        $user = createPurchaseUserWithPermission(
            PermissionEnum::PURCHASES_VIEW,
            PermissionEnum::PURCHASES_CREATE,
        );

        $supplier = Supplier::factory()->create();
        $price = RawMaterialPrice::factory()->create(['stock' => 10]);

        $this->actingAs($user)
            ->postJson(route('admin.manage.purchases.draft_items.store'), [
                'raw_material_price_id' => $price->id,
                'quantity' => 2,
            ])
            ->assertOk();

        $this->actingAs($user)
            ->post(route('admin.manage.purchases.store'), [
                'supplier_id' => $supplier->id,
                'discount' => 0,
                'shipping_cost' => 0,
            ])
            ->assertRedirect(route('admin.manage.purchases.index'));

        $purchase = Purchase::query()->latest()->firstOrFail();

        $response = $this->actingAs($user)
            ->get(route('admin.manage.purchases.index'));

        $response->assertOk();
        $response->assertInertia(fn ($page) => $page
            ->where('purchases.data.0.id', $purchase->id)
            ->where('purchases.data.0.has_pending_request', true)
            ->where('purchases.data.0.pending_request_action', OwnerVerificationAction::CREATE->value)
        );
    });
});
