<?php

declare(strict_types=1);

namespace App\Listeners;

use App\Events\ServiceProvisioned;
use App\Events\SubscriptionCreated;
use App\Models\ServiceBuildMilestone;
use App\Models\WinbackRecipient;
use App\Notifications\SubscriptionCreatedNotification;
use App\Services\Provisioning\ProvisioningFactory;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Log;

class HandleSubscriptionCreated implements ShouldQueue
{
    public function __construct(
        private ProvisioningFactory $provisioningFactory,
    ) {}

    public function handle(SubscriptionCreated $event): void
    {
        Log::info("Subscription created for user #{$event->user->id}", [
            'subscription_id' => $event->subscription->id,
            'type' => $event->subscription->type,
        ]);

        // Mark any active win-back recipients as reactivated
        WinbackRecipient::query()
            ->where('user_id', $event->user->id)
            ->where('reactivated', false)
            ->whereNull('unsubscribed_at')
            ->update([
                'reactivated' => true,
                'reactivated_at' => now(),
            ]);

        // Send notification first — provisioning may fail but user should always know about the subscription
        $event->user->notify(new SubscriptionCreatedNotification($event->subscription));

        // Get the plan
        $planId = $event->subscription->plan_id;
        if ($planId) {
            $plan = \App\Models\Plan::find($planId);

            if ($plan) {
                // 14th-gen build-to-order dedicated plans get a build tracker
                // started at the "ordered" stage. The remaining stages
                // (hardware_acquired → assembly → racked → deployed) are
                // marked manually by ops as work progresses.
                $isBuildToOrder = $plan->service_type === 'dedicated'
                    && ($plan->features['lead_time_days'] ?? null) !== null;

                if ($isBuildToOrder) {
                    ServiceBuildMilestone::firstOrCreate(
                        [
                            'subscription_id' => $event->subscription->id,
                            'stage' => ServiceBuildMilestone::STAGE_ORDERED,
                        ],
                        [
                            'reached_at' => now(),
                            'note' => 'Order received. Hardware will be ordered with our supplier shortly.',
                        ],
                    );
                }

                // Automatically provision the service. For build-to-order
                // dedicated, this provision step is admin-driven and the
                // factory throws — the catch block below handles it cleanly.
                try {
                    $provisioner = $this->provisioningFactory->make($plan->service_type);
                    $service = $provisioner->provision($event->subscription);

                    ServiceProvisioned::dispatch($event->user, $service);

                    Log::info('Service provisioned successfully', [
                        'service_id' => $service->id,
                        'service_type' => $plan->service_type,
                    ]);
                } catch (\Exception $e) {
                    Log::error('Service provisioning failed', [
                        'subscription_id' => $event->subscription->id,
                        'error' => $e->getMessage(),
                    ]);

                    // Don't re-throw — the provisioning:retry command handles retries
                    // with idempotent provision() that reuses existing Service records
                }
            }
        }
    }
}
