<?php

declare(strict_types=1);

namespace App\Framework\Telemetry\ValueObjects;

use DateTimeImmutable;

/**
 * Represents a telemetry operation (similar to a span in OpenTelemetry)
 */
final class Operation
{
    public ?DateTimeImmutable $endTime = null;

    public string $status = 'pending';

    public ?string $errorMessage = null;

    /**
     * @param string $id Unique identifier for the operation
     * @param string|null $parentId Parent operation ID if this is a child operation
     * @param string $name Name of the operation
     * @param string $type Type of operation (e.g., http, database, custom)
     * @param DateTimeImmutable $startTime Start time of the operation
     * @param array<string, mixed> $attributes Additional attributes for the operation
     */
    public function __construct(
        public readonly string $id,
        public readonly ?string $parentId,
        public readonly string $name,
        public readonly string $type,
        public readonly DateTimeImmutable $startTime,
        public array $attributes = []
    ) {
    }

    /**
     * Get the duration of the operation in milliseconds
     */
    public function getDuration(): ?float
    {
        if ($this->endTime === null) {
            return null;
        }

        return ($this->endTime->getTimestamp() - $this->startTime->getTimestamp()) * 1000 +
               ($this->endTime->format('u') - $this->startTime->format('u')) / 1000;
    }

    /**
     * Convert the operation to an array
     *
     * @return array<string, mixed>
     */
    public function toArray(): array
    {
        return [
            'id' => $this->id,
            'parent_id' => $this->parentId,
            'name' => $this->name,
            'type' => $this->type,
            'start_time' => $this->startTime->format('Y-m-d\TH:i:s.uP'),
            'end_time' => $this->endTime?->format('Y-m-d\TH:i:s.uP'),
            'duration_ms' => $this->getDuration(),
            'status' => $this->status,
            'error_message' => $this->errorMessage,
            'attributes' => $this->attributes,
        ];
    }

    /**
     * Add an attribute to the operation
     */
    public function addAttribute(string $key, mixed $value): self
    {
        $this->attributes[$key] = $value;

        return $this;
    }

    /**
     * End the operation with the given status and error message
     */
    public function end(string $status = 'success', ?string $errorMessage = null): self
    {
        $this->endTime = new DateTimeImmutable();
        $this->status = $status;
        $this->errorMessage = $errorMessage;

        return $this;
    }

    /**
     * Mark the operation as failed with the given error message
     */
    public function fail(string $errorMessage): self
    {
        return $this->end('error', $errorMessage);
    }

    /**
     * Check if the operation is completed
     */
    public function isCompleted(): bool
    {
        return $this->endTime !== null;
    }

    /**
     * Check if the operation was successful
     */
    public function isSuccessful(): bool
    {
        return $this->isCompleted() && $this->status === 'success';
    }
}
