<?php

namespace Tests\Feature;

use App\Models\Category;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Product;
use App\Models\StockMovement;
use App\Models\StockReservation;
use App\Models\User;
use App\Models\VariantStockMovement;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class AdminOrderCancelRestockTest extends TestCase
{
    use RefreshDatabase;

    public function test_delivered_order_cancelled_by_admin_restock_products(): void
    {
        $admin = $this->createUser('admin');
        [$product, $order] = $this->createDeliveredOrderWithSale($admin, 3);

        $this->actingAs($admin)
            ->put(route('admin.orders.update-status', $order), ['status' => Order::STATUS_CANCELLED])
            ->assertRedirect()
            ->assertSessionHas('success');

        $order->refresh();
        $this->assertSame(Order::STATUS_CANCELLED, $order->status);

        $product->refresh();
        $this->assertSame(3, $product->current_stock);

        $this->assertDatabaseHas('product_stock_movements', [
            'product_id' => $product->id,
            'type' => StockMovement::TYPE_RESTOCK,
            'quantity' => 3,
            'notes' => "Order #{$order->id} cancelled",
        ]);
    }

    public function test_cancel_restock_is_idempotent(): void
    {
        $admin = $this->createUser('admin');
        [$product, $order] = $this->createDeliveredOrderWithSale($admin, 2);

        $this->actingAs($admin)
            ->put(route('admin.orders.update-status', $order), ['status' => Order::STATUS_CANCELLED])
            ->assertSessionHas('success');

        $restockCount = StockMovement::where('product_id', $product->id)
            ->where('type', StockMovement::TYPE_RESTOCK)
            ->where('notes', "Order #{$order->id} cancelled")
            ->count();

        $this->assertSame(1, $restockCount);
        $product->refresh();
        $this->assertSame(2, $product->current_stock);
    }

    public function test_shipped_order_returned_by_admin_restock_products(): void
    {
        $admin = $this->createUser('admin');
        [$product, $order] = $this->createOrderWithSale($admin, Order::STATUS_SHIPPED, 4);

        $this->actingAs($admin)
            ->put(route('admin.orders.update-status', $order), ['status' => Order::STATUS_RETURNED])
            ->assertRedirect()
            ->assertSessionHas('success');

        $order->refresh();
        $this->assertSame(Order::STATUS_RETURNED, $order->status);

        $product->refresh();
        $this->assertSame(4, $product->current_stock);

        $this->assertDatabaseHas('product_stock_movements', [
            'product_id' => $product->id,
            'type' => StockMovement::TYPE_RESTOCK,
            'quantity' => 4,
            'notes' => "Order #{$order->id} returned",
        ]);
    }

    public function test_awaiting_payment_cancelled_releases_reservation_without_restock(): void
    {
        $admin = $this->createUser('admin');
        $customer = $this->createUser('customer');
        $category = Category::factory()->create();
        $product = Product::factory()->create([
            'category_id' => $category->id,
            'product_type' => 'single',
            'current_stock' => 10,
        ]);

        $order = Order::create([
            'user_id' => $customer->id,
            'source' => 'online',
            'address_snapshot' => ['name' => 'Test', 'pin' => '600001'],
            'subtotal' => 500,
            'discount_total' => 0,
            'tax_total' => 0,
            'shipping_total' => 0,
            'total_payable' => 500,
            'payment_status' => Order::PAYMENT_PENDING,
            'payment_method' => 'online',
            'status' => Order::STATUS_AWAITING_PAYMENT,
        ]);

        OrderItem::create([
            'order_id' => $order->id,
            'product_id' => $product->id,
            'name' => $product->name,
            'sku' => $product->sku ?? 'SKU-1',
            'qty' => 2,
            'price' => 250,
            'line_total' => 500,
        ]);

        $reservation = StockReservation::create([
            'product_id' => $product->id,
            'order_id' => $order->id,
            'quantity' => 2,
            'reservation_token' => 'test-reservation-token',
            'expires_at' => now()->addMinutes(15),
            'status' => 'pending',
        ]);

        $this->actingAs($admin)
            ->put(route('admin.orders.update-status', $order), ['status' => Order::STATUS_CANCELLED])
            ->assertRedirect()
            ->assertSessionHas('success');

        $reservation->refresh();
        $this->assertSame('released', $reservation->status);

        $this->assertDatabaseMissing('product_stock_movements', [
            'product_id' => $product->id,
            'type' => StockMovement::TYPE_RESTOCK,
            'notes' => "Order #{$order->id} cancelled",
        ]);
    }

    public function test_pos_sale_note_pattern_is_restocked_on_cancel(): void
    {
        $admin = $this->createUser('admin');
        $customer = $this->createUser('customer');
        $category = Category::factory()->create();
        $parent = Product::factory()->create([
            'category_id' => $category->id,
            'product_type' => 'variant',
            'current_stock' => 0,
        ]);
        $product = Product::factory()->create([
            'category_id' => $category->id,
            'product_type' => 'variant',
            'parent_id' => $parent->id,
            'current_stock' => 0,
        ]);

        $order = Order::create([
            'user_id' => $customer->id,
            'source' => 'pos',
            'address_snapshot' => ['name' => 'POS Customer', 'pin' => '600001'],
            'subtotal' => 1000,
            'discount_total' => 0,
            'tax_total' => 0,
            'shipping_total' => 0,
            'total_payable' => 1000,
            'payment_status' => Order::PAYMENT_PAID,
            'payment_method' => 'cash',
            'status' => Order::STATUS_DELIVERED,
            'placed_at' => now(),
        ]);

        OrderItem::create([
            'order_id' => $order->id,
            'product_id' => $product->id,
            'name' => $product->name,
            'sku' => $product->sku ?? 'SKU-POS',
            'qty' => 5,
            'price' => 200,
            'line_total' => 1000,
        ]);

        VariantStockMovement::create([
            'product_id' => $product->id,
            'type' => VariantStockMovement::TYPE_SALE,
            'quantity' => -5,
            'notes' => "POS Order #{$order->id}",
            'created_by' => $admin->id,
        ]);
        $product->recalculateStock();

        $this->actingAs($admin)
            ->put(route('admin.orders.update-status', $order), ['status' => Order::STATUS_CANCELLED])
            ->assertSessionHas('success');

        $product->refresh();
        $this->assertSame(5, $product->current_stock);

        $this->assertDatabaseHas('variant_stock_movements', [
            'product_id' => $product->id,
            'type' => VariantStockMovement::TYPE_RESTOCK,
            'quantity' => 5,
            'notes' => "Order #{$order->id} cancelled",
        ]);
    }

    /**
     * @return array{0: Product, 1: Order}
     */
    private function createDeliveredOrderWithSale(User $admin, int $qty): array
    {
        return $this->createOrderWithSale($admin, Order::STATUS_DELIVERED, $qty);
    }

    /**
     * @return array{0: Product, 1: Order}
     */
    private function createOrderWithSale(User $admin, string $status, int $qty): array
    {
        $customer = $this->createUser('customer');
        $category = Category::factory()->create();
        $product = Product::factory()->create([
            'category_id' => $category->id,
            'product_type' => 'single',
            'current_stock' => 0,
        ]);

        $order = Order::create([
            'user_id' => $customer->id,
            'source' => 'online',
            'address_snapshot' => ['name' => 'Test', 'pin' => '600001'],
            'subtotal' => 1000,
            'discount_total' => 0,
            'tax_total' => 0,
            'shipping_total' => 0,
            'total_payable' => 1000,
            'payment_status' => Order::PAYMENT_PAID,
            'payment_method' => 'online',
            'status' => $status,
            'placed_at' => now(),
        ]);

        OrderItem::create([
            'order_id' => $order->id,
            'product_id' => $product->id,
            'name' => $product->name,
            'sku' => $product->sku ?? 'SKU-1',
            'qty' => $qty,
            'price' => 1000 / max($qty, 1),
            'line_total' => 1000,
        ]);

        StockMovement::create([
            'product_id' => $product->id,
            'type' => StockMovement::TYPE_SALE,
            'quantity' => -$qty,
            'notes' => "Order #{$order->id}",
            'created_by' => $admin->id,
        ]);
        $product->recalculateStock();

        return [$product, $order];
    }

    private function createUser(string $type): User
    {
        static $counter = 1;

        $user = User::create([
            'name' => ucfirst($type) . ' User ' . $counter,
            'mobile' => '910000' . str_pad((string) $counter, 4, '0', STR_PAD_LEFT),
            'email' => strtolower($type) . $counter . '@example.com',
            'password' => bcrypt('password'),
            'user_type' => $type,
        ]);

        $counter++;

        return $user;
    }
}
