<?php

declare(strict_types=1);

usesApiSuite();

it('lists only visible departments and can return a single department with the slack webhook', function (): void {
    api_test_covers('GET /departments', 'happy');

    $session = api_fixtures()->createUserSession([
        'list_departments',
        'view_slack_webhook',
    ]);

    $visibleDepartment = api_fixtures()->createDepartment([
        'name' => 'Visible Department',
        'visible' => 1,
    ]);
    $hiddenDepartment = api_fixtures()->createDepartment([
        'name' => 'Hidden Department',
        'visible' => 0,
    ]);
    $archivedDepartment = api_fixtures()->createDepartment([
        'name' => 'Archived Department',
        'visible' => 1,
        'archived' => 1,
    ]);
    $webhookDepartment = api_fixtures()->createDepartment([
        'name' => 'Webhook Department',
        'slack_webhook' => 'https://hooks.slack.test/example',
        'visible' => 1,
    ]);

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

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

    $departmentIds = array_map(
        static fn(array $department): int => (int)($department['id'] ?? 0),
        is_array($listResponse->data()) ? $listResponse->data() : []
    );

    expect($departmentIds)
        ->toContain($visibleDepartment['id'])
        ->toContain($webhookDepartment['id'])
        ->not->toContain($hiddenDepartment['id'])
        ->not->toContain($archivedDepartment['id']);

    $singleResponse = api_client()->get('/departments?id=' . $webhookDepartment['id'], $session['headers']);

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

    expect($singleResponse->data())
        ->toBeArray()
        ->toHaveKey('id', $webhookDepartment['id'])
        ->toHaveKey('slack_webhook', 'https://hooks.slack.test/example');
});

it('allows superusers to filter archived departments', function (): void {
    api_test_covers('GET /departments', 'happy');

    $session = api_fixtures()->createUserSession([
        'list_departments',
        'superuser_fetch_department',
    ]);

    $activeDepartment = api_fixtures()->createDepartment([
        'name' => 'Active Department',
        'visible' => 1,
        'archived' => 0,
    ]);
    $archivedDepartment = api_fixtures()->createDepartment([
        'name' => 'Archived Department',
        'visible' => 1,
        'archived' => 1,
    ]);

    $response = api_client()->get('/departments?filters=archived:1', $session['headers']);

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

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

    expect($departmentIds)
        ->toContain($archivedDepartment['id'])
        ->not->toContain($activeDepartment['id']);

    foreach ($response->data() as $department) {
        expect((bool)($department['archived'] ?? false))->toBeTrue();
    }
});

it('does not allow regular department listings to reveal archived departments through filters', function (): void {
    api_test_covers('GET /departments', 'auth');

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

    $activeDepartment = api_fixtures()->createDepartment([
        'name' => 'Regular Active Department',
        'visible' => 1,
        'archived' => 0,
    ]);
    $archivedDepartment = api_fixtures()->createDepartment([
        'name' => 'Regular Archived Department',
        'visible' => 1,
        'archived' => 1,
    ]);

    $response = api_client()->get('/departments?filters=archived:1', $session['headers']);

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

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

    expect($departmentIds)
        ->toContain($activeDepartment['id'])
        ->not->toContain($archivedDepartment['id']);

    $response = api_client()->get('/departments?filters[name]=NOT%20NULL%2Carchived:1', $session['headers']);

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

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

    expect($departmentIds)->not->toContain($archivedDepartment['id']);
});

it('rejects department listing when the permission is missing', function (): void {
    api_test_covers('GET /departments', 'auth');

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

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

    $response
        ->assertStatus(403)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMissingPermissions(['list_departments']);
});

it('returns superuser department custom pricing state as a boolean', function (): void {
    $session = api_fixtures()->createUserSession(['superuser_fetch_department']);
    $fallbackDepartment = api_fixtures()->createDepartment([
        'name' => 'Fallback Pricing Department',
        'custom_pricing_only' => 0,
    ]);
    $customOnlyDepartment = api_fixtures()->createDepartment([
        'name' => 'Custom Only Pricing Department',
        'custom_pricing_only' => 1,
    ]);

    $fallbackResponse = api_client()->get(
        '/superuser/department?department_id=' . $fallbackDepartment['id'],
        $session['headers']
    );
    $customOnlyResponse = api_client()->get(
        '/superuser/department?department_id=' . $customOnlyDepartment['id'],
        $session['headers']
    );

    $fallbackResponse
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();
    $customOnlyResponse
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess();

    expect($fallbackResponse->data()['custom_pricing_only'] ?? null)->toBeFalse();
    expect($customOnlyResponse->data()['custom_pricing_only'] ?? null)->toBeTrue();
});

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

    $session = api_fixtures()->createUserSession(['add_department']);
    $name = 'Created Department ' . uniqid('', false);

    $response = api_client()->post('/departments', [
        'name' => $name,
        'description' => 'Created by API test',
    ], $session['headers']);

    $response
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess()
        ->assertMessage('Department added successfully');

    $createdRow = api_test_runtime()->queryOne(
        "SELECT * FROM departments WHERE name = '" . api_test_runtime()->db()->real_escape_string($name) . "' ORDER BY id DESC LIMIT 1"
    );

    expect($createdRow)->not->toBeNull();
    api_fixtures()->cleanupDeleteById('departments', (int)$createdRow['id']);
});

it('rejects invalid department create requests', function (): void {
    api_test_covers('POST /departments', 'failure');

    $authorizedSession = api_fixtures()->createUserSession(['add_department']);
    $missingDescription = api_client()->post('/departments', [
        'name' => 'Broken Department',
    ], $authorizedSession['headers']);

    $missingDescription
        ->assertStatus(400)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMessage('Description is required');

    $unauthorizedSession = api_fixtures()->createUserSession([]);
    $missingPermission = api_client()->post('/departments', [
        'name' => 'No Permission Department',
        'description' => 'Should fail',
    ], $unauthorizedSession['headers']);

    $missingPermission
        ->assertStatus(403)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMissingPermissions(['add_department']);
});

it('updates departments through the real endpoint', function (): void {
    api_test_covers('PUT /departments', 'happy');

    $session = api_fixtures()->createUserSession(['edit_department']);
    $department = api_fixtures()->createDepartment([
        'name' => 'Original Department',
        'description' => 'Original description',
        'order_priority' => 1,
    ]);

    $response = api_client()->put('/departments', [
        'id' => $department['id'],
        'name' => 'Updated Department',
        'description' => 'Updated description',
        'order_priority' => 5,
        'archived' => true,
        'custom_pricing_only' => true,
    ], $session['headers']);

    $response
        ->assertStatus(200)
        ->assertEnvelope()
        ->assertSuccess()
        ->assertMessage('Department updated successfully');

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

    expect($row)->not->toBeNull();
    expect($row['name'] ?? null)->toBe('Updated Department');
    expect($row['description'] ?? null)->toBe('Updated description');
    expect((int)($row['order_priority'] ?? 0))->toBe(5);
    expect((int)($row['archived'] ?? 0))->toBe(1);
    expect((int)($row['custom_pricing_only'] ?? 0))->toBe(1);
});

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

    $authorizedSession = api_fixtures()->createUserSession(['edit_department']);
    $missingId = api_client()->put('/departments', [
        'name' => 'Missing ID',
    ], $authorizedSession['headers']);

    $missingId
        ->assertStatus(400)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMessage('Missing required parameters: id');

    $unauthorizedSession = api_fixtures()->createUserSession([]);
    $missingPermission = api_client()->put('/departments', [
        'id' => 123,
        'name' => 'No Permission',
    ], $unauthorizedSession['headers']);

    $missingPermission
        ->assertStatus(403)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMissingPermissions(['edit_department']);
});

it('lists department categories for a department', function (): void {
    api_test_covers('GET /departments/categories', 'happy');

    $session = api_fixtures()->createUserSession(['list_department_categories']);
    $department = api_fixtures()->createDepartment();
    $category = api_fixtures()->createCategory([
        'name' => 'Department Category',
    ]);
    api_fixtures()->linkDepartmentCategory((int)$department['id'], (int)$category['id']);

    $response = api_client()->get('/departments/categories?id=' . $department['id'], $session['headers']);

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

    expect($response->data())
        ->toBeArray()
        ->toHaveCount(1)
        ->and($response->data()[0]['department_id'] ?? null)->toBe($department['id'])
        ->and($response->data()[0]['category']['id'] ?? null)->toBe($category['id']);
});

it('lets customer booking sessions list department categories without the management permission', function (): void {
    api_test_covers('GET /departments/categories', 'auth');

    $customerSession = api_fixtures()->createUserSession(['user']);
    $department = api_fixtures()->createDepartment();
    $category = api_fixtures()->createCategory([
        'name' => 'Customer Department Category',
    ]);
    api_fixtures()->linkDepartmentCategory((int)$department['id'], (int)$category['id']);

    $customerResponse = api_client()->get('/departments/categories?id=' . $department['id'], $customerSession['headers']);

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

    expect($customerResponse->data())
        ->toBeArray()
        ->toHaveCount(1)
        ->and($customerResponse->data()[0]['category']['id'] ?? null)->toBe($category['id']);

    $subuserSession = api_fixtures()->createSubuserSession((int)$customerSession['user']['customer_number'], []);
    $subuserResponse = api_client()->get('/departments/categories?id=' . $department['id'], $subuserSession['headers']);

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

    expect($subuserResponse->data())
        ->toBeArray()
        ->toHaveCount(1)
        ->and($subuserResponse->data()[0]['category']['id'] ?? null)->toBe($category['id']);
});

it('rejects invalid department category requests', function (): void {
    api_test_covers('GET /departments/categories', 'failure');

    $authorizedSession = api_fixtures()->createUserSession(['list_department_categories']);
    $missingId = api_client()->get('/departments/categories', $authorizedSession['headers']);

    $missingId
        ->assertStatus(400)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMessage('Missing required parameters: id');

    $unauthorizedSession = api_fixtures()->createUserSession([]);
    $missingPermission = api_client()->get('/departments/categories?id=1', $unauthorizedSession['headers']);

    $missingPermission
        ->assertStatus(403)
        ->assertEnvelope()
        ->assertSuccess(false)
        ->assertMissingPermissions(['list_department_categories']);
});
