<?php

use App\Enums\OrderChannel;
use App\Enums\OrderStatus;
use App\Enums\PaymentType;
use App\Enums\Permission as PermissionEnum;
use App\Enums\PriceType;
use App\Models\Customer;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Product;
use App\Models\ProductPrice;
use App\Models\ProductVariant;
use App\Models\User;
use Database\Seeders\CashAccountSeeder;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Storage;

uses(RefreshDatabase::class);

beforeEach(function () {
    $this->seed(RolePermissionSeeder::class);
    User::factory()->create();
    $this->seed(CashAccountSeeder::class);
    Storage::fake('public');
});

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

function createOrderUserWithPermission(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 createOrderWithItems(?User $user = null): Order
{
    $user ??= User::factory()->create();

    $order = Order::factory()->create([
        'created_by_id' => $user->id,
        'channel' => OrderChannel::STORE->value,
        'price_type' => PriceType::RETAIL->value,
        'payment_type' => PaymentType::CASH->value,
    ]);

    $product = Product::factory()->create();
    $variant = ProductVariant::factory()->create([
        'product_id' => $product->id,
        'stock' => 100,
    ]);

    OrderItem::factory()->create([
        'order_id' => $order->id,
        'product_variant_id' => $variant->id,
        'quantity' => 5,
        'unit_price' => 50000,
        'subtotal' => 250000,
    ]);

    return $order;
}

function setupOrderDraftItems(User $user): ProductVariant
{
    $product = Product::factory()->create();
    $variant = ProductVariant::factory()->create([
        'product_id' => $product->id,
        'stock' => 100,
    ]);

    // Create product price so CuttingResultPriceResolver can resolve it
    ProductPrice::factory()->create([
        'variant_id' => $variant->id,
        'type' => PriceType::RETAIL,
        'price' => 50000,
    ]);

    // Create draft item via API
    test()->actingAs($user)
        ->postJson(route('admin.manage.orders.draft_items.store'), [
            'product_variant_id' => $variant->id,
            'price_type' => PriceType::RETAIL->value,
            'stock_quality' => 'good',
            'quantity' => 5,
        ])
        ->assertOk();

    return $variant;
}

// ─── Index ────────────────────────────────────────────────

describe('Order Index', function () {
    test('authenticated user with permission can view order index', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW);

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

    test('guest is redirected to login', function () {
        $this->get(route('admin.manage.orders.index'))
            ->assertRedirect(route('login'));
    });

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

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

    test('index displays orders', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW);

        createOrderWithItems($user);
        createOrderWithItems($user);

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

    test('index can search orders by order number', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW);

        $order = Order::factory()->create([
            'created_by_id' => $user->id,
            'order_number' => 'ORD-20260704-TEST',
        ]);

        $this->actingAs($user)
            ->get(route('admin.manage.orders.index', ['search' => 'TEST']))
            ->assertOk();
    });
});

// ─── Create ───────────────────────────────────────────────

describe('Order Create', function () {
    test('authenticated user with permission can view create form', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW, PermissionEnum::ORDERS_CREATE);

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

    test('guest is redirected to login', function () {
        $this->get(route('admin.manage.orders.create'))
            ->assertRedirect(route('login'));
    });

    test('user without create permission is forbidden', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW);

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

// ─── Store ────────────────────────────────────────────────

describe('Order Store', function () {
    test('authenticated user with permission can create an order', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW, PermissionEnum::ORDERS_CREATE);

        $customer = Customer::factory()->create();
        setupOrderDraftItems($user);

        $this->actingAs($user)
            ->post(route('admin.manage.orders.store'), [
                'customer_id' => $customer->id,
                'channel' => OrderChannel::STORE->value,
                'price_type' => PriceType::RETAIL->value,
                'payment_type' => PaymentType::CASH->value,
                'discount' => 0,
            ])
            ->assertRedirect(route('admin.manage.orders.index'));

        $order = Order::query()->latest()->first();
        expect($order)->not->toBeNull();
        expect($order->status)->toBe(OrderStatus::PENDING);
        expect($order->items)->toHaveCount(1);
    });

    test('guest cannot create an order', function () {
        $this->post(route('admin.manage.orders.store'), [
            'channel' => OrderChannel::STORE->value,
            'price_type' => PriceType::RETAIL->value,
            'payment_type' => PaymentType::CASH->value,
        ])->assertRedirect(route('login'));
    });

    test('user without create permission is forbidden', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW);

        $this->actingAs($user)
            ->post(route('admin.manage.orders.store'), [
                'channel' => OrderChannel::STORE->value,
                'price_type' => PriceType::RETAIL->value,
                'payment_type' => PaymentType::CASH->value,
            ])
            ->assertForbidden();
    });

    test('store fails without draft items', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW, PermissionEnum::ORDERS_CREATE);

        $this->actingAs($user)
            ->post(route('admin.manage.orders.store'), [
                'channel' => OrderChannel::STORE->value,
                'price_type' => PriceType::RETAIL->value,
                'payment_type' => PaymentType::CASH->value,
                'discount' => 0,
            ])
            ->assertSessionHasErrors('items');
    });

    test('creating order decrements stock', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW, PermissionEnum::ORDERS_CREATE);

        $variant = setupOrderDraftItems($user);
        $initialStock = $variant->stock;

        $this->actingAs($user)
            ->post(route('admin.manage.orders.store'), [
                'channel' => OrderChannel::STORE->value,
                'price_type' => PriceType::RETAIL->value,
                'payment_type' => PaymentType::CASH->value,
                'discount' => 0,
            ]);

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

    test('cashier creates order automatically sets status to completed and unsets marketing and customer', function () {
        $user = User::factory()->create();
        $user->assignRole('cashier');
        $user->givePermissionTo(PermissionEnum::ORDERS_CREATE->value);
        $user->forgetCachedPermissions();

        setupOrderDraftItems($user);

        $marketing = User::factory()->create();
        $customer = Customer::factory()->create();

        $this->actingAs($user)
            ->post(route('admin.manage.orders.store'), [
                'customer_id' => $customer->id,
                'marketing_id' => $marketing->id,
                'channel' => OrderChannel::STORE->value,
                'price_type' => PriceType::RETAIL->value,
                'payment_type' => PaymentType::CASH->value,
                'status' => OrderStatus::PENDING->value,
                'discount' => 0,
            ])
            ->assertRedirect(route('admin.manage.orders.index'));

        $order = Order::query()->latest()->first();
        expect($order)->not->toBeNull();
        expect($order->status)->toBe(OrderStatus::COMPLETED);
        expect($order->marketing_id)->toBeNull();
        expect($order->customer_id)->toBeNull();
    });
});

// ─── Show ─────────────────────────────────────────────────

describe('Order Show', function () {
    test('authenticated user with permission can view order details', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW);

        $order = createOrderWithItems($user);

        $this->actingAs($user)
            ->get(route('admin.manage.orders.show', $order))
            ->assertOk();
    });

    test('guest is redirected to login', function () {
        $order = createOrderWithItems();

        $this->get(route('admin.manage.orders.show', $order))
            ->assertRedirect(route('login'));
    });
});

// ─── Edit ─────────────────────────────────────────────────

describe('Order Edit', function () {
    test('authenticated user with permission can view edit form', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW, PermissionEnum::ORDERS_UPDATE);

        $order = createOrderWithItems($user);

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

    test('guest is redirected to login', function () {
        $order = createOrderWithItems();

        $this->get(route('admin.manage.orders.edit', $order))
            ->assertRedirect(route('login'));
    });

    test('user without update permission is forbidden', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW);

        $order = createOrderWithItems($user);

        $this->actingAs($user)
            ->get(route('admin.manage.orders.edit', $order))
            ->assertForbidden();
    });

    test('completed order cannot be edited', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW, PermissionEnum::ORDERS_UPDATE);

        $order = Order::factory()->completed()->create(['created_by_id' => $user->id]);

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

// ─── Destroy ──────────────────────────────────────────────

describe('Order Destroy', function () {
    test('authenticated user with permission can delete an order', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW, PermissionEnum::ORDERS_DELETE);

        $order = createOrderWithItems($user);

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

        $this->assertSoftDeleted('orders', ['id' => $order->id]);
    });

    test('guest cannot delete an order', function () {
        $order = createOrderWithItems();

        $this->delete(route('admin.manage.orders.destroy', $order))
            ->assertRedirect(route('login'));

        $this->assertNotSoftDeleted('orders', ['id' => $order->id]);
    });

    test('user without delete permission is forbidden', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW);

        $order = createOrderWithItems($user);

        $this->actingAs($user)
            ->delete(route('admin.manage.orders.destroy', $order))
            ->assertForbidden();

        $this->assertNotSoftDeleted('orders', ['id' => $order->id]);
    });

    test('deleting order increments stock back', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW, PermissionEnum::ORDERS_DELETE);

        $order = createOrderWithItems($user);
        $item = $order->items->first();
        $variant = $item->productVariant;
        $stockBeforeDelete = $variant->fresh()->stock;

        $this->actingAs($user)
            ->delete(route('admin.manage.orders.destroy', $order));

        expect($variant->fresh()->stock)->toBe($stockBeforeDelete + $item->quantity);
    });
});

// ─── Status Transition ────────────────────────────────────

describe('Order Status Transition', function () {
    test('authenticated user can send an order', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW, PermissionEnum::ORDERS_SEND);

        $order = Order::factory()->create([
            'created_by_id' => $user->id,
            'status' => OrderStatus::PENDING->value,
        ]);

        $this->actingAs($user)
            ->post(route('admin.manage.orders.transition_status', $order), [
                'status' => OrderStatus::PROCESSING->value,
            ])
            ->assertRedirect(route('admin.manage.orders.index'));

        expect($order->fresh()->status)->toBe(OrderStatus::PROCESSING);
    });

    test('authenticated user can complete an order', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW, PermissionEnum::ORDERS_COMPLETE);

        $order = Order::factory()->create([
            'created_by_id' => $user->id,
            'status' => OrderStatus::PROCESSING->value,
        ]);

        $this->actingAs($user)
            ->post(route('admin.manage.orders.transition_status', $order), [
                'status' => OrderStatus::COMPLETED->value,
            ]);

        expect($order->fresh()->status)->toBe(OrderStatus::COMPLETED);
    });

    test('authenticated user can cancel an order', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW, PermissionEnum::ORDERS_CANCEL);

        $order = Order::factory()->create([
            'created_by_id' => $user->id,
            'status' => OrderStatus::PENDING->value,
        ]);

        $this->actingAs($user)
            ->post(route('admin.manage.orders.transition_status', $order), [
                'status' => OrderStatus::CANCELLED->value,
            ]);

        expect($order->fresh()->status)->toBe(OrderStatus::CANCELLED);
    });

    test('guest cannot transition status', function () {
        $order = Order::factory()->create(['status' => OrderStatus::PENDING->value]);

        $this->post(route('admin.manage.orders.transition_status', $order), [
            'status' => OrderStatus::PROCESSING->value,
        ])->assertRedirect(route('login'));
    });

    test('invalid status transition is rejected', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW, PermissionEnum::ORDERS_COMPLETE);

        $order = Order::factory()->create([
            'created_by_id' => $user->id,
            'status' => OrderStatus::PENDING->value,
        ]);

        // PENDING cannot go directly to COMPLETED
        $this->actingAs($user)
            ->post(route('admin.manage.orders.transition_status', $order), [
                'status' => OrderStatus::COMPLETED->value,
            ])
            ->assertSessionHasErrors('status');
    });

    test('cancelling order increments stock back', function () {
        $user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW, PermissionEnum::ORDERS_CANCEL);

        $order = createOrderWithItems($user);
        $item = $order->items->first();
        $variant = $item->productVariant;
        $stockBeforeCancel = $variant->fresh()->stock;

        $this->actingAs($user)
            ->post(route('admin.manage.orders.transition_status', $order), [
                'status' => OrderStatus::CANCELLED->value,
            ]);

        expect($variant->fresh()->stock)->toBe($stockBeforeCancel + $item->quantity);
    });
});

// ─── Order Model ──────────────────────────────────────────

describe('Order Model', function () {
    test('order uses soft deletes', function () {
        $order = Order::factory()->create();

        $order->delete();

        expect($order->trashed())->toBeTrue();
    });

    test('order has amounts cast to integer', function () {
        $order = Order::factory()->create([
            'subtotal' => 100000,
            'discount' => 5000,
            'total_amount' => 95000,
        ]);

        expect($order->subtotal)->toBeInt();
        expect($order->discount)->toBeInt();
        expect($order->total_amount)->toBeInt();
    });

    test('order has formatted amount accessors', function () {
        $order = Order::factory()->create([
            'subtotal' => 100000,
            'discount' => 5000,
            'total_amount' => 95000,
        ]);

        expect($order->subtotal_formatted)->toBe('Rp 100.000');
        expect($order->discount_formatted)->toBe('Rp 5.000');
        expect($order->total_amount_formatted)->toBe('Rp 95.000');
    });

    test('order has status cast to enum', function () {
        $order = Order::factory()->create(['status' => OrderStatus::PENDING->value]);

        expect($order->status)->toBe(OrderStatus::PENDING);
    });

    test('order has status label accessor', function () {
        $order = Order::factory()->create(['status' => OrderStatus::PENDING->value]);

        expect($order->status_label)->toBe('Menunggu');
    });

    test('order belongs to customer', function () {
        $customer = Customer::factory()->create();
        $order = Order::factory()->create(['customer_id' => $customer->id]);

        expect($order->customer)->not->toBeNull();
        expect($order->customer->id)->toBe($customer->id);
    });

    test('order belongs to created by user', function () {
        $user = User::factory()->create();
        $order = Order::factory()->create(['created_by_id' => $user->id]);

        expect($order->createdBy)->not->toBeNull();
        expect($order->createdBy->id)->toBe($user->id);
    });

    test('order can have items', function () {
        $order = Order::factory()->create();
        OrderItem::factory()->count(3)->create(['order_id' => $order->id]);

        expect($order->fresh()->items)->toHaveCount(3);
    });

    test('order status is editable for pending and processing', function () {
        $pending = Order::factory()->create(['status' => OrderStatus::PENDING->value]);
        $processing = Order::factory()->create(['status' => OrderStatus::PROCESSING->value]);

        expect($pending->status->isEditable())->toBeTrue();
        expect($processing->status->isEditable())->toBeTrue();
    });
});

// ─── OrderItem Model ──────────────────────────────────────

describe('OrderItem Model', function () {
    test('order item has amounts cast to integer', function () {
        $order = Order::factory()->create();
        $item = OrderItem::factory()->create([
            'order_id' => $order->id,
            'quantity' => 10,
            'unit_price' => 50000,
            'subtotal' => 500000,
        ]);

        expect($item->quantity)->toBeInt();
        expect($item->unit_price)->toBeInt();
        expect($item->subtotal)->toBeInt();
    });

    test('order item has formatted amount accessors', function () {
        $order = Order::factory()->create();
        $item = OrderItem::factory()->create([
            'order_id' => $order->id,
            'quantity' => 10,
            'unit_price' => 50000,
            'subtotal' => 500000,
        ]);

        expect($item->quantity_formatted)->toBe('10 pcs');
        expect($item->unit_price_formatted)->toBe('Rp 50.000');
        expect($item->subtotal_formatted)->toBe('Rp 500.000');
    });

    test('order item belongs to order', function () {
        $order = Order::factory()->create();
        $item = OrderItem::factory()->create(['order_id' => $order->id]);

        expect($item->order)->not->toBeNull();
        expect($item->order->id)->toBe($order->id);
    });

    test('order item uses soft deletes', function () {
        $order = Order::factory()->create();
        $item = OrderItem::factory()->create(['order_id' => $order->id]);

        $item->delete();

        expect($item->trashed())->toBeTrue();
    });
});
