<?php

declare(strict_types=1);

namespace App\Framework\EventSourcing\Sagas;

use App\Framework\EventSourcing\DomainEvent;

/**
 * Abstract Saga Base Class
 *
 * Provides common functionality for saga implementations
 * Including compensation tracking and state management
 */
abstract readonly class AbstractSaga implements SagaInterface
{
    public function __construct(
        protected SagaState $state
    ) {
    }

    abstract public function getName(): string;

    abstract public function subscribedTo(): array;

    abstract protected function handleEvent(DomainEvent $event): array;

    abstract protected function getCompensationCommands(): array;

    public function getSagaId(): SagaId
    {
        return $this->state->sagaId;
    }

    public function getState(): SagaState
    {
        return $this->state;
    }

    public function isCompleted(): bool
    {
        return $this->state->status === SagaStatus::COMPLETED;
    }

    /**
     * Handle event with state management
     */
    public function handle(DomainEvent $event): array
    {
        if ($this->isCompleted()) {
            return []; // Saga already completed
        }

        try {
            // Let subclass handle the event
            $commands = $this->handleEvent($event);

            // Update state after successful handling
            $newState = $this->state->withStepCompleted();
            $this->updateState($newState);

            return $commands;
        } catch (\Exception $e) {
            // Mark saga as failed
            $newState = $this->state->withFailed($e->getMessage());
            $this->updateState($newState);

            throw $e;
        }
    }

    /**
     * Compensate saga with automatic state management
     */
    public function compensate(string $reason): array
    {
        // Mark as compensating
        $newState = $this->state->withCompensating();
        $this->updateState($newState);

        try {
            // Get compensation commands from subclass
            $commands = $this->getCompensationCommands();

            // Mark as compensated
            $newState = $this->state->withCompensated();
            $this->updateState($newState);

            return $commands;
        } catch (\Exception $e) {
            // Compensation failed - keep in compensating state
            error_log("Compensation failed for saga {$this->state->sagaId->toString()}: {$e->getMessage()}");

            throw $e;
        }
    }

    /**
     * Update saga data
     */
    protected function updateData(array $data): void
    {
        $newState = $this->state->withData($data);
        $this->updateState($newState);
    }

    /**
     * Mark saga as completed
     */
    protected function complete(): void
    {
        $newState = $this->state->withCompleted();
        $this->updateState($newState);
    }

    /**
     * Update internal state (creates new instance due to readonly)
     */
    private function updateState(SagaState $newState): void
    {
        // Create new saga instance with updated state
        // This is handled by the repository when saving
        $this->state = $newState;
    }

    /**
     * Get data value from saga state
     */
    protected function getData(string $key, mixed $default = null): mixed
    {
        return $this->state->data[$key] ?? $default;
    }

    /**
     * Check if data key exists
     */
    protected function hasData(string $key): bool
    {
        return isset($this->state->data[$key]);
    }

    /**
     * Set total step count for progress tracking
     */
    protected function setStepCount(int $count): void
    {
        $newState = $this->state->withStepCount($count);
        $this->updateState($newState);
    }
}
