<?php

namespace modules\selfserve\traits;
require_once WD . '/modules/selfserve/helpers/selfserve_lane_status.php';
require_once WD . '/modules/selfserve/classes/selfserve_lane.php';
require_once WD . '/modules/selfserve/helpers/selfserve_lane_command.php';
require_once WD . '/modules/selfserve/helpers/selfserve_lane_mode.php';
require_once WD . '/modules/selfserve/helpers/selfserve_lane_port.php';
require_once WD . '/modules/selfserve/helpers/selfserve_lane_state.php';
require_once WD . '/modules/selfserve/classes/selfserve_lane_command_arguments.php';

use Exception;
use modules\selfserve\classes\selfserve_lane;
use modules\selfserve\classes\selfserve_lane_command_arguments;
use modules\selfserve\helpers\selfserve_lane_command;
use modules\selfserve\helpers\selfserve_lane_log_action;
use modules\selfserve\helpers\selfserve_lane_mode;
use modules\selfserve\helpers\selfserve_lane_port;
use modules\selfserve\helpers\selfserve_lane_state;
use modules\selfserve\helpers\selfserve_lane_status;
use objects\users_o;

trait selfserve_lane_command_t
{
    /**
     * Execute a command on a self-serve lane
     * @param selfserve_lane_command $command The command to execute
     * @param selfserve_lane_command_arguments $arguments The arguments for the command
     * @return selfserve_lane|selfserve_lane_command_t
     * @throws Exception If the command cannot be executed
     */
    public function execute(selfserve_lane_command $command, selfserve_lane_command_arguments $arguments): self
    {
        // Handle the command
        switch ($command) {
            case selfserve_lane_command::RESERVE:
                // Require lane to be available before reserving
                if (!$this->getLaneStatus()->equals(selfserve_lane_status::AVAILABLE)) throw new \RuntimeException("Cannot reserve lane: Lane is not available.");
                // Require the customer number
                if (empty($customer_number = $arguments->customer_number)) throw new \InvalidArgumentException("Customer number is required to reserve the lane.");
                // Validate customer number
                if (!is_numeric($customer_number) || (int)$customer_number <= 0) throw new \InvalidArgumentException("Invalid customer number: " . $customer_number);
                if (!(new users_o())->getUserByCustomerNumber((int)$customer_number)->exists()) throw new \InvalidArgumentException("Customer number does not exist: " . $customer_number);
                // Set the customer number
                $this->setCustomerNumber($customer_number);
                // Set the lane status to RESERVED when reserved
                $this->setLaneStatus(selfserve_lane_status::RESERVED);
                // Set the reservation time
                $this->setReservationStartTime(time());
                // Log the lane reserve event
                $this->logLaneAction(selfserve_lane_log_action::RESERVE_LANE);
                break;
            case selfserve_lane_command::RELEASE:
                // Require lane to be reserved before releasing
                if (!$this->getLaneStatus()->equals(selfserve_lane_status::RESERVED)) throw new \RuntimeException("Cannot release lane: Lane is not reserved.");
                // Check if the customer number equals the argument customer number
                if (($this->getCustomerNumber() !== $arguments->customer_number) && !$this->isBypassCustomerNumberValidation()) {
                    throw new \InvalidArgumentException("Customer number mismatch: Lane customer number " . $this->getCustomerNumber() . " does not match argument customer number " . $arguments->customer_number);
                }
                // Reset the lane
                self::execute(selfserve_lane_command::RESET, new selfserve_lane_command_arguments());
                // Log the lane release event
                $this->logLaneAction(selfserve_lane_log_action::RELEASE_LANE);
                break;
            case selfserve_lane_command::START:
                // Require lane to be available before starting
                if (!$this->getLaneStatus()->equals(selfserve_lane_status::AVAILABLE)) throw new \RuntimeException("Cannot start lane: Lane is not available.");
                // Require the customer number
                if (empty($customer_number = $arguments->customer_number)) throw new \InvalidArgumentException("Customer number is required to start the lane.");
                // Require the license plate
                if (empty($license_plate = $arguments->license_plate)) throw new \InvalidArgumentException("License plate is required to start the lane.");
                // Validate customer number
                if (!is_numeric($customer_number) || (int)$customer_number <= 0) throw new \InvalidArgumentException("Invalid customer number: " . $customer_number);
                if (!(new users_o())->getUserByCustomerNumber((int)$customer_number)->exists()) throw new \InvalidArgumentException("Customer number does not exist: " . $customer_number);
                // Set the customer number and license plate
                $this->setCustomerNumber($customer_number);
                $this->setLicensePlate($license_plate);
                // Set the lane status to OCCUPIED when started
                $this->setLaneStatus(selfserve_lane_status::OCCUPIED);
                // Set the lane state to IN_WASH
                $this->setLaneState(selfserve_lane_state::IN_WASH);
                // Open the entrance port
                $this->open(selfserve_lane_port::ENTRANCE);
                // Start the wash timer
                $this->setWashStartTime(time());
                // Log the lane start event
                $this->logLaneAction(selfserve_lane_log_action::START_WASH);
                break;
            case selfserve_lane_command::STOP:
                // Require lane to be occupied before stopping
                if (!$this->getLaneStatus()->equals(selfserve_lane_status::OCCUPIED)) throw new \RuntimeException("Cannot stop lane: Lane is not occupied.");
                // Check if the customer number equals the argument customer number
                if (($this->getCustomerNumber() !== $arguments->customer_number) && !$this->isBypassCustomerNumberValidation()) {
                    throw new \InvalidArgumentException("Customer number mismatch: Lane customer number " . $this->getCustomerNumber() . " does not match argument customer number " . $arguments->customer_number);
                }
                // Invoice the customer
                $this->invoice();
                // Open the exit port
                $this->open(selfserve_lane_port::EXIT);
                // Log the lane stop event
                $this->logLaneAction(selfserve_lane_log_action::STOP_WASH);
                // Reset the lane
                self::execute(selfserve_lane_command::RESET, new selfserve_lane_command_arguments());
                break;
            case selfserve_lane_command::RESET:
                // Reset the lane to default states
                $this->setLaneStatus(selfserve_lane_status::AVAILABLE);
                $this->setLaneMode(selfserve_lane_mode::MANUAL);
                $this->setLaneState(selfserve_lane_state::IDLE);
                $this->setWashStartTime(self::DEFAULT_WASH_START_TIME);
                $this->setCustomerNumber(self::DEFAULT_CUSTOMER_NUMBER);
                $this->setLicensePlate(self::DEFAULT_LICENSE_PLATE);
                $this->setReservationStartTime(null);
                break;
            default:
                throw new \InvalidArgumentException("Unknown command: " . $command->name);
        }
        return $this;
    }
}