<?php

declare(strict_types=1);

namespace App\Framework\ErrorBoundaries;

use App\Framework\Exception\ExceptionContext;
use App\Framework\Exception\FrameworkException;

/**
 * Exception thrown when operation exceeds timeout
 */
final class BoundaryTimeoutException extends FrameworkException
{
    public function __construct(
        string $message,
        private readonly string $boundaryName,
        private readonly float $executionTime,
        private readonly float $timeoutLimit = 0.0,
        int $code = 0
    ) {
        parent::__construct(
            message: $message,
            context: ExceptionContext::forOperation('error_boundary_timeout', 'ErrorBoundary')
                ->withData([
                    'boundary_name' => $boundaryName,
                    'execution_time' => $executionTime,
                    'timeout_limit' => $timeoutLimit,
                    'overage' => max(0.0, $executionTime - $timeoutLimit),
                    'hard_timeout' => $timeoutLimit > 0.0 && $executionTime > $timeoutLimit,
                ]),
            code: $code
        );
    }

    /**
     * Get the boundary name where timeout occurred
     */
    public function getBoundaryName(): string
    {
        return $this->boundaryName;
    }

    /**
     * Get the actual execution time in seconds
     */
    public function getExecutionTime(): float
    {
        return $this->executionTime;
    }

    /**
     * Get the timeout limit in seconds
     */
    public function getTimeoutLimit(): float
    {
        return $this->timeoutLimit;
    }

    /**
     * Get how much the execution exceeded the timeout
     */
    public function getOverage(): float
    {
        return max(0.0, $this->executionTime - $this->timeoutLimit);
    }

    /**
     * Check if this was a hard timeout (exceeded limit)
     */
    public function isHardTimeout(): bool
    {
        return $this->timeoutLimit > 0.0 && $this->executionTime > $this->timeoutLimit;
    }

    /**
     * Get human-readable timeout information
     */
    public function getTimeoutInfo(): string
    {
        $info = "Boundary '{$this->boundaryName}' timed out after {$this->executionTime}s";

        if ($this->timeoutLimit > 0.0) {
            $info .= " (limit: {$this->timeoutLimit}s, overage: " . $this->getOverage() . "s)";
        }

        return $info;
    }

    /**
     * Create timeout exception with limit
     */
    public static function withLimit(
        string $boundaryName,
        float $executionTime,
        float $timeoutLimit
    ): self {
        return new self(
            "Operation in boundary '{$boundaryName}' exceeded timeout of {$timeoutLimit}s (took {$executionTime}s)",
            $boundaryName,
            $executionTime,
            $timeoutLimit
        );
    }

    /**
     * Create timeout exception without specific limit
     */
    public static function withoutLimit(string $boundaryName, float $executionTime): self
    {
        return new self(
            "Operation in boundary '{$boundaryName}' timed out after {$executionTime}s",
            $boundaryName,
            $executionTime
        );
    }
}
