<?php

namespace routes;

use classes\authentication;
use classes\email;
use classes\response;
use classes\router;
use classes\selfserve;
use classes\stripe;
use modules\selfserve\helpers\selfserve_lane_command;
use modules\selfserve\helpers\selfserve_lane_relay;
use objects\departments_o;
use objects\logs_o;
use objects\orders_o;
use objects\stripe_module_customers_o;
use traits\route_t;

class moduleSelfServeRoute
{
    use route_t;

    public function run(): void
    {
        global /** @var response $response */
        /** @var router $router */
        $router, $response;


        /** Modules > Self Serve > Lane > Status */
        $this->get('/modules/self-serve/lane/status', function () {
            global $response;
            self::requirePermission('modules_selfserve_lane_status_view');
            $selfserve = new selfserve();
            $lane_id = self::isParametersSet(['lane_id']) ? (int)self::getParameter('lane_id') : 1;
            $lane = $selfserve->lane($lane_id);
            $response->success([
                'id' => $lane->id,
                'status' => $lane->getLaneStatus()->name,
                'mode' => $lane->getLaneMode()->name,
                'state' => $lane->getLaneState()->name,
                'wash_start_time' => $lane->getWashStartTime(),
                'elapsed_wash_time' => $lane->getElapsedWashTime(),
                'license_plate' => $lane->getLicensePlate(),
                'customer_number' => $lane->getCustomerNumber(),
            ]);
        },
            [
                'modules_selfserve_lane_status_view' => 'View self-serve lane status',
            ]
        );

        /** Modules > Self Serve > Lane > Command */
        $this->post('/modules/self-serve/lane/command', function () {
            global $response;
            self::requirePermission('modules_selfserve_lane_command_execute');
            $selfserve = new selfserve();
            // Get the request user
            $user = (new authentication())->get_user();
            $param_lane_id = 'lane_id';
            $param_command = 'command';
            // Validate parameters
            self::requireParameters([$param_lane_id, $param_command]);
            $lane_id = (int)$this->getParameter($param_lane_id);
            self::requireType($lane_id, self::type_int());
            self::requireMinValue($lane_id, 1);
            $commandParam = (string)$this->getParameter($param_command);
            self::requireType($commandParam, self::type_string());
            // Get the lane and command
            $lane = $selfserve->lane($lane_id);
            // If the user has the bypass permission, set the lane to bypass customer number validation
            if (self::hasPermission('modules_selfserve_lane_command_bypass_customer_number_validation')) {
                $lane->setBypassCustomerNumberValidation(true);
            }
            $command = selfserve_lane_command::tryFrom($commandParam);
            if ($command === null) {
                $response->error("Invalid command: " . $commandParam);
            }
            // Require permissions for specific commands
            switch ($command) {
                case selfserve_lane_command::START:
                    self::requirePermission('modules_selfserve_lane_command_execute_start');
                    break;
                case selfserve_lane_command::STOP:
                    self::requirePermission('modules_selfserve_lane_command_execute_stop');
                    break;
                case selfserve_lane_command::RESERVE:
                    self::requirePermission('modules_selfserve_lane_command_execute_reserve');
                    break;
                case selfserve_lane_command::RELEASE:
                    self::requirePermission('modules_selfserve_lane_command_execute_release');
                    break;
                case selfserve_lane_command::RESET:
                    self::requirePermission('modules_selfserve_lane_command_execute_reset');
                    break;
            }
            // Execute the command
            try {
                $args = new \modules\selfserve\classes\selfserve_lane_command_arguments();
                $args->setParameters([
                    ...$this->getParametersAsArray(), // Pass all parameters
                    'customer_number' => (int)$user->customer_number->value(), // Get customer number from request user
                    ]);
                $lane->execute($command, $args);
                $response->success([
                    'id' => $lane->id,
                    'status' => $lane->getLaneStatus()->name,
                    'mode' => $lane->getLaneMode()->name,
                    'state' => $lane->getLaneState()->name,
                    'wash_start_time' => $lane->getWashStartTime(),
                    'elapsed_wash_time' => $lane->getElapsedWashTime(),
                    'license_plate' => $lane->getLicensePlate(),
                    'customer_number' => $lane->getCustomerNumber(),
                ]);
            } catch (\Exception $e) {
                $response->error("Failed to execute command: " . $e->getMessage());
            }
        },
            [
                'modules_selfserve_lane_command_execute' => 'Execute self-serve lane command. This is required together with specific command permissions below.',
                'modules_selfserve_lane_command_execute_start' => 'Execute self-serve lane START command',
                'modules_selfserve_lane_command_execute_stop' => 'Execute self-serve lane STOP command',
                'modules_selfserve_lane_command_execute_reserve' => 'Execute self-serve lane RESERVE command',
                'modules_selfserve_lane_command_execute_release' => 'Execute self-serve lane RELEASE command (Release the lane reservation and reset its state)',
                'modules_selfserve_lane_command_execute_reset' => 'Execute self-serve lane RESET command',
                'modules_selfserve_lane_command_bypass_customer_number_validation' => 'Bypass customer number validation when executing commands',
            ]
        );

        /** Modules > Self Serve > Lane > Allowed services (derived from shown tasks) */
        $this->post('/modules/self-serve/lane/services/allowed', function () {
            global $response;
            self::requirePermission('modules_selfserve_lane_services_set_allowed');
            $selfserve = new selfserve();
            // Validate parameters
            self::requireParameters(['lane_id']);
            $lane_id = (int)$this->getParameter('lane_id');
            self::requireType($lane_id, self::type_int());
            self::requireMinValue($lane_id, 1);
            // Parse task_ids from request (optional, may be empty to clear)
            $task_ids_param = $this->isParametersSet(['task_ids']) ? $this->getParameter('task_ids') : [];
            if (is_string($task_ids_param)) {
                $decoded = json_decode($task_ids_param, true);
                if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
                    $task_ids_param = $decoded;
                } else {
                    $task_ids_param = array_filter(array_map(fn($s) => (int)trim($s), explode(',', $task_ids_param)), fn($v) => $v > 0);
                }
            }
            if (!is_array($task_ids_param)) {
                $response->error('Invalid format for task_ids. Expected array, JSON array, or comma-separated string of IDs.', 400);
            }
            $task_ids = array_values(array_unique(array_map(fn($v) => (int)$v, $task_ids_param)));
            // Build allowed services from provided tasks
            $lane = $selfserve->lane($lane_id);
            $allowed_services = [];
            foreach ($task_ids as $tid) {
                if ($tid <= 0) continue;
                $t = new \objects\department_selfserve_tasks_o();
                $t->select($tid);
                if (!$t->exists()) continue; // ignore unknown ids
                // Validate the task belongs to the same lane
                if ((int)$t->lane->value() !== $lane_id) continue;
                // Merge services (if any)
                $services = (array)$t->services->value();
                foreach ($services as $srv) {
                    $name = strtoupper((string)$srv);
                    if (!in_array($name, $allowed_services, true)) {
                        $allowed_services[] = $name;
                    }
                }
            }
            // Persist on lane cache (overwrites previous allowed services)
            $lane->setLaneCache($lane_id, $lane::CACHE_SELFSERVE_LANE_KEY_ALLOWED_SERVICES, $allowed_services);
            $response->success(['lane_id' => $lane_id, 'allowed_services' => $allowed_services]);
        }, [
            'modules_selfserve_lane_services_set_allowed' => 'Set allowed services for a lane based on currently shown tasks (post-Q&A)'
        ]);

        /** Modules > Self Serve > Lane > Relay > Enable MACHINE (manual, gated by allowed services) */
        $this->post('/modules/self-serve/lane/relay/machine/enable', function () {
            global $response;
            self::requirePermission('modules_selfserve_lane_relay_enable_machine');
            $selfserve = new selfserve();
            // Validate parameters
            self::requireParameters(['lane_id']);
            $lane_id = (int)$this->getParameter('lane_id');
            self::requireType($lane_id, self::type_int());
            self::requireMinValue($lane_id, 1);
            $duration = null;
            if ($this->isParametersSet(['duration'])) {
                $duration = (int)$this->getParameter('duration');
                self::requireMinValue($duration, 1);
            }
            $lane = $selfserve->lane($lane_id);
            try {
                $lane->turnOnRelay(selfserve_lane_relay::MACHINE, $duration);
                $response->success(['lane_id' => $lane_id, 'relay' => 'MACHINE', 'enabled' => true, 'duration' => $duration]);
            } catch (\Exception $e) {
                $msg = $e->getMessage();
                // If gating prevented activation, respond with 403
                if (str_contains(strtoupper($msg), 'NOT ALLOWED')) {
                    $response->error($msg, 403);
                }
                $response->error('Failed to enable MACHINE relay: ' . $msg, 400);
            }
        }, [
            'modules_selfserve_lane_relay_enable_machine' => 'Manually enable MACHINE relay for a lane (requires allowed task to be present)'
        ]);

        /** Modules > Self Serve > Lane > Force > MACHINE enable (simulate started wash) */
        $this->post('/modules/self-serve/lane/force/machine/enable', function () {
            global $response;
            self::requirePermission('modules_selfserve_lane_force_machine_enable');
            $selfserve = new selfserve();
            // Validate parameters
            self::requireParameters(['lane_id']);
            $lane_id = (int)$this->getParameter('lane_id');
            self::requireType($lane_id, self::type_int());
            self::requireMinValue($lane_id, 1);
            $duration = null;
            if ($this->isParametersSet(['duration'])) {
                $duration = (int)$this->getParameter('duration');
                self::requireMinValue($duration, 1);
            }
            $license_plate = $this->isParametersSet(['license_plate']) ? (string)$this->getParameter('license_plate') : null;
            $lane = $selfserve->lane($lane_id);
            try {
                // If lane looks idle/available, mark as occupied and in wash, set timers
                if ($lane->getLaneStatus()->equals(\modules\selfserve\helpers\selfserve_lane_status::AVAILABLE)) {
                    $lane->setLaneStatus(\modules\selfserve\helpers\selfserve_lane_status::OCCUPIED);
                }
                if (!$lane->getLaneState()->equals(\modules\selfserve\helpers\selfserve_lane_state::IN_WASH)) {
                    $lane->setLaneState(\modules\selfserve\helpers\selfserve_lane_state::IN_WASH);
                }
                if ($license_plate !== null && $license_plate !== '') {
                    $lane->setLicensePlate($license_plate);
                }
                if ((int)$lane->getWashStartTime() <= 0) {
                    $lane->setWashStartTime(time());
                }
                // Force enable the machine relay (bypass gating)
                $lane->forceTurnOnMachineRelay($duration);
                // Reflect relay state explicitly
                try { $lane->setLaneState(\modules\selfserve\helpers\selfserve_lane_state::MACHINE_RELAY_ON); } catch (\Throwable $ignored) {}
                $response->success([
                    'lane_id' => $lane_id,
                    'forced' => true,
                    'machine' => 'ENABLED',
                    'duration' => $duration,
                    'status' => $lane->getLaneStatus()->name,
                    'state' => $lane->getLaneState()->name,
                    'wash_start_time' => $lane->getWashStartTime(),
                ]);
            } catch (\Exception $e) {
                $response->error('Failed to force-enable MACHINE: ' . $e->getMessage(), 400);
            }
        }, [
            'modules_selfserve_lane_force_machine_enable' => 'Force enable MACHINE relay and mark lane as in-wash (superusers only)'
        ]);

        /** Modules > Self Serve > Lane > Force > MACHINE disable (simulate started wash without machine) */
        $this->post('/modules/self-serve/lane/force/machine/disable', function () {
            global $response;
            self::requirePermission('modules_selfserve_lane_force_machine_disable');
            $selfserve = new selfserve();
            // Validate parameters
            self::requireParameters(['lane_id']);
            $lane_id = (int)$this->getParameter('lane_id');
            self::requireType($lane_id, self::type_int());
            self::requireMinValue($lane_id, 1);
            $license_plate = $this->isParametersSet(['license_plate']) ? (string)$this->getParameter('license_plate') : null;
            $lane = $selfserve->lane($lane_id);
            try {
                // Ensure lane reflects an ongoing wash, but with machine off
                if ($lane->getLaneStatus()->equals(\modules\selfserve\helpers\selfserve_lane_status::AVAILABLE)) {
                    $lane->setLaneStatus(\modules\selfserve\helpers\selfserve_lane_status::OCCUPIED);
                }
                if (!$lane->getLaneState()->equals(\modules\selfserve\helpers\selfserve_lane_state::IN_WASH)) {
                    $lane->setLaneState(\modules\selfserve\helpers\selfserve_lane_state::IN_WASH);
                }
                if ($license_plate !== null && $license_plate !== '') {
                    $lane->setLicensePlate($license_plate);
                }
                if ((int)$lane->getWashStartTime() <= 0) {
                    $lane->setWashStartTime(time());
                }
                // Turn off the machine relay (do not swallow errors)
                $lane->forceTurnOffMachineRelay();
                // Reflect relay state explicitly
                try { $lane->setLaneState(\modules\selfserve\helpers\selfserve_lane_state::MACHINE_RELAY_OFF); } catch (\Throwable $ignored) {}
                $response->success([
                    'lane_id' => $lane_id,
                    'forced' => true,
                    'machine' => 'DISABLED',
                    'status' => $lane->getLaneStatus()->name,
                    'state' => $lane->getLaneState()->name,
                    'wash_start_time' => $lane->getWashStartTime(),
                ]);
            } catch (\Exception $e) {
                $response->error('Failed to force-disable MACHINE: ' . $e->getMessage(), 400);
            }
        }, [
            'modules_selfserve_lane_force_machine_disable' => 'Force disable MACHINE relay but keep lane as in-wash (superusers only)'
        ]);
    }
}