<?php

namespace App\Jobs;

use App\Models\Upload;
use App\Models\PhoneResult;
use App\Services\PhoneProcessingService;
use App\Services\FileParserService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;

/**
 * Background job that processes an uploaded file.
 *
 * Flow:
 *  1. Parse file → raw strings
 *  2. Process through PhoneProcessingService (normalization, validation, scoring)
 *  3. Bulk insert PhoneResult records
 *  4. Update Upload stats
 *
 * Dispatched to the 'processing' queue — configure supervisor to run:
 *   php artisan queue:work --queue=processing --timeout=3600
 */
class ProcessUploadJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $timeout = 3600;  // 1 hour max
    public int $tries   = 3;

    public function __construct(public readonly int $uploadId)
    {
        $this->onQueue('processing');
    }

    public function handle(
        PhoneProcessingService $processor,
        FileParserService $parser
    ): void {
        $upload = Upload::findOrFail($this->uploadId);

        try {
            $upload->update(['status' => Upload::STATUS_PROCESSING]);

            $startTime = microtime(true);

            // 1. Parse file
            $ext       = pathinfo($upload->file_path, PATHINFO_EXTENSION);
            $numbers   = $parser->parse(storage_path('app/' . $upload->file_path), $ext);

            // Update total count early for progress tracking
            $upload->update(['total_numbers' => count($numbers)]);

            // 2. Process in chunks of 1000
            $seen         = [];
            $clusterIndex = 0;
            $allResults   = [];
            $chunkSize    = 1000;
            $chunks       = array_chunk($numbers, $chunkSize);

            foreach ($chunks as $chunk) {
                foreach ($chunk as $raw) {
                    $result = $processor->processOne($raw, $upload->default_country, $seen, $clusterIndex);
                    $result['upload_id']  = $upload->id;
                    $result['tenant_id']  = $upload->tenant_id;
                    $result['created_at'] = now();
                    $result['updated_at'] = now();
                    $allResults[] = $result;
                }

                // Bulk insert this chunk
                $insertChunk = array_map(fn($r) => [
                    'upload_id'            => $r['upload_id'],
                    'tenant_id'            => $r['tenant_id'],
                    'original'             => $r['original'],
                    'e164'                 => $r['e164'],
                    'local_format'         => $r['local_format'],
                    'whatsapp_format'      => $r['whatsapp_format'],
                    'dashed_format'        => $r['dashed_format'],
                    'is_valid'             => $r['is_valid'],
                    'is_duplicate'         => $r['is_duplicate'],
                    'country_code'         => $r['country_code'],
                    'country_name'         => $r['country_name'],
                    'number_type'          => $r['number_type'],
                    'risk_score'           => $r['risk_score'],
                    'whatsapp_likely'      => $r['whatsapp_likely'],
                    'duplicate_cluster_id' => $r['duplicate_cluster_id'],
                    'created_at'           => now(),
                    'updated_at'           => now(),
                ], $allResults);

                DB::table('phone_results')->insert($insertChunk);
                $allResults = []; // free memory
            }

            // 3. Aggregate stats
            $stats = DB::table('phone_results')
                ->where('upload_id', $upload->id)
                ->selectRaw('
                    COUNT(*) as total,
                    SUM(CASE WHEN is_valid = 1 THEN 1 ELSE 0 END) as valid_count,
                    SUM(CASE WHEN is_valid = 0 THEN 1 ELSE 0 END) as invalid_count,
                    SUM(CASE WHEN is_duplicate = 1 THEN 1 ELSE 0 END) as duplicate_count,
                    SUM(CASE WHEN risk_score >= 60 THEN 1 ELSE 0 END) as risky_count
                ')
                ->first();

            $processingTime = microtime(true) - $startTime;

            $upload->update([
                'status'          => Upload::STATUS_COMPLETED,
                'total_numbers'   => $stats->total,
                'valid_count'     => $stats->valid_count,
                'invalid_count'   => $stats->invalid_count,
                'duplicate_count' => $stats->duplicate_count,
                'risky_count'     => $stats->risky_count,
                'processing_time' => $processingTime,
                'processed_at'    => now(),
            ]);

            // Clean up temp file
            if (file_exists(storage_path('app/' . $upload->file_path))) {
                unlink(storage_path('app/' . $upload->file_path));
            }

        } catch (\Throwable $e) {
            Log::error("ProcessUploadJob failed for upload #{$this->uploadId}", [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ]);

            $upload->update([
                'status'        => Upload::STATUS_FAILED,
                'error_message' => $e->getMessage(),
            ]);

            throw $e;
        }
    }
}
