<?php

namespace App\Jobs;

use App\Jobs\Middleware\TrackOperationRun;
use App\Models\BulkOperationRun;
use App\Models\InventorySyncRun;
use App\Models\OperationRun;
use App\Models\Tenant;
use App\Models\User;
use App\Services\BulkOperationService;
use App\Services\Intune\AuditLogger;
use App\Services\Inventory\InventorySyncService;
use App\Services\OperationRunService;
use App\Support\OperationRunLinks;
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 RuntimeException;

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

    public ?OperationRun $operationRun = null;

    /**
     * Create a new job instance.
     */
    public function __construct(
        public int $tenantId,
        public int $userId,
        public int $bulkRunId,
        public int $inventorySyncRunId,
        ?OperationRun $operationRun = null
    ) {
        $this->operationRun = $operationRun;
    }

    /**
     * Get the middleware the job should pass through.
     *
     * @return array<int, object>
     */
    public function middleware(): array
    {
        return [new TrackOperationRun];
    }

    /**
     * Execute the job.
     */
    public function handle(BulkOperationService $bulkOperationService, InventorySyncService $inventorySyncService, AuditLogger $auditLogger): void
    {
        $tenant = Tenant::query()->find($this->tenantId);
        if (! $tenant instanceof Tenant) {
            throw new RuntimeException('Tenant not found.');
        }

        $user = User::query()->find($this->userId);
        if (! $user instanceof User) {
            throw new RuntimeException('User not found.');
        }

        $bulkRun = BulkOperationRun::query()->find($this->bulkRunId);
        if (! $bulkRun instanceof BulkOperationRun) {
            throw new RuntimeException('BulkOperationRun not found.');
        }

        $run = InventorySyncRun::query()->find($this->inventorySyncRunId);
        if (! $run instanceof InventorySyncRun) {
            throw new RuntimeException('InventorySyncRun not found.');
        }

        $bulkOperationService->start($bulkRun);

        $processedPolicyTypes = [];

        // Note: The TrackOperationRun middleware will automatically set status to 'running' at start.
        // It will also handle success completion if no exceptions thrown.
        // However, InventorySyncService execution logic might be complex with partial failures.
        // We might want to explicitly update the OperationRun if partial failures occur.

        $run = $inventorySyncService->executePendingRun(
            $run,
            $tenant,
            function (string $policyType, bool $success, ?string $errorCode) use ($bulkOperationService, $bulkRun, &$processedPolicyTypes): void {
                $processedPolicyTypes[] = $policyType;

                if ($success) {
                    $bulkOperationService->recordSuccess($bulkRun);

                    return;
                }

                $bulkOperationService->recordFailure($bulkRun, $policyType, $errorCode ?? 'failed');
            },
        );

        $policyTypes = is_array($bulkRun->item_ids ?? null) ? $bulkRun->item_ids : [];
        if ($policyTypes === []) {
            $policyTypes = is_array($run->selection_payload['policy_types'] ?? null) ? $run->selection_payload['policy_types'] : [];
        }

        // --- Helper to update OperationRun with rich context ---
        $updateOpRun = function (string $outcome, array $counts = [], array $failures = []) {
            if ($this->operationRun) {
                /** @var OperationRunService $opService */
                $opService = app(OperationRunService::class);
                $opService->updateRun(
                    $this->operationRun,
                    'completed',
                    $outcome,
                    $counts,
                    $failures
                );
            }
        };
        // -----------------------------------------------------

        if ($run->status === InventorySyncRun::STATUS_SUCCESS) {
            $bulkOperationService->complete($bulkRun);

            // Update Operation Run explicitly to provide counts
            $updateOpRun('succeeded', [
                'observed' => $run->items_observed_count,
                'upserted' => $run->items_upserted_count,
            ]);

            $auditLogger->log(
                tenant: $tenant,
                action: 'inventory.sync.completed',
                context: [
                    'metadata' => [
                        'inventory_sync_run_id' => $run->id,
                        'bulk_run_id' => $bulkRun->id,
                        'selection_hash' => $run->selection_hash,
                        'observed' => $run->items_observed_count,
                        'upserted' => $run->items_upserted_count,
                    ],
                ],
                actorId: $user->id,
                actorEmail: $user->email,
                actorName: $user->name,
                resourceType: 'inventory_sync_run',
                resourceId: (string) $run->id,
            );

            Notification::make()
                ->title('Inventory sync completed')
                ->body('Inventory sync finished successfully.')
                ->success()
                ->actions($this->operationRun ? [
                    \Filament\Actions\Action::make('view_run')
                        ->label('View run')
                        ->url(OperationRunLinks::view($this->operationRun, $tenant)),
                ] : [])
                ->sendToDatabase($user)
                ->send();

            return;
        }

        if ($run->status === InventorySyncRun::STATUS_PARTIAL) {
            $bulkOperationService->complete($bulkRun);

            $updateOpRun('partially_succeeded', [
                'observed' => $run->items_observed_count,
                'upserted' => $run->items_upserted_count,
                'errors' => $run->errors_count,
            ], [
                // Minimal error summary
                ['code' => 'PARTIAL_SYNC', 'message' => "Errors: {$run->errors_count}"],
            ]);

            $auditLogger->log(
                tenant: $tenant,
                action: 'inventory.sync.partial',
                context: [
                    'metadata' => [
                        'inventory_sync_run_id' => $run->id,
                        'bulk_run_id' => $bulkRun->id,
                        'selection_hash' => $run->selection_hash,
                        'observed' => $run->items_observed_count,
                        'upserted' => $run->items_upserted_count,
                        'errors' => $run->errors_count,
                    ],
                ],
                actorId: $user->id,
                actorEmail: $user->email,
                actorName: $user->name,
                status: 'failure',
                resourceType: 'inventory_sync_run',
                resourceId: (string) $run->id,
            );

            Notification::make()
                ->title('Inventory sync completed with errors')
                ->body('Inventory sync finished with some errors. Review the run details for error codes.')
                ->warning()
                ->actions($this->operationRun ? [
                    \Filament\Actions\Action::make('view_run')
                        ->label('View run')
                        ->url(OperationRunLinks::view($this->operationRun, $tenant)),
                ] : [])
                ->sendToDatabase($user)
                ->send();

            return;
        }

        if ($run->status === InventorySyncRun::STATUS_SKIPPED) {
            $reason = (string) (($run->error_codes ?? [])[0] ?? 'skipped');

            foreach ($policyTypes as $policyType) {
                $bulkOperationService->recordSkippedWithReason($bulkRun, (string) $policyType, $reason);
            }
            $bulkOperationService->complete($bulkRun);

            $updateOpRun('failed', [], [['code' => 'SKIPPED', 'message' => $reason]]);

            $auditLogger->log(
                tenant: $tenant,
                action: 'inventory.sync.skipped',
                context: [
                    'metadata' => [
                        'inventory_sync_run_id' => $run->id,
                        'bulk_run_id' => $bulkRun->id,
                        'selection_hash' => $run->selection_hash,
                        'reason' => $reason,
                    ],
                ],
                actorId: $user->id,
                actorEmail: $user->email,
                actorName: $user->name,
                resourceType: 'inventory_sync_run',
                resourceId: (string) $run->id,
            );

            Notification::make()
                ->title('Inventory sync skipped')
                ->body('Inventory sync could not start due to locks or concurrency limits.')
                ->warning()
                ->actions($this->operationRun ? [
                    \Filament\Actions\Action::make('view_run')
                        ->label('View run')
                        ->url(OperationRunLinks::view($this->operationRun, $tenant)),
                ] : [])
                ->sendToDatabase($user)
                ->send();

            return;
        }

        $reason = (string) (($run->error_codes ?? [])[0] ?? 'failed');

        $missingPolicyTypes = array_values(array_diff($policyTypes, array_unique($processedPolicyTypes)));
        foreach ($missingPolicyTypes as $policyType) {
            $bulkOperationService->recordFailure($bulkRun, (string) $policyType, $reason);
        }

        $bulkOperationService->complete($bulkRun);

        $updateOpRun('failed', [], [['code' => 'FAILED', 'message' => $reason]]);

        $auditLogger->log(
            tenant: $tenant,
            action: 'inventory.sync.failed',
            context: [
                'metadata' => [
                    'inventory_sync_run_id' => $run->id,
                    'bulk_run_id' => $bulkRun->id,
                    'selection_hash' => $run->selection_hash,
                    'reason' => $reason,
                ],
            ],
            actorId: $user->id,
            actorEmail: $user->email,
            actorName: $user->name,
            status: 'failure',
            resourceType: 'inventory_sync_run',
            resourceId: (string) $run->id,
        );

        Notification::make()
            ->title('Inventory sync failed')
            ->body('Inventory sync finished with errors.')
            ->danger()
            ->actions($this->operationRun ? [
                \Filament\Actions\Action::make('view_run')
                    ->label('View run')
                    ->url(OperationRunLinks::view($this->operationRun, $tenant)),
            ] : [])
            ->sendToDatabase($user)
            ->send();
    }
}
