<?php
// UssdPipeline: deterministic chain execution with early abort capability
require_once __DIR__ . '/Logger.php';

class UssdPipeline {
    private $steps = [];
    private $context = [];
    private $aborted = false;
    private $outputSent = false;

    public function __construct(array $context) {
        $this->context = $context;
    }

    public function add(callable $fn, $name = 'step') : self {
        $this->steps[] = [$fn, $name];
        return $this;
    }

    public function run() {
        foreach ($this->steps as [$fn, $name]) {
            if ($this->aborted) break;
            $start = microtime(true);
            try {
                $result = $fn($this->context, $this);
                $elapsed = round((microtime(true) - $start)*1000,2);
                Logger::log('PIPE', "Executed $name", ['ms' => $elapsed]);
                if (is_string($result) && !$this->outputSent) {
                    echo $result;
                    $this->outputSent = true;
                    $this->abort();
                }
            } catch (Throwable $e) {
                Logger::log('ERR', $e->getMessage(), ['step' => $name]);
                if (!$this->outputSent) {
                    echo 'END Temporary error. Please try again later.';
                    $this->outputSent = true;
                }
                $this->abort();
            }
        }
        if (!$this->outputSent) {
            echo 'END Service unavailable.';
        }
    }

    public function abort() { $this->aborted = true; }
    public function ctx($key, $value = null) {
        if ($value === null) return $this->context[$key] ?? null;
        $this->context[$key] = $value;
    }
}
