<?php

namespace App\Jobs;

use App\Models\BulkOperationRun;
use App\Models\RestoreRun;
use App\Services\BulkOperationService;
use Filament\Notifications\Notification;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Throwable;

class BulkRestoreRunDeleteJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(
        public int $bulkRunId,
    ) {}

    public function handle(BulkOperationService $service): void
    {
        $run = BulkOperationRun::with('user')->find($this->bulkRunId);

        if (! $run || $run->status !== 'pending') {
            return;
        }

        $service->start($run);

        try {
            $itemCount = 0;
            $succeeded = 0;
            $failed = 0;
            $skipped = 0;
            $skipReasons = [];

            $chunkSize = max(1, (int) config('tenantpilot.bulk_operations.chunk_size', 10));
            $totalItems = $run->total_items ?: count($run->item_ids ?? []);
            $failureThreshold = (int) floor($totalItems / 2);

            foreach (($run->item_ids ?? []) as $restoreRunId) {
                $itemCount++;

                try {
                    /** @var RestoreRun|null $restoreRun */
                    $restoreRun = RestoreRun::withTrashed()
                        ->where('tenant_id', $run->tenant_id)
                        ->whereKey($restoreRunId)
                        ->first();

                    if (! $restoreRun) {
                        $service->recordFailure($run, (string) $restoreRunId, 'Restore run not found');
                        $failed++;

                        if ($failed > $failureThreshold) {
                            $service->abort($run, 'Circuit breaker: more than 50% of items failed.');

                            if ($run->user) {
                                Notification::make()
                                    ->title('Bulk Delete Aborted')
                                    ->body('Circuit breaker triggered: too many failures (>50%).')
                                    ->icon('heroicon-o-exclamation-triangle')
                                    ->danger()
                                    ->sendToDatabase($run->user)
                                    ->send();
                            }

                            return;
                        }

                        continue;
                    }

                    if ($restoreRun->trashed()) {
                        $service->recordSkippedWithReason($run, (string) $restoreRun->id, 'Already archived');
                        $skipped++;
                        $skipReasons['Already archived'] = ($skipReasons['Already archived'] ?? 0) + 1;

                        continue;
                    }

                    if (! $restoreRun->isDeletable()) {
                        $reason = "Not deletable (status: {$restoreRun->status})";

                        $service->recordSkippedWithReason($run, (string) $restoreRun->id, $reason);
                        $skipped++;
                        $skipReasons[$reason] = ($skipReasons[$reason] ?? 0) + 1;

                        continue;
                    }

                    $restoreRun->delete();
                    $service->recordSuccess($run);
                    $succeeded++;
                } catch (Throwable $e) {
                    $service->recordFailure($run, (string) $restoreRunId, $e->getMessage());
                    $failed++;

                    if ($failed > $failureThreshold) {
                        $service->abort($run, 'Circuit breaker: more than 50% of items failed.');

                        if ($run->user) {
                            Notification::make()
                                ->title('Bulk Delete Aborted')
                                ->body('Circuit breaker triggered: too many failures (>50%).')
                                ->icon('heroicon-o-exclamation-triangle')
                                ->danger()
                                ->sendToDatabase($run->user)
                                ->send();
                        }

                        return;
                    }
                }

                if ($itemCount % $chunkSize === 0) {
                    $run->refresh();
                }
            }

            $service->complete($run);

            if ($run->user) {
                $message = "Deleted {$succeeded} restore runs";
                if ($skipped > 0) {
                    $message .= " ({$skipped} skipped)";
                }
                if ($failed > 0) {
                    $message .= " ({$failed} failed)";
                }

                if (! empty($skipReasons)) {
                    $summary = collect($skipReasons)
                        ->sortDesc()
                        ->map(fn (int $count, string $reason) => "{$reason} ({$count})")
                        ->take(3)
                        ->implode(', ');

                    if ($summary !== '') {
                        $message .= " Skip reasons: {$summary}.";
                    }
                }

                $message .= '.';

                Notification::make()
                    ->title('Bulk Delete Completed')
                    ->body($message)
                    ->icon('heroicon-o-check-circle')
                    ->success()
                    ->sendToDatabase($run->user)
                    ->send();
            }
        } catch (Throwable $e) {
            $service->fail($run, $e->getMessage());

            $run->refresh();
            $run->load('user');

            if ($run->user) {
                Notification::make()
                    ->title('Bulk Delete Failed')
                    ->body($e->getMessage())
                    ->icon('heroicon-o-x-circle')
                    ->danger()
                    ->sendToDatabase($run->user)
                    ->send();
            }

            throw $e;
        }
    }
}
