<?php

use App\Enums\Permission as PermissionEnum;
use App\Enums\ProductStockQuality;
use App\Enums\StokOpnameStatus;
use App\Models\Product;
use App\Models\ProductVariant;
use App\Models\StokOpname;
use App\Models\StokOpnameItem;
use App\Models\User;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

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

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

function createStokOpnameUserWithPermission(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 createCatalogSetup(): array
{
    $product = Product::factory()->create();
    $variant = ProductVariant::factory()->create([
        'product_id' => $product->id,
        'stock' => 10,
        'retail_stock' => 5,
        'reject_stock' => 2,
    ]);

    return ['product' => $product, 'variant' => $variant];
}

// ─── Index & Create ───────────────────────────────────────

describe('Stok Opname Index & Create', function () {
    test('authenticated user with permission can view stok opnames index', function () {
        $user = createStokOpnameUserWithPermission(PermissionEnum::STOK_OPNAMES_VIEW);

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

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

        $this->actingAs($user)
            ->get(route('admin.manage.stok-opnames.index'))
            ->assertForbidden();
    });

    test('user with permission can view create page', function () {
        $user = createStokOpnameUserWithPermission(
            PermissionEnum::STOK_OPNAMES_VIEW,
            PermissionEnum::STOK_OPNAMES_CREATE
        );

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

// ─── Store & Update ───────────────────────────────────────

describe('Stok Opname Store & Update', function () {
    test('user can store a new stok opname', function () {
        $user = createStokOpnameUserWithPermission(
            PermissionEnum::STOK_OPNAMES_VIEW,
            PermissionEnum::STOK_OPNAMES_CREATE
        );

        $setup = createCatalogSetup();

        $payload = [
            'opname_date' => now()->format('Y-m-d'),
            'notes' => 'Test Opname Notes',
            'items' => [
                [
                    'product_variant_id' => $setup['variant']->id,
                    'stock_quality' => ProductStockQuality::GOOD->value,
                    'physical_stock' => 8,
                    'notes' => 'Kurang 2 bagus',
                ],
                [
                    'product_variant_id' => $setup['variant']->id,
                    'stock_quality' => ProductStockQuality::REJECT->value,
                    'physical_stock' => 3,
                    'notes' => 'Kelebihan 1 reject',
                ]
            ],
        ];

        $this->actingAs($user)
            ->post(route('admin.manage.stok-opnames.store'), $payload)
            ->assertRedirect(route('admin.manage.stok-opnames.index'));

        $this->assertDatabaseHas('stok_opnames', [
            'notes' => 'Test Opname Notes',
            'status' => StokOpnameStatus::DRAFT->value,
        ]);

        $this->assertDatabaseHas('stok_opname_items', [
            'product_variant_id' => $setup['variant']->id,
            'stock_quality' => ProductStockQuality::GOOD->value,
            'physical_stock' => 8,
            'difference' => -2,
        ]);

        $this->assertDatabaseHas('stok_opname_items', [
            'product_variant_id' => $setup['variant']->id,
            'stock_quality' => ProductStockQuality::REJECT->value,
            'physical_stock' => 3,
            'difference' => 1,
        ]);
    });

    test('user can auto-save a stok opname draft', function () {
        $user = createStokOpnameUserWithPermission(
            PermissionEnum::STOK_OPNAMES_VIEW,
            PermissionEnum::STOK_OPNAMES_CREATE
        );

        $setup = createCatalogSetup();

        $payload = [
            'opname_date' => now()->format('Y-m-d'),
            'notes' => 'Draft Notes',
            'items' => [
                [
                    'product_variant_id' => $setup['variant']->id,
                    'stock_quality' => ProductStockQuality::RETAIL->value,
                    'physical_stock' => 6,
                    'notes' => 'Tambah eceran',
                ]
            ],
        ];

        $response = $this->actingAs($user)
            ->postJson(route('admin.manage.stok-opnames.auto_save'), $payload)
            ->assertOk();

        $stokOpnameId = $response->json('stok_opname_id');

        $this->assertDatabaseHas('stok_opnames', [
            'id' => $stokOpnameId,
            'status' => StokOpnameStatus::DRAFT->value,
        ]);
    });

    test('user can update a draft stok opname', function () {
        $user = createStokOpnameUserWithPermission(
            PermissionEnum::STOK_OPNAMES_VIEW,
            PermissionEnum::STOK_OPNAMES_UPDATE
        );

        $setup = createCatalogSetup();

        $stokOpname = StokOpname::create([
            'opname_date' => now(),
            'status' => StokOpnameStatus::DRAFT,
            'created_by_id' => $user->id,
        ]);

        $item = StokOpnameItem::create([
            'stok_opname_id' => $stokOpname->id,
            'product_variant_id' => $setup['variant']->id,
            'stock_quality' => ProductStockQuality::GOOD,
            'system_stock' => 10,
            'physical_stock' => 8,
            'difference' => -2,
        ]);

        $payload = [
            'opname_date' => now()->format('Y-m-d'),
            'notes' => 'Updated Notes',
            'items' => [
                [
                    'product_variant_id' => $setup['variant']->id,
                    'stock_quality' => ProductStockQuality::GOOD->value,
                    'physical_stock' => 9,
                    'notes' => 'Diubah jadi 9',
                ]
            ],
        ];

        $this->actingAs($user)
            ->put(route('admin.manage.stok-opnames.update', $stokOpname), $payload)
            ->assertRedirect(route('admin.manage.stok-opnames.index'));

        $this->assertDatabaseHas('stok_opnames', [
            'id' => $stokOpname->id,
            'notes' => 'Updated Notes',
        ]);

        $this->assertDatabaseHas('stok_opname_items', [
            'stok_opname_id' => $stokOpname->id,
            'product_variant_id' => $setup['variant']->id,
            'stock_quality' => ProductStockQuality::GOOD->value,
            'physical_stock' => 9,
            'difference' => -1,
        ]);
    });
});

// ─── Submit, Verify & Reject ──────────────────────────────

describe('Stok Opname Submission & Verification', function () {
    test('user can submit a draft stok opname for verification', function () {
        $user = createStokOpnameUserWithPermission(
            PermissionEnum::STOK_OPNAMES_VIEW,
            PermissionEnum::STOK_OPNAMES_SUBMIT
        );

        $setup = createCatalogSetup();

        $stokOpname = StokOpname::create([
            'opname_date' => now(),
            'status' => StokOpnameStatus::DRAFT,
            'created_by_id' => $user->id,
        ]);

        StokOpnameItem::create([
            'stok_opname_id' => $stokOpname->id,
            'product_variant_id' => $setup['variant']->id,
            'stock_quality' => ProductStockQuality::GOOD,
            'system_stock' => 10,
            'physical_stock' => 8,
            'difference' => -2,
        ]);

        $this->actingAs($user)
            ->post(route('admin.manage.stok-opnames.submit', $stokOpname))
            ->assertRedirect(route('admin.manage.stok-opnames.index'));

        $this->assertEquals(StokOpnameStatus::PENDING, $stokOpname->refresh()->status);
    });

    test('user with verify permission can verify stok opname and reconcile multiple qualities', function () {
        $user = createStokOpnameUserWithPermission(
            PermissionEnum::STOK_OPNAMES_VIEW,
            PermissionEnum::STOK_OPNAMES_VERIFY
        );

        $setup = createCatalogSetup();

        $stokOpname = StokOpname::create([
            'opname_date' => now(),
            'status' => StokOpnameStatus::PENDING,
            'created_by_id' => $user->id,
        ]);

        // Create opname items for Good, Retail, and Reject qualities
        StokOpnameItem::create([
            'stok_opname_id' => $stokOpname->id,
            'product_variant_id' => $setup['variant']->id,
            'stock_quality' => ProductStockQuality::GOOD,
            'system_stock' => 10,
            'physical_stock' => 8,
            'difference' => -2,
        ]);

        StokOpnameItem::create([
            'stok_opname_id' => $stokOpname->id,
            'product_variant_id' => $setup['variant']->id,
            'stock_quality' => ProductStockQuality::RETAIL,
            'system_stock' => 5,
            'physical_stock' => 7,
            'difference' => 2,
        ]);

        StokOpnameItem::create([
            'stok_opname_id' => $stokOpname->id,
            'product_variant_id' => $setup['variant']->id,
            'stock_quality' => ProductStockQuality::REJECT,
            'system_stock' => 2,
            'physical_stock' => 0,
            'difference' => -2,
        ]);

        $this->actingAs($user)
            ->post(route('admin.manage.stok-opnames.verify', $stokOpname), [
                'verification_notes' => 'Verified successfully',
            ])
            ->assertRedirect(route('admin.manage.stok-opnames.index'));

        $stokOpname->refresh();
        $setup['variant']->refresh();

        $this->assertEquals(StokOpnameStatus::VERIFIED, $stokOpname->status);
        $this->assertEquals('Verified successfully', $stokOpname->verification_notes);
        $this->assertEquals($user->id, $stokOpname->verified_by_id);

        // Verify variant stocks are updated correctly matching their quality enums
        $this->assertEquals(8, $setup['variant']->stock);
        $this->assertEquals(7, $setup['variant']->retail_stock);
        $this->assertEquals(0, $setup['variant']->reject_stock);
    });

    test('user with verify permission can reject stok opname', function () {
        $user = createStokOpnameUserWithPermission(
            PermissionEnum::STOK_OPNAMES_VIEW,
            PermissionEnum::STOK_OPNAMES_VERIFY
        );

        $setup = createCatalogSetup();

        $stokOpname = StokOpname::create([
            'opname_date' => now(),
            'status' => StokOpnameStatus::PENDING,
            'created_by_id' => $user->id,
        ]);

        $this->actingAs($user)
            ->post(route('admin.manage.stok-opnames.reject', $stokOpname), [
                'reason' => 'Data is invalid',
            ])
            ->assertRedirect(route('admin.manage.stok-opnames.index'));

        $stokOpname->refresh();
        $this->assertEquals(StokOpnameStatus::REJECTED, $stokOpname->status);
        $this->assertEquals('Data is invalid', $stokOpname->verification_notes);
    });
});
