<?php

use App\Models\Branch;
use App\Models\Category;
use App\Models\Item;
use App\Models\Kot;
use App\Models\KotItem;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\RestaurantTable;
use App\Models\TableArea;
use App\Models\User;

use function Pest\Laravel\getJson;
use function Pest\Laravel\postJson;

beforeEach(function () {
    // Set up database environment manually to guarantee valid foreign constraints
    $this->owner = User::factory()->create([
        'role' => 'owner',
    ]);

    $this->branch = Branch::create([
        'user_id' => $this->owner->id,
        'name' => 'Downtown Cafe',
        'slug' => 'downtown-cafe',
        'address' => '456 Elm St',
        'phone' => '0987654321',
        'active' => true,
    ]);

    $this->waiter = User::factory()->create([
        'role' => 'waiter',
        'active_branch_id' => $this->branch->id,
        'parent_id' => $this->owner->id,
        'active' => 1,
    ]);

    $this->token = $this->waiter->createToken('waiter-token')->plainTextToken;

    // Create an area & table
    $this->area = TableArea::create([
        'branch_id' => $this->branch->id,
        'name' => 'Main Dining',
        'is_active' => true,
    ]);

    $this->table = RestaurantTable::create([
        'branch_id' => $this->branch->id,
        'area_id' => $this->area->id,
        'name' => 'Table A1',
        'capacity' => 2,
        'status' => 'available',
        'pos_x' => 10,
        'pos_y' => 20,
        'width' => 50,
        'height' => 50,
        'shape' => 'round',
        'is_active' => true,
    ]);

    // Create a category and item
    $this->category = Category::create([
        'branch_id' => $this->branch->id,
        'name' => 'Appetizers',
        'slug' => 'appetizers',
        'is_active' => true,
    ]);

    $this->item = Item::create([
        'branch_id' => $this->branch->id,
        'category_id' => $this->category->id,
        'name' => 'Garlic Bread',
        'slug' => 'garlic-bread',
        'price' => 5.50,
        'dine_in_price' => 5.50,
        'delivery_price' => 6.00,
        'takeaway_price' => 5.50,
        'gst_percentage' => 5.00,
        'tax_inclusive' => true,
        'available_dine_in' => true,
        'available_delivery' => true,
        'available_takeaway' => true,
        'is_active' => true,
    ]);
});

test('waiter endpoints require authentication', function () {
    getJson(url('api/waiter/tables'))->assertUnauthorized();
    getJson(url('api/waiter/categories'))->assertUnauthorized();
    getJson(url('api/waiter/items'))->assertUnauthorized();
    getJson(url('api/waiter/deals'))->assertUnauthorized();
    getJson(url("api/waiter/order/{$this->table->id}"))->assertUnauthorized();
    postJson(url('api/waiter/order/kot'), [])->assertUnauthorized();
    postJson(url('api/waiter/order/item/serve'), [])->assertUnauthorized();
});

test('non-waiter cannot access waiter endpoints', function () {
    $biller = User::factory()->create([
        'role' => 'biller',
        'active_branch_id' => $this->branch->id,
        'active' => 1,
    ]);
    $billerToken = $biller->createToken('biller-token')->plainTextToken;

    getJson(url('api/waiter/tables'), ['Authorization' => "Bearer $billerToken"])
        ->assertStatus(403);
});

test('waiter can load tables with active statuses and ready status', function () {
    $response = getJson(
        url('api/waiter/tables'),
        ['Authorization' => "Bearer $this->token"]
    );

    $response->assertStatus(200)
        ->assertJsonCount(1)
        ->assertJsonFragment([
            'id' => $this->table->id,
            'name' => 'Table A1',
            'status' => 'available',
            'has_ready_items' => false,
            'order_id' => null,
        ]);
});

test('waiter can retrieve active categories and items', function () {
    // Categories
    getJson(url('api/waiter/categories'), ['Authorization' => "Bearer $this->token"])
        ->assertStatus(200)
        ->assertJsonCount(1)
        ->assertJsonPath('0.name', 'Appetizers');

    // Items
    getJson(url('api/waiter/items'), ['Authorization' => "Bearer $this->token"])
        ->assertStatus(200)
        ->assertJsonCount(1)
        ->assertJsonPath('0.name', 'Garlic Bread');

    // Filter items by category
    getJson(url("api/waiter/items?category_id={$this->category->id}"), ['Authorization' => "Bearer $this->token"])
        ->assertStatus(200)
        ->assertJsonCount(1);

    getJson(url('api/waiter/items?category_id=9999'), ['Authorization' => "Bearer $this->token"])
        ->assertStatus(200)
        ->assertJsonCount(0);
});

test('waiter can submit a new KOT, updating table status to occupied', function () {
    $payload = [
        'table_id' => $this->table->id,
        'subtotal' => 11.00,
        'gst_amount' => 0.55,
        'grand_total' => 11.55,
        'cart' => [
            [
                'id' => $this->item->id,
                'name' => 'Garlic Bread',
                'price' => 5.50,
                'qty' => 2,
            ],
        ],
    ];

    $response = postJson(
        url('api/waiter/order/kot'),
        $payload,
        ['Authorization' => "Bearer $this->token"]
    );

    $response->assertStatus(200)
        ->assertJson(['success' => true]);

    $orderId = $response->json('order_id');
    $kotId = $response->json('kot_id');

    expect($orderId)->not->toBeNull();
    expect($kotId)->not->toBeNull();

    // Check database matches state
    $this->assertDatabaseHas('orders', [
        'id' => $orderId,
        'table_id' => $this->table->id,
        'status' => 'active',
        'grand_total' => 11.55,
    ]);

    $this->assertDatabaseHas('order_items', [
        'order_id' => $orderId,
        'item_id' => $this->item->id,
        'quantity' => 2,
    ]);

    $this->assertDatabaseHas('kots', [
        'id' => $kotId,
        'order_id' => $orderId,
    ]);

    $this->assertDatabaseHas('kot_items', [
        'kot_id' => $kotId,
        'item_id' => $this->item->id,
        'quantity' => 2,
        'status' => 'pending',
    ]);

    $this->table->refresh();
    expect($this->table->status)->toBe('occupied');
});

test('waiter can view active order details for a table', function () {
    $order = Order::create([
        'branch_id' => $this->branch->id,
        'table_id' => $this->table->id,
        'status' => 'active',
        'subtotal' => 11.00,
        'gst_amount' => 0.55,
        'grand_total' => 11.55,
    ]);

    $response = getJson(
        url("api/waiter/order/{$this->table->id}"),
        ['Authorization' => "Bearer $this->token"]
    );

    $response->assertStatus(200)
        ->assertJsonPath('id', $order->id)
        ->assertJsonPath('status', 'active');
});

test('waiter cannot view table active order details from another branch', function () {
    $otherBranch = Branch::create([
        'user_id' => $this->owner->id,
        'name' => 'Other Branch',
        'slug' => 'other-branch',
        'address' => '789 Oak St',
        'phone' => '1122334455',
        'active' => true,
    ]);

    $otherArea = TableArea::create([
        'branch_id' => $otherBranch->id,
        'name' => 'Outer Deck',
        'is_active' => true,
    ]);

    $otherTable = RestaurantTable::create([
        'branch_id' => $otherBranch->id,
        'area_id' => $otherArea->id,
        'name' => 'Table Z9',
        'capacity' => 4,
        'status' => 'available',
        'pos_x' => 0,
        'pos_y' => 0,
        'width' => 10,
        'height' => 10,
        'shape' => 'square',
        'is_active' => true,
    ]);

    getJson(
        url("api/waiter/order/{$otherTable->id}"),
        ['Authorization' => "Bearer $this->token"]
    )->assertStatus(403);
});

test('waiter can submit incremental quantities to subsequent KOTs', function () {
    // 1st order submission (Garlic Bread qty = 2)
    $payload = [
        'table_id' => $this->table->id,
        'subtotal' => 11.00,
        'gst_amount' => 0.55,
        'grand_total' => 11.55,
        'cart' => [
            [
                'id' => $this->item->id,
                'name' => 'Garlic Bread',
                'price' => 5.50,
                'qty' => 2,
            ],
        ],
    ];

    $response1 = postJson(
        url('api/waiter/order/kot'),
        $payload,
        ['Authorization' => "Bearer $this->token"]
    );
    $orderId = $response1->json('order_id');
    $kotId1 = $response1->json('kot_id');

    // Increase total Garlic Bread quantity to 5
    $payload['cart'][0]['qty'] = 5;
    $payload['subtotal'] = 27.50;
    $payload['gst_amount'] = 1.38;
    $payload['grand_total'] = 28.88;

    $response2 = postJson(
        url('api/waiter/order/kot'),
        $payload,
        ['Authorization' => "Bearer $this->token"]
    );

    $response2->assertStatus(200);
    $kotId2 = $response2->json('kot_id');

    expect($kotId2)->not->toBeNull();
    expect($kotId2)->not->toBe($kotId1);

    // KOT 2 should only fire the incremental quantity of 3 (5 - 2)
    $this->assertDatabaseHas('kot_items', [
        'kot_id' => $kotId2,
        'item_id' => $this->item->id,
        'quantity' => 3,
    ]);
});

test('waiter can mark ready items as served', function () {
    $order = Order::create([
        'branch_id' => $this->branch->id,
        'table_id' => $this->table->id,
        'status' => 'active',
        'subtotal' => 5.50,
        'gst_amount' => 0.28,
        'grand_total' => 5.78,
    ]);

    $orderItem = OrderItem::create([
        'order_id' => $order->id,
        'item_id' => $this->item->id,
        'name' => 'Garlic Bread',
        'price' => 5.50,
        'quantity' => 1,
        'subtotal' => 5.50,
    ]);

    $kot = Kot::create([
        'branch_id' => $this->branch->id,
        'order_id' => $order->id,
        'status' => 'ready',
    ]);

    $kotItem = KotItem::create([
        'kot_id' => $kot->id,
        'order_item_id' => $orderItem->id,
        'item_id' => $this->item->id,
        'quantity' => 1,
        'status' => 'ready',
    ]);

    $response = postJson(
        url('api/waiter/order/item/serve'),
        ['order_item_id' => $orderItem->id],
        ['Authorization' => "Bearer $this->token"]
    );

    $response->assertStatus(200)
        ->assertJson(['success' => true]);

    $kotItem->refresh();
    expect($kotItem->status)->toBe('served');
});
