<?php

declare(strict_types=1);

namespace App\Framework\EventSourcing\Sagas\Examples;

use App\Framework\EventSourcing\DomainEvent;
use App\Framework\EventSourcing\Sagas\AbstractSaga;

/**
 * User Onboarding Saga Example
 *
 * Orchestrates user onboarding process:
 * 1. Email verification
 * 2. Profile setup
 * 3. Welcome email
 * 4. Feature tour activation
 *
 * Demonstrates time-based saga with multi-step coordination
 */
final readonly class UserOnboardingSaga extends AbstractSaga
{
    public function getName(): string
    {
        return 'user_onboarding';
    }

    public function subscribedTo(): array
    {
        return [
            UserRegisteredEvent::class,
            EmailVerifiedEvent::class,
            ProfileCompletedEvent::class,
            WelcomeEmailSentEvent::class,
        ];
    }

    protected function handleEvent(DomainEvent $event): array
    {
        return match ($event::class) {
            UserRegisteredEvent::class => $this->handleUserRegistered($event),
            EmailVerifiedEvent::class => $this->handleEmailVerified($event),
            ProfileCompletedEvent::class => $this->handleProfileCompleted($event),
            WelcomeEmailSentEvent::class => $this->handleWelcomeEmailSent($event),
            default => []
        };
    }

    private function handleUserRegistered(UserRegisteredEvent $event): array
    {
        // Initialize saga
        $this->updateData([
            'userId' => $event->userId,
            'email' => $event->email,
            'registeredAt' => $event->registeredAt,
            'step' => 'email_verification',
        ]);

        $this->setStepCount(4); // 4 steps total

        // Step 1: Send verification email
        return [
            new SendVerificationEmailCommand(
                userId: $event->userId,
                email: $event->email
            ),
        ];
    }

    private function handleEmailVerified(EmailVerifiedEvent $event): array
    {
        // Email verified - prompt for profile setup
        $this->updateData([
            'emailVerifiedAt' => $event->verifiedAt,
            'step' => 'profile_setup',
        ]);

        // Step 2: Send profile setup reminder
        return [
            new SendProfileSetupReminderCommand(
                userId: $this->getData('userId'),
                email: $this->getData('email')
            ),
        ];
    }

    private function handleProfileCompleted(ProfileCompletedEvent $event): array
    {
        // Profile completed - send welcome email
        $this->updateData([
            'profileCompletedAt' => $event->completedAt,
            'profileData' => $event->profileData,
            'step' => 'welcome_email',
        ]);

        // Step 3: Send welcome email
        return [
            new SendWelcomeEmailCommand(
                userId: $this->getData('userId'),
                email: $this->getData('email'),
                userName: $event->profileData['name'] ?? 'User'
            ),
        ];
    }

    private function handleWelcomeEmailSent(WelcomeEmailSentEvent $event): array
    {
        // Welcome email sent - activate feature tour
        $this->updateData([
            'welcomeEmailSentAt' => $event->sentAt,
            'step' => 'feature_tour',
        ]);

        // Step 4: Activate feature tour
        $commands = [
            new ActivateFeatureTourCommand(
                userId: $this->getData('userId')
            ),
        ];

        // Onboarding complete
        $this->complete();

        return $commands;
    }

    protected function getCompensationCommands(): array
    {
        // User onboarding has limited compensation
        // Mainly just cleanup and notifications

        $compensationCommands = [];
        $userId = $this->getData('userId');

        // Deactivate any features that were enabled
        if ($this->hasData('welcomeEmailSentAt')) {
            $compensationCommands[] = new DeactivateFeatureTourCommand($userId);
        }

        // Send onboarding failure notification to admins
        $compensationCommands[] = new NotifyOnboardingFailureCommand(
            userId: $userId,
            email: $this->getData('email'),
            failedStep: $this->getData('step'),
            reason: $this->state->errorMessage ?? 'Unknown error'
        );

        return $compensationCommands;
    }
}

// Example Events
final readonly class UserRegisteredEvent implements DomainEvent
{
    public function __construct(
        public string $userId,
        public string $email,
        public string $registeredAt
    ) {
    }
}

final readonly class EmailVerifiedEvent implements DomainEvent
{
    public function __construct(
        public string $userId,
        public string $verifiedAt
    ) {
    }
}

final readonly class ProfileCompletedEvent implements DomainEvent
{
    public function __construct(
        public string $userId,
        public array $profileData,
        public string $completedAt
    ) {
    }
}

final readonly class WelcomeEmailSentEvent implements DomainEvent
{
    public function __construct(
        public string $userId,
        public string $sentAt
    ) {
    }
}

// Example Commands
final readonly class SendVerificationEmailCommand
{
    public function __construct(
        public string $userId,
        public string $email
    ) {
    }
}

final readonly class SendProfileSetupReminderCommand
{
    public function __construct(
        public string $userId,
        public string $email
    ) {
    }
}

final readonly class SendWelcomeEmailCommand
{
    public function __construct(
        public string $userId,
        public string $email,
        public string $userName
    ) {
    }
}

final readonly class ActivateFeatureTourCommand
{
    public function __construct(
        public string $userId
    ) {
    }
}

final readonly class DeactivateFeatureTourCommand
{
    public function __construct(
        public string $userId
    ) {
    }
}

final readonly class NotifyOnboardingFailureCommand
{
    public function __construct(
        public string $userId,
        public string $email,
        public string $failedStep,
        public string $reason
    ) {
    }
}
