<?php

namespace modules\selfserve\classes;

class selfserve_studio_actions
{
    public const EVENT_WASH_START_COMMAND = 'wash_start_command';
    public const EVENT_WASH_STOP_COMMAND = 'wash_stop_command';
    public const EVENT_MACHINE_START_TRIGGERED = 'machine_start_triggered';

    public const MODE_MANUAL = 'manual';
    public const MODE_MACHINE = 'machine';
    public const MODE_BOTH = 'both';

    public const OP_OPEN_PROPERTY_ENTRANCE_GATE = 'open_property_entrance_gate';
    public const OP_OPEN_PROPERTY_EXIT_GATE = 'open_property_exit_gate';
    public const OP_OPEN_LANE_ENTRANCE_PORT = 'open_lane_entrance_port';
    public const OP_OPEN_LANE_EXIT_PORT = 'open_lane_exit_port';
    public const OP_SET_CLEANER_RELAY = 'set_cleaner_relay';
    public const OP_SET_MACHINE_RELAY = 'set_machine_relay';
    public const OP_SET_PROGRAM_PICKER_RELAY = 'set_program_picker_relay';

    public const FAILURE_CONTINUE = 'continue';
    public const FAILURE_BLOCK = 'block';

    /**
     * @return array<int,string>
     */
    public static function events(): array
    {
        return [
            self::EVENT_WASH_START_COMMAND,
            self::EVENT_WASH_STOP_COMMAND,
            self::EVENT_MACHINE_START_TRIGGERED,
        ];
    }

    /**
     * @return array<int,string>
     */
    public static function washModes(): array
    {
        return [
            self::MODE_MANUAL,
            self::MODE_MACHINE,
            self::MODE_BOTH,
        ];
    }

    /**
     * @return array<int,string>
     */
    public static function operations(): array
    {
        return [
            self::OP_OPEN_PROPERTY_ENTRANCE_GATE,
            self::OP_OPEN_PROPERTY_EXIT_GATE,
            self::OP_OPEN_LANE_ENTRANCE_PORT,
            self::OP_OPEN_LANE_EXIT_PORT,
            self::OP_SET_CLEANER_RELAY,
            self::OP_SET_MACHINE_RELAY,
            self::OP_SET_PROGRAM_PICKER_RELAY,
        ];
    }

    /**
     * @return array<int,string>
     */
    public static function failurePolicies(): array
    {
        return [
            self::FAILURE_CONTINUE,
            self::FAILURE_BLOCK,
        ];
    }

    public static function isRelayOperation(string $operation): bool
    {
        return in_array($operation, [
            self::OP_SET_CLEANER_RELAY,
            self::OP_SET_MACHINE_RELAY,
            self::OP_SET_PROGRAM_PICKER_RELAY,
        ], true);
    }

    public static function isOpenOperation(string $operation): bool
    {
        return in_array($operation, [
            self::OP_OPEN_PROPERTY_ENTRANCE_GATE,
            self::OP_OPEN_PROPERTY_EXIT_GATE,
            self::OP_OPEN_LANE_ENTRANCE_PORT,
            self::OP_OPEN_LANE_EXIT_PORT,
        ], true);
    }

    public static function relayRoleForOperation(string $operation): string
    {
        return match ($operation) {
            self::OP_OPEN_PROPERTY_ENTRANCE_GATE => 'PROPERTY_ENTRANCE',
            self::OP_OPEN_PROPERTY_EXIT_GATE => 'PROPERTY_EXIT',
            self::OP_OPEN_LANE_ENTRANCE_PORT => 'ENTRY',
            self::OP_OPEN_LANE_EXIT_PORT => 'EXIT',
            self::OP_SET_CLEANER_RELAY => 'CLEANER',
            self::OP_SET_MACHINE_RELAY => 'MACHINE',
            self::OP_SET_PROGRAM_PICKER_RELAY => 'PROGRAM_PICKER',
            default => 'ACTION',
        };
    }

    public static function eventLabel(string $event): string
    {
        return match ($event) {
            self::EVENT_WASH_START_COMMAND => 'When wash starts',
            self::EVENT_WASH_STOP_COMMAND => 'When wash stops',
            self::EVENT_MACHINE_START_TRIGGERED => 'When the machine start button is triggered',
            default => 'On action event',
        };
    }

    public static function operationLabel(string $operation, ?bool $relayState = null): string
    {
        $state = $relayState === null ? '' : ($relayState ? 'ON ' : 'OFF ');
        return match ($operation) {
            self::OP_OPEN_PROPERTY_ENTRANCE_GATE => 'Open property entrance gate',
            self::OP_OPEN_PROPERTY_EXIT_GATE => 'Open property exit gate',
            self::OP_OPEN_LANE_ENTRANCE_PORT => 'Open lane entrance port',
            self::OP_OPEN_LANE_EXIT_PORT => 'Open lane exit port',
            self::OP_SET_CLEANER_RELAY => 'Turn ' . $state . 'CLEANER',
            self::OP_SET_MACHINE_RELAY => 'Turn ' . $state . 'MACHINE',
            self::OP_SET_PROGRAM_PICKER_RELAY => 'Turn ' . $state . 'PROGRAM PICKER',
            default => 'Action',
        };
    }

    public static function runtimeStageForEvent(string $event): string
    {
        return match ($event) {
            self::EVENT_WASH_START_COMMAND => 'start',
            self::EVENT_WASH_STOP_COMMAND => 'stop',
            self::EVENT_MACHINE_START_TRIGGERED => 'machine_start',
            default => 'action',
        };
    }

    /**
     * @param array<string,mixed> $action
     * @return array<string,mixed>
     */
    public static function normalize(array $action): array
    {
        $event = strtolower(trim((string)($action['event'] ?? self::EVENT_WASH_START_COMMAND)));
        if (!in_array($event, self::events(), true)) {
            $event = self::EVENT_WASH_START_COMMAND;
        }

        $operation = strtolower(trim((string)($action['operation'] ?? self::OP_OPEN_LANE_ENTRANCE_PORT)));
        if (!in_array($operation, self::operations(), true)) {
            $operation = self::OP_OPEN_LANE_ENTRANCE_PORT;
        }

        $washMode = strtolower(trim((string)($action['wash_mode'] ?? self::MODE_BOTH)));
        if (!in_array($washMode, self::washModes(), true)) {
            $washMode = self::MODE_BOTH;
        }

        $options = is_array($action['options'] ?? null) ? (array)$action['options'] : [];
        $failurePolicy = strtolower(trim((string)($options['failure_policy'] ?? self::FAILURE_CONTINUE)));
        if (!in_array($failurePolicy, self::failurePolicies(), true)) {
            $failurePolicy = self::FAILURE_CONTINUE;
        }

        $relayState = array_key_exists('relay_state', $action)
            ? filter_var($action['relay_state'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
            : null;
        if (self::isRelayOperation($operation) && $relayState === null) {
            $relayState = true;
        }

        $label = trim((string)($action['name'] ?? $action['label'] ?? ''));
        if ($label === '') {
            $label = self::operationLabel($operation, $relayState);
        }

        return [
            'id' => (int)($action['id'] ?? 0),
            'department' => (int)($action['department'] ?? 0),
            'lane' => (int)($action['lane'] ?? 0),
            'product' => (int)($action['product'] ?? 0),
            'machine_type_id' => self::nullableInt($action['machine_type_id'] ?? null),
            'condition_id' => self::nullableInt($action['condition_id'] ?? null),
            'name' => $label,
            'description' => (string)($action['description'] ?? ''),
            'event' => $event,
            'wash_mode' => $washMode,
            'operation' => $operation,
            'relay_state' => $relayState,
            'enabled' => filter_var($action['enabled'] ?? true, FILTER_VALIDATE_BOOLEAN),
            'order_priority' => (int)($action['order_priority'] ?? 0),
            'options' => [
                'delay_ms' => max(0, (int)($options['delay_ms'] ?? 0)),
                'toggle_after_seconds' => self::nullableInt($options['toggle_after_seconds'] ?? null),
                'retry_count' => max(0, min(3, (int)($options['retry_count'] ?? 0))),
                'failure_policy' => $failurePolicy,
                'record_event' => filter_var($options['record_event'] ?? true, FILTER_VALIDATE_BOOLEAN),
            ],
        ];
    }

    private static function nullableInt(mixed $value): ?int
    {
        if ($value === null || $value === '' || $value === 'null') {
            return null;
        }
        $intValue = (int)$value;
        return $intValue <= 0 ? null : $intValue;
    }
}
