<?php

it('wires self-serve machine types, eligibility, summaries, and machine-start webhook routes', function (): void {
    $machineTypesRoute = file_get_contents(app_path('routes/departmentSelfserveMachineTypesRoute.php'));
    $vehicleConditionsRoute = file_get_contents(app_path('routes/departmentSelfserveVehicleConditionsRoute.php'));
    $webhookRoute = file_get_contents(app_path('routes/machineButtonPressRoute.php'));

    expect($machineTypesRoute)->not->toBeFalse();
    expect($machineTypesRoute)->toContain('/department/selfserve/machine-types');

    expect($vehicleConditionsRoute)->not->toBeFalse();
    expect($vehicleConditionsRoute)->toContain('/department/selfserve/vehicle/allowed');
    expect($vehicleConditionsRoute)->toContain('/department/selfserve/washes/summary');
    expect($vehicleConditionsRoute)->toContain('synchronizeSession');
    expect($vehicleConditionsRoute)->toContain("definePermission('list_own_department_selfserve_vehicle_conditions', subusers_permission_node_key::SELFSERVE_LIST)");
    expect($vehicleConditionsRoute)->toContain("definePermission('add_own_department_selfserve_vehicle_conditions', subusers_permission_node_key::SELFSERVE_ADD)");
    expect($vehicleConditionsRoute)->toContain('getAuthenticatedSelfServePrincipal');
    expect($vehicleConditionsRoute)->toContain('resolveEffectiveCustomerNumber()');

    expect($webhookRoute)->not->toBeFalse();
    expect($webhookRoute)->toContain('/relay/button/press/post');
    expect($webhookRoute)->toContain('/relay/machine/on/post');
    expect($webhookRoute)->toContain('recordMachineStartWebhook');
    expect($webhookRoute)->toContain('recordCloudShellySignal');
});

it('wires local edge gateway machine ON signal monitor endpoints', function (): void {
    $edgeGatewayRoute = file_get_contents(app_path('modules/edgegateway/routes/edgeGatewaysRoute.php'));
    $agent = file_get_contents(app_path('resources/edge-gateway-agent/agent.php'));
    $worker = file_get_contents(app_path('resources/edge-gateway-agent/lan-worker.php'));

    expect($edgeGatewayRoute)->not->toBeFalse()
        ->and($edgeGatewayRoute)->toContain('/edge-agent/gateways/{id}/selfserve/machine-signal-bindings')
        ->and($edgeGatewayRoute)->toContain('/edge-agent/gateways/{id}/selfserve/machine-signal')
        ->and($edgeGatewayRoute)->toContain('recordEdgeGatewaySignal')
        ->and($agent)->toContain('pollMachineStartSignals')
        ->and($agent)->toContain('/selfserve/machine-signal')
        ->and($worker)->toContain('Input.GetStatus')
        ->and($worker)->toContain('/relay/input-status')
        ->and($worker)->toContain('worker_require_authorization')
        ->and($worker)->toContain('HTTP_X_TRUCKWASH_WORKER_TOKEN');
});

it('keeps machine type support wired into lanes, tasks, and conditions routes', function (): void {
    $lanesRoute = file_get_contents(app_path('routes/departmentLanesRoute.php'));
    $tasksRoute = file_get_contents(app_path('routes/departmentSelfserveTasksRoute.php'));
    $conditionsRoute = file_get_contents(app_path('routes/departmentSelfserveConditionsRoute.php'));
    $questionsRoute = file_get_contents(app_path('routes/departmentSelfserveQuestionsRoute.php'));

    expect($lanesRoute)->toContain('machine_type_id');
    expect($tasksRoute)->toContain('machine_type_id');
    expect($conditionsRoute)->toContain('machine_type_id');
    expect($questionsRoute)->toContain('department') // shared questions still use the legacy columns with default 0 scope
        ->toContain('lane')
        ->toContain('product');
});

it('wires lane-level self-serve toggles through lane APIs, guest payloads, and edge workspace readiness', function (): void {
    $lanesRoute = file_get_contents(app_path('routes/departmentLanesRoute.php'));
    $laneObject = file_get_contents(app_path('objects/department_lanes_o.php'));
    $guestRoute = file_get_contents(app_path('routes/guestRoute.php'));
    $edgeWorkspace = file_get_contents(app_path('modules/edgegateway/classes/edge_gateway_department_workspace_service.php'));

    expect($lanesRoute)->not->toBeFalse()
        ->and($lanesRoute)->toContain("'selfserve_enabled'")
        ->and($lanesRoute)->toContain('normalizeSelfServeEnabledValue')
        ->and($lanesRoute)->toContain('disableSelfServeRelaysBestEffort')
        ->and($lanesRoute)->toContain("'lane' => \$created_lane->asArray()")
        ->and($lanesRoute)->toContain("'lane' => \$department_lane->asArray()");

    expect($laneObject)->not->toBeFalse()
        ->and($laneObject)->toContain('public object_property $selfserve_enabled')
        ->and($laneObject)->toContain('public function isSelfServeEnabled(): bool')
        ->and($laneObject)->toContain('public static function normalizeSelfServeEnabledValue')
        ->and($laneObject)->toContain('public static function disableSelfServeRelaysBestEffort')
        ->and($laneObject)->toContain('setMachineRelayStatusHard(false)')
        ->and($laneObject)->toContain('setMachineProgramPickerRelayStatusHard(false)')
        ->and($laneObject)->toContain('setMachineCleanerRelayStatusHard(false)');

    expect($guestRoute)->not->toBeFalse()
        ->and($guestRoute)->toContain("\$additional_data['self_serve_module_enabled'] = \$self_serve_module_enabled")
        ->and($guestRoute)->toContain("'self_serve_module_disabled'")
        ->and($guestRoute)->toContain("'selfserve_enabled' => \$lane_self_serve_enabled")
        ->and($guestRoute)->toContain("'selfserve_available' => \$self_serve_available")
        ->and($guestRoute)->toContain("'selfserve_unavailable_reason' => \$self_serve_unavailable_reason")
        ->and($guestRoute)->toContain("'machine_available' => \$self_serve_available && !empty(\$lane->relay_machine_id->value())");

    expect($edgeWorkspace)->not->toBeFalse()
        ->and($edgeWorkspace)->toContain("'selfserve_enabled' => \$lane->isSelfServeEnabled()")
        ->and($edgeWorkspace)->toContain("'enabled_lanes' => count(\$enabledLanes)")
        ->and($edgeWorkspace)->toContain("'ready_lanes' => count(\$readyLanes)")
        ->and($edgeWorkspace)->toContain("(int)(\$selfServe['ready_lanes'] ?? 0) < (int)(\$selfServe['enabled_lanes'] ?? 0)");
});

it('sources guest lane product availability from the published v2 self-serve config before legacy tasks', function (): void {
    $laneObject = file_get_contents(app_path('objects/department_lanes_o.php'));

    expect($laneObject)->not->toBeFalse()
        ->and($laneObject)->toContain('getPublishedSelfServeLaneProducts')
        ->and($laneObject)->toContain('selfserve_config_versioning')
        ->and($laneObject)->toContain('getPublishedV2Config')
        ->and($laneObject)->toContain("\$taskLane !== 0 && \$taskLane !== \$laneId")
        ->and($laneObject)->toContain('department_selfserve_tasks_o::getLaneProducts');
});

it('wires self-serve config draft/publish/rollback lifecycle endpoints', function (): void {
    $configRoute = file_get_contents(app_path('routes/departmentSelfserveConfigVersionsRoute.php'));

    expect($configRoute)->not->toBeFalse();
    expect($configRoute)->toContain('/department/selfserve/config/versions');
    expect($configRoute)->toContain('/department/selfserve/config/history');
    expect($configRoute)->toContain('/department/selfserve/config/active');
    expect($configRoute)->toContain('/department/selfserve/config/draft');
    expect($configRoute)->toContain('/department/selfserve/config/validate');
    expect($configRoute)->toContain('/department/selfserve/config/publish');
    expect($configRoute)->toContain('/department/selfserve/config/rollback');
    expect($configRoute)->toContain('publish_department_selfserve_config_versions');
    expect($configRoute)->toContain('rollback_department_selfserve_config_versions');
});

it('wires the all-in-one self-serve studio replacement endpoints', function (): void {
    $studioRoute = file_get_contents(app_path('routes/departmentSelfserveStudioRoute.php'));
    $studioGraph = file_get_contents(app_path('modules/selfserve/classes/selfserve_studio_graph.php'));

    expect($studioRoute)->not->toBeFalse();
    expect($studioRoute)->toContain('/department/selfserve/studio/graph');
    expect($studioRoute)->toContain('/department/selfserve/studio/layout');
    expect($studioRoute)->toContain('/department/selfserve/studio/validate');
    expect($studioRoute)->toContain('/department/selfserve/studio/simulate');
    expect($studioRoute)->toContain('/department/selfserve/studio/path-outcomes');
    expect($studioRoute)->toContain('/department/selfserve/studio/path-outcomes/stream');
    expect($studioRoute)->toContain('/department/selfserve/studio/path-confirmations');
    expect($studioRoute)->toContain("ini_set('display_errors', '0')");
    expect($studioRoute)->toContain('/department/selfserve/studio/publish');
    expect($studioRoute)->toContain('/department/selfserve/studio/rollback');
    expect($studioRoute)->toContain('/department/selfserve/studio/gateway-action');
    expect($studioRoute)->toContain('projectPathOutcomes');
    expect($studioRoute)->toContain('confirmPathOutcome');
    expect($studioRoute)->toContain('modules_shelly_config');
    expect($studioRoute)->toContain('modules_selfserve_sessions_force_stop');

    $simulateStart = strpos($studioRoute, "/department/selfserve/studio/simulate");
    $simulateEnd = strpos($studioRoute, "/department/selfserve/studio/path-outcomes/stream", $simulateStart);
    expect($simulateStart)->not->toBeFalse();
    expect($simulateEnd)->not->toBeFalse();
    $simulateRoute = substr($studioRoute, $simulateStart, $simulateEnd - $simulateStart);
    expect($simulateRoute)->toContain("requireStudioUser('list_department_selfserve_config_versions')");
    expect($simulateRoute)->toContain("'list_department_selfserve_config_versions' => 'Run the self-serve studio simulator'");
    expect($simulateRoute)->not->toContain('list_department_selfserve_vehicle_conditions');

    expect($studioGraph)->not->toBeFalse();
    $loadSimulationConfigStart = strpos($studioGraph, 'private function loadSimulationConfig');
    $projectPathOutcomesStart = strpos($studioGraph, 'public function projectPathOutcomes', $loadSimulationConfigStart);
    expect($loadSimulationConfigStart)->not->toBeFalse();
    expect($projectPathOutcomesStart)->not->toBeFalse();
    $loadSimulationConfig = substr($studioGraph, $loadSimulationConfigStart, $projectPathOutcomesStart - $loadSimulationConfigStart);
    expect($loadSimulationConfig)->toContain('selectLatestByDepartmentAndStatus');
    expect($loadSimulationConfig)->toContain('snapshotLegacyConfig($departmentId)');
    expect($loadSimulationConfig)->not->toContain('ensureDraftFromLegacy');
    expect($loadSimulationConfig)->toContain('Draft studio simulation requires list_department_selfserve_config_versions permission.');

    expect($studioGraph)->toContain('department_selfserve_studio_layouts');
    expect($studioGraph)->toContain('department_selfserve_path_confirmations');
    expect($studioGraph)->toContain('buildGatewayWorkspace');
    expect($studioGraph)->toContain('runGatewayAction');
    expect($studioGraph)->toContain('layout_affects_runtime');
    expect($studioGraph)->toContain('resolved');
});

it('wires machine wash controls into self-serve module config', function (): void {
    $selfserveConfig = file_get_contents(app_path('modules/selfserve/selfserve_c.php'));
    $machineWashEnabledConfig = file_get_contents(app_path('modules/selfserve/config/selfserve_machine_wash_enabled_c.php'));

    expect($selfserveConfig)->not->toBeFalse();
    expect($selfserveConfig)->toContain('selfserve_machine_wash_minutes_included_c');
    expect($selfserveConfig)->toContain('machine_wash_minutes_included');
    expect($selfserveConfig)->toContain('selfserve_machine_wash_enabled_c');
    expect($selfserveConfig)->toContain('machine_wash_enabled');
    expect($machineWashEnabledConfig)->not->toBeFalse()
        ->and($machineWashEnabledConfig)->toContain("'machine_wash_enabled'")
        ->and($machineWashEnabledConfig)->toContain("'bool'")
        ->and($machineWashEnabledConfig)->toContain('true');
});

it('keeps legacy self-serve CRUD routes syncing canonical drafts', function (): void {
    $questionsRoute = file_get_contents(app_path('routes/departmentSelfserveQuestionsRoute.php'));
    $conditionsRoute = file_get_contents(app_path('routes/departmentSelfserveConditionsRoute.php'));
    $rulesRoute = file_get_contents(app_path('routes/departmentSelfserveConditionRulesRoute.php'));
    $tasksRoute = file_get_contents(app_path('routes/departmentSelfserveTasksRoute.php'));

    expect($questionsRoute)->toContain('syncDraftFromLegacyForDepartment');
    expect($conditionsRoute)->toContain('syncDraftFromLegacyForDepartment');
    expect($rulesRoute)->toContain('syncDraftFromLegacyForDepartment');
    expect($tasksRoute)->toContain('syncDraftFromLegacyForDepartment');
});

it('wires machine relay status get and set endpoints', function (): void {
    $moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));
    $relayController = file_get_contents(app_path('modules/selfserve/traits/selfserve_lane_relay_controller_t.php'));

    expect($moduleSelfServeRoute)->not->toBeFalse();
    expect($relayController)->not->toBeFalse();
    expect($moduleSelfServeRoute)->toContain("definePermission(self::CUSTOMER_SELFSERVE_PERMISSION, subusers_permission_node_key::SELFSERVE_LIST)");
    expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/relay/machine/status');
    expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/relay/machine/set');
    expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/relay/machine_program_picker/status');
    expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/relay/machine_program_picker/set');
    expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/relay/machine_cleaner/status');
    expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/relay/machine_cleaner/set');
    expect($moduleSelfServeRoute)->toContain('getMachineRelayStatus');
    expect($moduleSelfServeRoute)->toContain('setMachineRelayStatus');
    expect($moduleSelfServeRoute)->toContain('getMachineProgramPickerRelayStatus');
    expect($moduleSelfServeRoute)->toContain('setMachineProgramPickerRelayStatus');
    expect($moduleSelfServeRoute)->toContain('getMachineCleanerRelayStatus');
    expect($moduleSelfServeRoute)->toContain('setMachineCleanerRelayStatus');
    expect($moduleSelfServeRoute)->toContain('setMachineCleanerRelayStatusHard(true)');
    expect($relayController)->toContain('markLatestSelfServeSessionRelayEnabledForLane');
    expect($relayController)->toContain('selectLatestOpenByLane(');
    expect($relayController)->toContain('$session->markRelayEnabled();');
    expect($moduleSelfServeRoute)->toContain('applyShellyTransportOverride($lane)');
    expect($moduleSelfServeRoute)->toContain("'transport' => \$this->requestedShellyTransportOverride()");
    expect($moduleSelfServeRoute)->toContain('buildRelayStatusResponse');
});

it('falls back to edge gateway relay bindings for lane hardware batches', function (): void {
    $moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));

    expect($moduleSelfServeRoute)->not->toBeFalse();
    expect($moduleSelfServeRoute)
        ->toContain('use objects\\edge_gateway_relay_bindings_o;')
        ->toContain('edgeGatewayRelayIdForLaneHardwareTarget')
        ->toContain('$this->edgeGatewayRelayIdForLaneHardwareTarget($lane, $target)')
        ->toContain('edgeGatewayBindingMatchesHardwareTarget')
        ->toContain('edgeGatewayBindingMatchesLane')
        ->toContain('Multiple edge gateway relay bindings match lane hardware target')
        ->toContain("'MACHINE' => ['MACHINE']")
        ->toContain("'PROGRAM_PICKER', 'MACHINE_PROGRAM_PICKER' => ['PROGRAM_PICKER', 'MACHINE_PROGRAM_PICKER']")
        ->toContain("'CLEANER', 'MACHINE_CLEANER' => ['CLEANER', 'MACHINE_CLEANER']")
        ->toContain("'ENTRANCE' => ['ENTRANCE', 'ENTRY']");
});

it('wires dashboard lane machine status and Dognvask toggle endpoints', function (): void {
    $lanesRoute = file_get_contents(app_path('routes/departmentLanesRoute.php'));
    $laneObject = file_get_contents(app_path('objects/department_lanes_o.php'));
    $moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));

    expect($lanesRoute)->not->toBeFalse()
        ->and($lanesRoute)->toContain('/department/lanes/status-toggles')
        ->and($lanesRoute)->toContain('LIST_DEPARTMENT_LANE_STATUS_TOGGLES')
        ->and($lanesRoute)->toContain('getDepartmentLanes($department_id)');

    expect($laneObject)->not->toBeFalse()
        ->and($laneObject)->toContain("'machine_status_enabled' => self::isOperationalStatusName(\$status)")
        ->and($laneObject)->toContain("'dognvask_configured' => \$selfserve_configuration_warnings === []")
        ->and($laneObject)->toContain("'dognvask_configuration_warnings' => \$selfserve_configuration_warnings")
        ->and($laneObject)->toContain('public function getSelfServeConfigurationWarnings(): array')
        ->and($laneObject)->toContain('relay_machine_program_picker_id')
        ->and($laneObject)->toContain('machine_type_id');

    expect($moduleSelfServeRoute)->not->toBeFalse()
        ->and($moduleSelfServeRoute)->toContain("\$this->put('/modules/self-serve/lane/status'")
        ->and($moduleSelfServeRoute)->toContain('modules_selfserve_lane_status_set')
        ->and($moduleSelfServeRoute)->toContain('setLaneStatus($target_status)')
        ->and($moduleSelfServeRoute)->toContain('selfserve_lane_status::MAINTENANCE')
        ->and($moduleSelfServeRoute)->toContain('machine_status_enabled');
});

it('applies Shelly transport overrides across self-serve relay side-effect routes', function (): void {
    $moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));

    expect($moduleSelfServeRoute)->not->toBeFalse();
    expect(substr_count($moduleSelfServeRoute, 'applyShellyTransportOverride($lane)'))->toBeGreaterThanOrEqual(17);
    expect($moduleSelfServeRoute)->toContain('$lane->execute($command, $args);');
    expect($moduleSelfServeRoute)->toContain('$lane->setAllowedServicesFromVisibleTasks($allowed_services);');
    expect($moduleSelfServeRoute)->toContain('$lane->open($gate, $toggle_after);');
    expect($moduleSelfServeRoute)->toContain('$lane->turnOnRelay(selfserve_lane_relay::MACHINE, $duration);');
    expect($moduleSelfServeRoute)->toContain('$lane->turnOnRelay(selfserve_lane_relay::MACHINE_PROGRAM_PICKER, $duration);');
    expect($moduleSelfServeRoute)->toContain('$lane->turnOnRelay(selfserve_lane_relay::MACHINE_CLEANER, $duration);');
    expect($moduleSelfServeRoute)->toContain('$lane->forceTurnOnMachineRelay($duration);');
    expect($moduleSelfServeRoute)->toContain('$lane->forceTurnOffMachineRelay();');
    expect($moduleSelfServeRoute)->toContain('$lane->forceTurnOnRelay(selfserve_lane_relay::MACHINE_PROGRAM_PICKER, $duration);');
    expect($moduleSelfServeRoute)->toContain('$lane->forceTurnOffRelay(selfserve_lane_relay::MACHINE_PROGRAM_PICKER);');
    expect($moduleSelfServeRoute)->toContain('$lane->forceTurnOnRelay(selfserve_lane_relay::MACHINE_CLEANER, $duration);');
    expect($moduleSelfServeRoute)->toContain('$lane->forceTurnOffRelay(selfserve_lane_relay::MACHINE_CLEANER);');
});

it('wires allowed services route through machine relay visibility sync', function (): void {
    $moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));

    expect($moduleSelfServeRoute)->not->toBeFalse();
    expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/services/allowed');
    expect($moduleSelfServeRoute)->toContain('selfserve_wash_session_tasks_o');
    expect($moduleSelfServeRoute)->toContain('selectLatestOpenByLane');
    expect($moduleSelfServeRoute)->toContain('$session_task_services[$task_id]');
    expect($moduleSelfServeRoute)->toContain('publishedConfigTaskServicesForLane');
    expect($moduleSelfServeRoute)->toContain('$published_config_task_services[$tid]');
    expect($moduleSelfServeRoute)->toContain('setAllowedServicesFromVisibleTasks($allowed_services)');
    expect($moduleSelfServeRoute)->not->toContain('syncMachineRelayFromVisibleServices($allowed_services, true)');
    expect($moduleSelfServeRoute)->toContain("'relay_sync' => \$relay_sync");
});

it('wires self-serve lane gate open endpoint', function (): void {
    $moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));

    expect($moduleSelfServeRoute)->not->toBeFalse();
    expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/gate/open');
    expect($moduleSelfServeRoute)->toContain('modules_selfserve_lane_gate_open');
    expect($moduleSelfServeRoute)->toContain('selfserve_lane_port::ENTRANCE');
    expect($moduleSelfServeRoute)->toContain('selfserve_lane_port::EXIT');
    expect($moduleSelfServeRoute)->toContain('$toggle_after = $this->requestedRelayToggleAfter(1);');
    expect($moduleSelfServeRoute)->toContain('MAX_GATE_OPEN_TOGGLE_AFTER_SECONDS = 5');
    expect($moduleSelfServeRoute)->toContain('self::requireMaxValue($toggle_after, self::MAX_GATE_OPEN_TOGGLE_AFTER_SECONDS);');
    expect($moduleSelfServeRoute)->toContain('$lane->open($gate, $toggle_after)');
    expect($moduleSelfServeRoute)->toContain('queueLocalLaneGateOpen($lane, $gate, $toggle_after)');
    expect($moduleSelfServeRoute)->toContain('$this->requestedShellyTransportOverride() === \'local\'');
    expect($moduleSelfServeRoute)->toContain('queueRelaySwitchBatch(');
    expect($moduleSelfServeRoute)->toContain("'target' => \$gate->name");
    expect($moduleSelfServeRoute)->toContain("'queued' => \$queued_gate_command !== null");
    expect($moduleSelfServeRoute)->toContain('$queued_gate_command !== null ? 202 : 200');
    expect($moduleSelfServeRoute)->toContain("'toggle_after' => \$toggle_after");
    expect($moduleSelfServeRoute)->toContain('private function requestedRelayToggleAfter');
    expect($moduleSelfServeRoute)->toContain('Failed to open self-serve lane gate');
    expect($moduleSelfServeRoute)->toContain("'Failed to open ' . strtolower(\$gate->name) . ' gate.'");
    expect($moduleSelfServeRoute)->not->toContain('Failed to open lane gate: ');
});

it('wires self-serve property gate command permissions', function (): void {
    $moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));
    $commandTrait = file_get_contents(app_path('modules/selfserve/traits/selfserve_lane_command_t.php'));

    expect($moduleSelfServeRoute)->not->toBeFalse();
    expect($moduleSelfServeRoute)->toContain('case selfserve_lane_command::OPEN_PROPERTY_ACCESS_GATE');
    expect($moduleSelfServeRoute)->toContain('case selfserve_lane_command::OPEN_PROPERTY_EXIT_GATE');
    expect($moduleSelfServeRoute)->toContain('modules_selfserve_lane_command_execute_open_property_access_gate');
    expect($moduleSelfServeRoute)->toContain('modules_selfserve_lane_command_execute_open_property_exit_gate');
    expect($moduleSelfServeRoute)->toContain('Failed to execute self-serve property gate command');
    expect($moduleSelfServeRoute)->toContain('$this->canCustomerUsePropertyGateForLane($lane, $customer_number)');
    expect($moduleSelfServeRoute)->toContain('$this->isOwnCustomerContext($customer_number)');
    expect($moduleSelfServeRoute)->toContain('$this->customerHasActiveSelfServeWashInDepartment($department_id, $customer_number, $subuser_id)');

    expect($commandTrait)->not->toBeFalse();
    expect($commandTrait)->toContain('Failed to open property access gate.');
    expect($commandTrait)->toContain('Failed to open property exit gate.');
    expect($commandTrait)->not->toContain('Failed to open property access gate: ');
});

it('wires in-progress self-serve wash details endpoint', function (): void {
    $moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));

    expect($moduleSelfServeRoute)->not->toBeFalse();
    expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/wash/in-progress');
    expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/wash/my-active-wash');
    expect($moduleSelfServeRoute)->toContain('modules_selfserve_lane_wash_in_progress_view');
    expect($moduleSelfServeRoute)->toContain('add_own_department_selfserve_vehicle_conditions');
    expect($moduleSelfServeRoute)->toContain('requireInProgressWashDetailsAccess($lane_id)');
    expect($moduleSelfServeRoute)->toContain('findLatestActiveSelfServeSessionForCustomer(');
    expect($moduleSelfServeRoute)->toContain('buildActiveSelfServeSessionResponse($session)');
    expect($moduleSelfServeRoute)->toContain('(new department_lanes_o())->select($lane_id)');
    expect($moduleSelfServeRoute)->toContain('self::requireDepartmentAccess((string)$department_lane->department->value())');
    expect($moduleSelfServeRoute)->toContain('scopeInProgressWashResponseForCustomer');
    expect($moduleSelfServeRoute)->toContain("'in_progress' => true");
    expect($moduleSelfServeRoute)->toContain("'in_progress' => false");
    expect($moduleSelfServeRoute)->toContain('selfserve_lane_status::OCCUPIED');
    expect($moduleSelfServeRoute)->toContain('selfserve_lane_state::IN_WASH');
    expect($moduleSelfServeRoute)->toContain('getWashStartTime');
    expect($moduleSelfServeRoute)->toContain('machine_wash_minutes_included');
    expect($moduleSelfServeRoute)->toContain("'included_minutes' =>");
    expect($moduleSelfServeRoute)->toContain("'machine_relay_enabled' =>");
    expect($moduleSelfServeRoute)->toContain("'machine_relay_enabled_at' =>");
    expect($moduleSelfServeRoute)->toContain("'wash_started_at' =>");
});

it('wires self-serve session management endpoints and OpenAPI coverage', function (): void {
    $moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));
    $openApiFiles = [app_path('openapi.yaml')];
    $rootOpenApiFile = dirname(app_path(), 3) . '/openapi.yaml';

    if (is_file($rootOpenApiFile)) {
        $openApiFiles[] = $rootOpenApiFile;
    }

    expect($moduleSelfServeRoute)->not->toBeFalse();
    expect($moduleSelfServeRoute)->toContain('/modules/self-serve/sessions');
    expect($moduleSelfServeRoute)->toContain('/modules/self-serve/sessions/{id}');
    expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/force/stop');
    expect($moduleSelfServeRoute)->toContain('modules_selfserve_sessions_view');
    expect($moduleSelfServeRoute)->toContain('modules_selfserve_sessions_force_stop');
    expect($moduleSelfServeRoute)->toContain('modules_selfserve_sessions_force_stop_bill');
    expect($moduleSelfServeRoute)->toContain('listObjectsWithPaginationIfSet');
    expect($moduleSelfServeRoute)->toContain('getSessionSummary($session_id)');
    expect($moduleSelfServeRoute)->toContain('forceStopLane($lane_id, $session_id, $bill, $reason, $user_id)');

    foreach ($openApiFiles as $openApiFile) {
        $openApi = file_get_contents($openApiFile);

        expect($openApi)->not->toBeFalse();
        expect($openApi)->toContain('/modules/self-serve/sessions:');
        expect($openApi)->toContain('/modules/self-serve/sessions/{id}:');
        expect($openApi)->toContain('/modules/self-serve/lane/force/stop:');
        expect($openApi)->toContain('listSelfServeSessions');
        expect($openApi)->toContain('getSelfServeSessionDetail');
        expect($openApi)->toContain('forceStopSelfServeLane');
        expect($openApi)->toContain('SelfserveForceStopResponse');
        expect($openApi)->toContain('FORCE_STOPPED');
        expect($openApi)->toContain('SESSION_FORCE_STOPPED');
    }
});

it('returns authoritative allowed service state from self-serve session summaries', function (): void {
    $washFlow = file_get_contents(app_path('modules/selfserve/classes/selfserve_wash_flow.php'));

    expect($washFlow)->not->toBeFalse();
    $start = strpos($washFlow, 'public function getSessionSummary');
    $end = strpos($washFlow, 'public function getLatestSessionSummary', $start);
    expect($start)->not->toBeFalse();
    expect($end)->not->toBeFalse();
    $summarySource = substr($washFlow, $start, $end - $start);

    expect($summarySource)
        ->toContain('$metadata = is_array($session->metadata_json->value())')
        ->toContain('$allowedServices = $this->normalizeServiceNames(')
        ->toContain('$tasks = $this->filterTasksForAllowedServices($tasks, $allowedServices)')
        ->toContain("'allowed_services' => \$allowedServices")
        ->toContain("'machine_available' => \$machineAvailable")
        ->toContain("'all_visible_questions_answered' => \$allVisibleQuestionsAnswered")
        ->toContain("'allowed' => (bool)\$session->allowed->value()");
});

it('filters machine button tasks out of self-serve snapshots when MACHINE is not allowed', function (): void {
    $washFlow = file_get_contents(app_path('modules/selfserve/classes/selfserve_wash_flow.php'));

    expect($washFlow)->not->toBeFalse()
        ->and($washFlow)->toContain('$visibleTasks = $this->filterTasksForAllowedServices($activeTasks, $allowedServices)')
        ->and($washFlow)->toContain("'tasks' => \$visibleTasks")
        ->and($washFlow)->toContain('protected function filterTasksForAllowedServices')
        ->and($washFlow)->toContain('protected function taskUsesMachineControls')
        ->and($washFlow)->toContain("in_array(selfserve_lane_services::MACHINE->name, \$allowedServices, true)")
        ->and($washFlow)->toContain("\$this->normalizeButtonList(\$task['buttons'] ?? null) !== []")
        ->and($washFlow)->toContain("\$task['dynamic_images_vehicle_type'] ?? null");
});

it('applies the global machine wash toggle before exposing machine tasks or services', function (): void {
    $washFlow = file_get_contents(app_path('modules/selfserve/classes/selfserve_wash_flow.php'));
    $moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));

    expect($washFlow)->not->toBeFalse()
        ->and($washFlow)->toContain('$machineWashEnabled = $this->isMachineWashEnabled();')
        ->and($washFlow)->toContain('$allowedServices = $this->withoutMachineService($allowedServices);')
        ->and($washFlow)->toContain('$machineAvailable = $machineWashEnabled && !empty($lane->relay_machine_id->value());')
        ->and($washFlow)->toContain("'machine_wash_enabled' => \$machineWashEnabled")
        ->and($washFlow)->toContain('Machine wash is disabled globally.')
        ->and($washFlow)->toContain('->config->machine_wash_enabled->isTrue()');

    expect($moduleSelfServeRoute)->not->toBeFalse()
        ->and($moduleSelfServeRoute)->toContain('if (!$this->isMachineWashEnabled())')
        ->and($moduleSelfServeRoute)->toContain('selfserve_lane_services::MACHINE->name')
        ->and($moduleSelfServeRoute)->toContain('->config->machine_wash_enabled->isTrue()');
});

it('keeps self-serve force stop distinct from normal STOP relay and gate behavior', function (): void {
    $washFlow = file_get_contents(app_path('modules/selfserve/classes/selfserve_wash_flow.php'));
    $sessionObject = file_get_contents(app_path('objects/selfserve_wash_sessions_o.php'));
    $statusEnum = file_get_contents(app_path('modules/selfserve/helpers/selfserve_wash_session_status.php'));
    $eventEnum = file_get_contents(app_path('modules/selfserve/helpers/selfserve_wash_event_type.php'));

    expect($washFlow)->not->toBeFalse();
    expect($sessionObject)->not->toBeFalse();
    expect($statusEnum)->toContain('FORCE_STOPPED');
    expect($eventEnum)->toContain('SESSION_FORCE_STOPPED');
    expect($sessionObject)->toContain('markForceStopped');
    expect($sessionObject)->toContain("metadata_json->set(\$existing)");

    $start = strpos($washFlow, 'public function forceStopLane');
    $end = strpos($washFlow, 'protected function buildEligibilitySnapshot', $start);
    expect($start)->not->toBeFalse();
    expect($end)->not->toBeFalse();
    $forceStopSource = substr($washFlow, $start, $end - $start);

    expect($forceStopSource)->toContain('$lane->invoice()');
    expect($forceStopSource)->toContain('getLastInvoiceOrderId');
    expect($forceStopSource)->toContain('markForceStopped');
    expect($forceStopSource)->toContain('SESSION_FORCE_STOPPED');
    expect($forceStopSource)->toContain('selfserve_lane_command::RESET');
    expect($forceStopSource)->toContain('runtime_before_reset');
    expect($forceStopSource)->not->toContain('selfserve_lane_command::STOP');
    expect($forceStopSource)->not->toContain('selfserve_lane_port::EXIT');
    expect($forceStopSource)->not->toContain('open(');
    expect($forceStopSource)->not->toContain('turnOffRelaysAfterStop');
    expect($forceStopSource)->not->toContain('disableMachineRelayForCompletedWash');
    expect($forceStopSource)->not->toContain('getRelayStatus');
    expect($forceStopSource)->not->toContain('MACHINE_PROGRAM_PICKER');
    expect($forceStopSource)->not->toContain('addVehicleTypeProductToInvoiceIfNeeded');
    expect($forceStopSource)->not->toContain('addVehicleTypeProductToLastInvoiceOrder');
});

it('wires vehicle type override into self-serve preview and synchronization routes', function (): void {
    $vehicleConditionsRoute = file_get_contents(app_path('routes/departmentSelfserveVehicleConditionsRoute.php'));
    $washFlow = file_get_contents(app_path('modules/selfserve/classes/selfserve_wash_flow.php'));

    expect($vehicleConditionsRoute)->not->toBeFalse();
    expect($vehicleConditionsRoute)->toContain('vehicle_type_id');
    expect($vehicleConditionsRoute)->toContain('vehicle_type');
    expect($vehicleConditionsRoute)->toContain('resolveVehicleTypeIdFromQuery');
    expect($vehicleConditionsRoute)->toContain('resolveVehicleTypeIdFromRequest');
    expect($vehicleConditionsRoute)->toContain('normalizeVehicleTypeOverride');
    expect($vehicleConditionsRoute)->toContain('shouldRefreshSummaryForVehicleType');
    expect($vehicleConditionsRoute)->toContain('previewVehicleEligibility($lane_id, $reg, $customer_number, $vehicle_type_id, [');
    expect($vehicleConditionsRoute)->toContain('synchronizeSession($lane_id, $reg, $customer_number, false, $vehicle_type_id, false, [');
    expect($vehicleConditionsRoute)->toContain("'create_session' => false");
    expect($vehicleConditionsRoute)->toContain("'subuser_id' => \$subuser_id");
    expect($vehicleConditionsRoute)->toContain('requestBooleanFlag');
    expect($vehicleConditionsRoute)->toContain('synchronizeSession($lane, $reg, $customer_id, $activate_machine, $vehicle_type_id, $sync_relay_state, [');
    expect($vehicleConditionsRoute)->toContain('synchronizeSession($lane_id, $reg, $customer_id, $activate_machine, $vehicle_type_id, $sync_relay_state)');

    expect($washFlow)->not->toBeFalse();
    expect($washFlow)->toContain('resolveVehicleTypeId($vehicle, $vehicleTypeIdOverride)');
    expect($washFlow)->toContain("'vehicle_type_id' => \$snapshot['vehicle_type_id']");
    expect($washFlow)->toContain("\$session->vehicle_type_id->set(\$snapshot['vehicle_type_id']);");
    expect($washFlow)->toContain("\$session->vehicle_id->set(\$snapshot['vehicle']['id'] ?? null);");
});

it('blocks new self-serve eligibility and session sync for disabled lanes', function (): void {
    $washFlow = file_get_contents(app_path('modules/selfserve/classes/selfserve_wash_flow.php'));
    $commandTrait = file_get_contents(app_path('modules/selfserve/traits/selfserve_lane_command_t.php'));

    expect($washFlow)->not->toBeFalse()
        ->and($washFlow)->toContain('if (!$lane->isSelfServeEnabled())')
        ->and($washFlow)->toContain("'allowed_services' => []")
        ->and($washFlow)->toContain("'machine_available' => false")
        ->and($washFlow)->toContain("'allowed' => false")
        ->and($washFlow)->toContain("'blocked_reason' => 'Self-serve is disabled for this lane.'")
        ->and($washFlow)->toContain("'blocking_reasons' => ['LANE_SELFSERVE_DISABLED']")
        ->and($washFlow)->toContain("'disabled_lane' => true")
        ->and($washFlow)->toContain('formatBlockedSessionSummary')
        ->and($washFlow)->toContain('empty($summary[\'session\'][\'id\'])');

    expect($commandTrait)->not->toBeFalse()
        ->and($commandTrait)->toContain('$this->department_lane->isSelfServeEnabled()')
        ->and($commandTrait)->toContain('Self-serve is not enabled for this lane.');
});

it('keeps read-only self-serve preview and summary refreshes from touching relay hardware', function (): void {
    $vehicleConditionsRoute = file_get_contents(app_path('routes/departmentSelfserveVehicleConditionsRoute.php'));

    expect($vehicleConditionsRoute)->not->toBeFalse();
    expect($vehicleConditionsRoute)->toContain('$flow->synchronizeSession($lane_id, $reg, $customer_number, false, $vehicle_type_id, false, [');
    expect($vehicleConditionsRoute)->toContain("'create_session' => false");
    expect($vehicleConditionsRoute)->toContain('$summary = $flow->synchronizeSession(');
    expect($vehicleConditionsRoute)->toContain('$summary = $flow->synchronizeSession($lane_id, $reg, $customer_number, false, $vehicle_type_id, false, [');
    expect($vehicleConditionsRoute)->toContain('$summary = $flow->getLatestSessionSummary($lane_id, $reg);');
    expect($vehicleConditionsRoute)->toContain('$activate_machine = $this->requestBooleanFlag(\'activate_machine\', true);');
    expect($vehicleConditionsRoute)->toContain('$sync_relay_state = $this->requestBooleanFlag(\'sync_relay_state\', true);');
    expect($vehicleConditionsRoute)->toContain('$this->getWashFlow()->synchronizeSession($lane, $reg, $customer_id, $activate_machine, $vehicle_type_id, $sync_relay_state, [');
    expect($vehicleConditionsRoute)->toContain('$this->getWashFlow()->synchronizeSession($lane_id, $reg, $customer_id, $activate_machine, $vehicle_type_id, $sync_relay_state);');
});

it('classifies self-serve lane command route authorization by customer product behavior', function (): void {
    $moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));
    $productDocs = file_get_contents(app_path('modules/selfserve/selfserve.md'));
    $washFlow = file_get_contents(app_path('modules/selfserve/classes/selfserve_wash_flow.php'));

    expect($moduleSelfServeRoute)->not->toBeFalse();
    expect($productDocs)->not->toBeFalse();
    expect($washFlow)->not->toBeFalse();

    // Product flow confirms the customer UI sends STOP, while route docs expose START and property gates
    // as customer-scoped lane commands. RESERVE, RELEASE, and RESET remain absent from customer flow/docs.
    expect($productDocs)->toContain('Customer->>API: POST /modules/self-serve/lane/command (STOP)');
    expect($productDocs)->toContain('customer `add_own_department_selfserve_vehicle_conditions` for scoped `START`');
    expect($productDocs)->toContain('customer `list_own_department_selfserve_vehicle_conditions` for scoped `STOP` and property gate commands');
    expect($productDocs)->toContain('Customer `START` requires an enabled self-serve lane. Customer `STOP` and property gate commands require the customer\'s active wash in the lane department.');
    expect($washFlow)->toContain('$payload[\'command\'] = $relayRole === \'PROPERTY_ENTRANCE\' ? \'OPEN_PROPERTY_ACCESS_GATE\' : \'OPEN_PROPERTY_EXIT_GATE\'');
    expect($washFlow)->toContain('$signalType = \'studio_action_gate_open\'');
    expect($moduleSelfServeRoute)
        ->toContain('if ($command === selfserve_lane_command::START)')
        ->toContain('(new selfserve_wash_flow())->synchronizeSession(')
        ->toContain('Failed to persist self-serve START session');

    $commandCases = selfserve_lane_command_route_cases($moduleSelfServeRoute);

    expect($commandCases['START'])
        ->toContain('requireSelfServeLaneCommandPermission')
        ->toContain("'modules_selfserve_lane_command_execute_start'")
        ->toContain("true\n                    );")
        ->not->toContain('requireDepartmentAccess')
        ->not->toContain('requireOperatorLaneCommandPermission');

    expect($commandCases['STOP'])
        ->toContain('requireSelfServeLaneCommandPermission')
        ->toContain("'modules_selfserve_lane_command_execute_stop'")
        ->toContain("true,\n                        true")
        ->not->toContain('requireDepartmentAccess')
        ->not->toContain('requireOperatorLaneCommandPermission');

    expect($commandCases['OPEN_PROPERTY_ACCESS_GATE'])
        ->toContain('requirePropertyGateCommandPermission')
        ->toContain("'modules_selfserve_lane_command_execute_open_property_access_gate'")
        ->not->toContain('self::requirePermission')
        ->not->toContain('requireDepartmentAccess');

    expect($commandCases['OPEN_PROPERTY_EXIT_GATE'])
        ->toContain('requirePropertyGateCommandPermission')
        ->toContain("'modules_selfserve_lane_command_execute_open_property_exit_gate'")
        ->not->toContain('self::requirePermission')
        ->not->toContain('requireDepartmentAccess');

    foreach (['RESERVE', 'RELEASE', 'RESET'] as $operatorOnlyCommand) {
        expect($commandCases[$operatorOnlyCommand])
            ->toContain('requireOperatorLaneCommandPermission')
            ->not->toContain('requireSelfServeLaneCommandPermission')
            ->not->toContain('requirePropertyGateCommandPermission');
    }
});

/**
 * @return array<string,string>
 */
function selfserve_lane_command_route_cases(string $moduleSelfServeRoute): array
{
    $cases = [];
    preg_match_all(
        '/case selfserve_lane_command::([A-Z_]+):(.*?)(?=\n\s*case selfserve_lane_command::|\n\s*}\n\s*\/\/ Execute the command)/s',
        $moduleSelfServeRoute,
        $matches,
        PREG_SET_ORDER
    );

    foreach ($matches as $match) {
        $cases[$match[1]] = $match[2];
    }

    return $cases;
}
