<?php

declare(strict_types=1);

usesApiSuite();

it('lists orders for an admin-scoped user and limits the results to permitted departments', function (): void {
    api_test_covers('GET /orders', 'happy');

    $visibleDepartment = api_fixtures()->createDepartment(['name' => 'Orders List Visible']);
    $hiddenDepartment = api_fixtures()->createDepartment(['name' => 'Orders List Hidden']);
    $visibleCustomer = api_fixtures()->createUser(['display_name' => 'Orders List Visible Customer']);
    $hiddenCustomer = api_fixtures()->createUser(['display_name' => 'Orders List Hidden Customer']);
    $cashier = api_fixtures()->createUser(['display_name' => 'Orders List Cashier']);

    $visibleOrder = api_fixtures()->createOrder([
        'customer_id' => $visibleCustomer['customer_number'],
        'cashier_id' => $cashier['id'],
        'department_id' => $visibleDepartment['id'],
        'reference' => 'VISIBLE-ORDER',
        'reg_1' => 'VISIBLE1',
    ]);
    $hiddenOrder = api_fixtures()->createOrder([
        'customer_id' => $hiddenCustomer['customer_number'],
        'cashier_id' => $cashier['id'],
        'department_id' => $hiddenDepartment['id'],
        'reference' => 'HIDDEN-ORDER',
        'reg_1' => 'HIDDEN1',
    ]);

    $session = api_fixtures()->createUserSession([
        'list_orders',
        'department_access_' . $visibleDepartment['id'],
    ]);

    $response = api_client()->get('/orders', $session['headers']);

    $response
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();

    $orderIds = array_map(
        static fn(array $order): int => (int)($order['id'] ?? 0),
        is_array($response->data()) ? $response->data() : []
    );

    expect($orderIds)
        ->toContain($visibleOrder['id'])
        ->not->toContain($hiddenOrder['id']);
});

it('returns auth and permission failures when order listing is not allowed', function (): void {
    api_test_covers('GET /orders', 'auth');

    api_client()->get('/orders')
        ->assertStatus(400)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMessage('Invalid session');

    $session = api_fixtures()->createUserSession([]);

    api_client()->get('/orders', $session['headers'])
        ->assertStatus(403)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMissingPermissions(['list_own_orders', 'list_orders']);
});

it('creates orders through the orders endpoint', function (): void {
    api_test_covers('POST /orders', 'happy');

    $customer = api_fixtures()->createUser(['display_name' => 'Order Create Customer']);
    $department = api_fixtures()->createDepartment(['name' => 'Order Create Department']);
    $session = api_fixtures()->createUserSession(['add_order', 'department_access_' . $department['id']]);

    $response = api_client()->post('/orders', [
        'customer_id' => $customer['customer_number'],
        'department_id' => $department['id'],
        'reference' => 'ORDER-CREATE',
        'notes' => 'Created through HTTP',
        'reg_1' => ' create-123 ',
        'safety_seal' => 'SEAL-CREATE',
    ], $session['headers']);

    $response
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();

    $orderId = (int)($response->data()['id'] ?? 0);
    expect($orderId)->toBeGreaterThan(0);

    $row = api_fixtures()->fetchRowById('orders', $orderId);

    expect($row)->not->toBeNull();
    expect($row['reference'] ?? null)->toBe('ORDER-CREATE');
    expect($row['reg_1'] ?? null)->toBe('CREATE123');
    expect($row['safety_seal'] ?? null)->toBe('SEAL-CREATE');

    api_fixtures()->cleanupDeleteById('orders', $orderId);
});


it('defaults order PO only from a matching active booking', function (): void {
    api_test_covers('POST /orders', 'security');
    api_test_covers('PUT /orders', 'security');

    $department = api_fixtures()->createDepartment(['name' => 'Order Booking PO Department']);
    $otherDepartment = api_fixtures()->createDepartment(['name' => 'Order Booking PO Other Department']);
    $customer = api_fixtures()->createUser(['display_name' => 'Order Booking PO Customer']);
    $otherCustomer = api_fixtures()->createUser(['display_name' => 'Order Booking PO Other Customer']);
    $cashier = api_fixtures()->createUser(['display_name' => 'Order Booking PO Cashier']);
    $matchingBooking = api_fixtures()->createOrderBooking([
        'customer_number' => $customer['customer_number'],
        'department' => $department['id'],
        'po' => 'MATCHING-BOOKING-PO',
    ]);
    $foreignBooking = api_fixtures()->createOrderBooking([
        'customer_number' => $otherCustomer['customer_number'],
        'department' => $otherDepartment['id'],
        'po' => 'FOREIGN-BOOKING-PO',
    ]);
    $deletedBooking = api_fixtures()->createOrderBooking([
        'customer_number' => $customer['customer_number'],
        'department' => $department['id'],
        'po' => 'DELETED-BOOKING-PO',
        'deleted_at' => date('Y-m-d H:i:s'),
    ]);
    $session = api_fixtures()->createUserSession(['add_order', 'edit_order', 'department_access_' . $department['id']], [
        'customer_number' => $customer['customer_number'],
    ]);

    $createResponse = api_client()->post('/orders', [
        'customer_id' => $customer['customer_number'],
        'department_id' => $department['id'],
        'reference' => 'ORDER-BOOKING-PO-MATCH',
        'notes' => 'Created with matching booking',
        'reg_1' => 'MATCHPO',
        'booking_id' => $matchingBooking['id'],
    ], $session['headers']);

    $createResponse
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();

    $matchingOrderId = (int)($createResponse->data()['id'] ?? 0);
    expect($createResponse->data()['po'] ?? null)->toBe('MATCHING-BOOKING-PO');

    $unauthorizedSession = api_fixtures()->createUserSession(['add_order', 'department_access_' . $department['id']], [
        'customer_number' => $otherCustomer['customer_number'],
    ]);
    $unauthorizedResponse = api_client()->post('/orders', [
        'customer_id' => $customer['customer_number'],
        'department_id' => $department['id'],
        'reference' => 'ORDER-BOOKING-PO-UNAUTHORIZED',
        'notes' => 'Created without booking access',
        'reg_1' => 'NOAUTHPO',
        'booking_id' => $matchingBooking['id'],
    ], $unauthorizedSession['headers']);

    $unauthorizedResponse
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();

    $unauthorizedOrderId = (int)($unauthorizedResponse->data()['id'] ?? 0);
    expect($unauthorizedResponse->data()['po'] ?? null)->toBeNull();

    $foreignResponse = api_client()->post('/orders', [
        'customer_id' => $customer['customer_number'],
        'department_id' => $department['id'],
        'reference' => 'ORDER-BOOKING-PO-FOREIGN',
        'notes' => 'Created with foreign booking',
        'reg_1' => 'FOREIGNPO',
        'booking_id' => $foreignBooking['id'],
    ], $session['headers']);

    $foreignResponse
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();

    $foreignOrderId = (int)($foreignResponse->data()['id'] ?? 0);
    expect($foreignResponse->data()['po'] ?? null)->toBeNull();

    $deletedResponse = api_client()->post('/orders', [
        'customer_id' => $customer['customer_number'],
        'department_id' => $department['id'],
        'reference' => 'ORDER-BOOKING-PO-DELETED',
        'notes' => 'Created with deleted booking',
        'reg_1' => 'DELETEPO',
        'booking_id' => $deletedBooking['id'],
    ], $session['headers']);

    $deletedResponse
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();

    $deletedOrderId = (int)($deletedResponse->data()['id'] ?? 0);
    expect($deletedResponse->data()['po'] ?? null)->toBeNull();

    $existingOrder = api_fixtures()->createOrder([
        'customer_id' => $customer['customer_number'],
        'cashier_id' => $cashier['id'],
        'department_id' => $department['id'],
        'reference' => 'ORDER-BOOKING-PO-UPDATE',
        'reg_1' => 'UPDATEPO',
    ]);

    api_client()->put('/orders', [
        'id' => $existingOrder['id'],
        'booking_id' => $foreignBooking['id'],
    ], $session['headers'])
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();

    $updatedRow = api_fixtures()->fetchRowById('orders', (int)$existingOrder['id']);
    expect($updatedRow['po'] ?? null)->toBeNull();

    api_fixtures()->cleanupDeleteById('orders', $matchingOrderId);
    api_fixtures()->cleanupDeleteById('orders', $unauthorizedOrderId);
    api_fixtures()->cleanupDeleteById('orders', $foreignOrderId);
    api_fixtures()->cleanupDeleteById('orders', $deletedOrderId);
});

it('rejects invalid order creation requests', function (): void {
    api_test_covers('POST /orders', 'failure');

    $customer = api_fixtures()->createUser();
    $department = api_fixtures()->createDepartment();
    $session = api_fixtures()->createUserSession(['add_order', 'department_access_' . $department['id']]);

    api_client()->post('/orders', [
        'customer_id' => $customer['customer_number'],
        'department_id' => $department['id'],
        'notes' => 'Missing reference',
        'reg_1' => 'MISSREF',
    ], $session['headers'])
        ->assertStatus(400)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMessage('Reference is required');
});

it('updates orders through the primary and legacy endpoints', function (): void {
    api_test_covers('PUT /orders', 'happy');
    api_test_covers('PUT /order', 'happy');

    $department = api_fixtures()->createDepartment(['name' => 'Order Update Department']);
    $customer = api_fixtures()->createUser(['display_name' => 'Order Update Customer']);
    $cashier = api_fixtures()->createUser(['display_name' => 'Order Update Cashier']);
    $order = api_fixtures()->createOrder([
        'customer_id' => $customer['customer_number'],
        'cashier_id' => $cashier['id'],
        'department_id' => $department['id'],
        'reference' => 'BEFORE-UPDATE',
        'notes' => 'Before update',
        'reg_1' => 'BEFORE1',
    ]);
    $session = api_fixtures()->createUserSession(['edit_order', 'department_access_' . $department['id']]);

    api_client()->put('/orders', [
        'id' => $order['id'],
        'reference' => 'AFTER-UPDATE',
        'notes' => 'After update',
        'reg_1' => ' after-123 ',
    ], $session['headers'])
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess()
        ->assertMessage('Order updated successfully');

    api_client()->put('/order', [
        'id' => $order['id'],
        'field' => 'reg_2',
        'value' => ' legacy-456 ',
    ], $session['headers'])
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess()
        ->assertMessage('Order updated successfully');

    $row = api_fixtures()->fetchRowById('orders', (int)$order['id']);

    expect($row)->not->toBeNull();
    expect($row['reference'] ?? null)->toBe('AFTER-UPDATE');
    expect($row['notes'] ?? null)->toBe('After update');
    expect($row['reg_1'] ?? null)->toBe('AFTER123');
    expect($row['reg_2'] ?? null)->toBe('LEGACY456');
});

it('rejects invalid order update requests', function (): void {
    api_test_covers('PUT /orders', 'failure');
    api_test_covers('PUT /order', 'failure');

    $session = api_fixtures()->createUserSession(['edit_order']);

    foreach (['/orders', '/order'] as $endpoint) {
        api_client()->put($endpoint, [
            'notes' => 'Missing id',
        ], $session['headers'])
            ->assertStatus(400)
            ->assertEnvelope()
            ->assertSuccess(false)
            ->assertMessage('ID is required');
    }
});

it('deletes orders through the orders endpoint', function (): void {
    api_test_covers('DELETE /orders', 'happy');

    $department = api_fixtures()->createDepartment(['name' => 'Order Delete Department']);
    $customer = api_fixtures()->createUser(['display_name' => 'Order Delete Customer']);
    $cashier = api_fixtures()->createUser(['display_name' => 'Order Delete Cashier']);
    $order = api_fixtures()->createOrder([
        'customer_id' => $customer['customer_number'],
        'cashier_id' => $cashier['id'],
        'department_id' => $department['id'],
    ]);
    $session = api_fixtures()->createUserSession([
        'delete_order',
        'department_access_' . $department['id'],
    ]);

    api_client()->delete('/orders', [
        'id' => $order['id'],
    ], $session['headers'])
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess()
        ->assertMessage('Order deleted successfully');

    $row = api_fixtures()->fetchRowById('orders', (int)$order['id']);
    expect($row)->not->toBeNull();
    expect($row['deleted_at'] ?? null)->not->toBeNull();
});

it('rejects invalid order delete requests', function (): void {
    api_test_covers('DELETE /orders', 'failure');

    $session = api_fixtures()->createUserSession(['delete_order']);

    api_client()->delete('/orders', [], $session['headers'])
        ->assertStatus(400)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMessage('ID is required');
});
