<?php

declare(strict_types=1);

usesApiSuite();

function products_api_department_price(int $departmentId, int $productId, int $price): void
{
    $statement = api_test_runtime()->db()->prepare(
        'INSERT INTO `product_department_prices` (`department_id`, `product_id`, `price`)
         VALUES (?, ?, ?)
         ON DUPLICATE KEY UPDATE `price` = VALUES(`price`)'
    );
    $statement->bind_param('iii', $departmentId, $productId, $price);
    $statement->execute();
    $statement->close();

    api_fixtures()->cleanupDeleteWhere('product_department_prices', [
        'department_id' => $departmentId,
        'product_id' => $productId,
    ]);
}

it('treats null-like optional product params as omitted for product detail requests', function (): void {
    api_test_covers('GET /products', 'optional-params');

    $product = api_fixtures()->createProduct([
        'name' => 'Null Query Product',
        'price' => 400,
    ]);
    $session = api_fixtures()->createUserSession([], ['group_id' => 1]);

    $response = api_client()->get(
        '/products?id=' . (int)$product['id']
        . '&department_id=null&customer_id=null&category_id=null&final_price=false',
        $session['headers']
    );

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

    expect($response->data())
        ->toBeArray()
        ->toHaveKey('id', (int)$product['id']);
    expect($response->body)->not->toContain('department_access_0');
});

it('returns final department pricing without requiring list_products permission', function (): void {
    api_test_covers('GET /products', 'customer-booking');

    $department = api_fixtures()->createDepartment(['name' => 'Product Pricing Department']);
    $product = api_fixtures()->createProduct([
        'name' => 'Department Priced Product',
        'price' => 500,
    ]);
    products_api_department_price((int)$department['id'], (int)$product['id'], 375);
    $session = api_fixtures()->createUserSession(['user']);

    $response = api_client()->get(
        '/products?final_price=true&id=' . (int)$product['id']
        . '&department_id=' . (int)$department['id'],
        $session['headers']
    );

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

    expect($response->data())
        ->toBeArray()
        ->toHaveKey('id', (int)$product['id'])
        ->toHaveKey('price', 375);
    expect($response->body)->not->toContain('department_access_' . (int)$department['id']);
});

it('lets customer booking sessions read own single-product final pricing with customer context', function (): void {
    api_test_covers('GET /products', 'customer-booking');

    $department = api_fixtures()->createDepartment(['name' => 'Single Customer Product Pricing Department']);
    $product = api_fixtures()->createProduct([
        'name' => 'Single Customer Priced Product',
        'price' => 0,
        'display_in_booking_form' => 0,
    ]);
    products_api_department_price((int)$department['id'], (int)$product['id'], 175);
    $session = api_fixtures()->createUserSession(['user']);

    $response = api_client()->get(
        '/products?final_price=true&id=' . (int)$product['id']
        . '&department_id=' . (int)$department['id']
        . '&customer_id=' . (int)$session['user']['customer_number'],
        $session['headers']
    );

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

    expect($response->data())
        ->toBeArray()
        ->toHaveKey('id', (int)$product['id'])
        ->toHaveKey('price', 175);
    expect($response->body)->not->toContain('department_access_' . (int)$department['id']);
});

it('lets customer booking sessions list public products with final department pricing', function (): void {
    api_test_covers('GET /products', 'customer-booking');

    $department = api_fixtures()->createDepartment(['name' => 'Customer Booking Products Department']);
    $product = api_fixtures()->createProduct([
        'name' => 'Customer Booking Visible Wash',
        'price' => 700,
        'is_wash' => 1,
        'display_in_booking_form' => 1,
    ]);
    products_api_department_price((int)$department['id'], (int)$product['id'], 650);
    $hiddenProduct = api_fixtures()->createProduct([
        'name' => 'Customer Booking Hidden Wash',
        'price' => 900,
        'is_wash' => 1,
        'display_in_booking_form' => 0,
    ]);
    $session = api_fixtures()->createUserSession(['user']);

    $response = api_client()->get(
        '/products?department_id=' . (int)$department['id'] . '&final_price=true',
        $session['headers']
    );

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

    $productsById = [];
    foreach ($response->data() as $returnedProduct) {
        $productsById[(int)($returnedProduct['id'] ?? 0)] = $returnedProduct;
    }

    expect($productsById)
        ->toHaveKey((int)$product['id'])
        ->and($productsById[(int)$product['id']]['name'] ?? null)->toBe('Customer Booking Visible Wash')
        ->and((int)($productsById[(int)$product['id']]['price'] ?? 0))->toBe(650)
        ->and($productsById[(int)$product['id']]['display_in_booking_form'] ?? null)->toBeTrue();
    expect($productsById)->not->toHaveKey((int)$hiddenProduct['id']);

    expect($response->body)
        ->not->toContain('list_products')
        ->not->toContain('department_access_' . (int)$department['id']);
});

it('lets customer booking sessions with list_products read own final department pricing without department access', function (): void {
    api_test_covers('GET /products', 'customer-booking-auth');

    $department = api_fixtures()->createDepartment(['name' => 'Own Customer Booking Pricing Department']);
    $product = api_fixtures()->createProduct([
        'name' => 'Own Customer Booking Wash',
        'price' => 800,
        'is_wash' => 1,
        'display_in_booking_form' => 1,
    ]);
    products_api_department_price((int)$department['id'], (int)$product['id'], 725);
    $session = api_fixtures()->createUserSession(['user', 'list_products']);

    $response = api_client()->get(
        '/products?department_id=' . (int)$department['id']
        . '&final_price=true'
        . '&customer_id=' . (int)$session['user']['customer_number'],
        $session['headers']
    );

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

    $productsById = [];
    foreach ($response->data() as $returnedProduct) {
        $productsById[(int)($returnedProduct['id'] ?? 0)] = $returnedProduct;
    }

    expect($productsById)
        ->toHaveKey((int)$product['id'])
        ->and((int)($productsById[(int)$product['id']]['price'] ?? 0))->toBe(725);
    expect($response->body)
        ->not->toContain('department_access_' . (int)$department['id']);
});

it('still requires department access when list_products users request another customer final department pricing', function (): void {
    api_test_covers('GET /products', 'customer-booking-auth');

    $department = api_fixtures()->createDepartment(['name' => 'Other Customer Pricing Department']);
    $otherCustomer = api_fixtures()->createUser();
    $session = api_fixtures()->createUserSession(['user', 'list_products']);

    api_client()->get(
        '/products?department_id=' . (int)$department['id']
        . '&final_price=true'
        . '&customer_id=' . (int)$otherCustomer['customer_number'],
        $session['headers']
    )
        ->assertStatus(403)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMissingPermissions(['department_access_' . (int)$department['id']]);
});

it('prevents customer booking sessions from requesting another customer product pricing', function (): void {
    api_test_covers('GET /products', 'customer-booking-auth');

    $session = api_fixtures()->createUserSession(['user']);
    $otherCustomer = api_fixtures()->createUser();

    api_client()->get(
        '/products?final_price=true&customer_id=' . (int)$otherCustomer['customer_number'],
        $session['headers']
    )
        ->assertStatus(403)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMissingPermissions(['list_products']);
});

it('rejects invalid department ids without requesting department access zero', function (): void {
    api_test_covers('GET /products', 'validation');

    $product = api_fixtures()->createProduct([
        'name' => 'Invalid Department Product',
        'price' => 600,
    ]);
    $session = api_fixtures()->createUserSession(['user']);

    $response = api_client()->get(
        '/products?final_price=true&id=' . (int)$product['id'] . '&department_id=0',
        $session['headers']
    );

    $response
        ->assertStatus(400)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMessage('Invalid department_id');

    expect($response->body)->not->toContain('department_access_0');
});
