<?php

namespace App\Jobs;

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\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use App\Services\RecordFileService;
use App\Http\Middleware\TenantMiddleware;

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

    protected $recordId;
    protected $tempFilePath;
    protected $originalFileName;
    protected $type;
    protected $clientName;

    public $timeout = 300;
    public $tries = 3;
    public $backoff = [10, 30, 60];

    public function boot()
    {
        app(TenantMiddleware::class)->setupTenantConnection();

    }
    public function __construct($recordId, $tempFilePath, $originalFileName, $type = 'OUT', $clientName = null)
    {
        $this->recordId = $recordId;
        $this->tempFilePath = $tempFilePath;
        $this->originalFileName = $originalFileName;
        $this->type = strtolower($type);
        $this->clientName = $clientName ?: session('clientName', 'default');

        $this->clientName = Str::slug($this->clientName, '_');
    }

    public function handle(RecordFileService $recordFileService)
    {
        try {
            Log::info("=== PROCESSING ATTACHMENT JOB START ===");
            Log::info("Client: {$this->clientName}");
            Log::info("Record ID: {$this->recordId}");
            Log::info("Temp file: {$this->tempFilePath}");
            Log::info("Original name: {$this->originalFileName}");
            Log::info("Type: {$this->type}");

            DB::table('records')
                ->where('id', $this->recordId)
                ->update([
                    'attachment_status' => 'processing',
                    'updated_at' => now()
                ]);

            if (!Storage::disk('s3')->exists($this->tempFilePath)) {
                Log::error("Temp file not found on S3: {$this->tempFilePath}");

                try {
                    $tempFiles = Storage::disk('s3')->files("{$this->clientName}/temp/uploads");
                    Log::info("Available temp files for client '{$this->clientName}' on S3: " . json_encode($tempFiles));
                } catch (\Exception $e) {
                    Log::error("Could not list temp files for client '{$this->clientName}': " . $e->getMessage());
                }

                throw new \Exception("Temp file not found on S3: {$this->tempFilePath}");
            }

            $tempFileSize = Storage::disk('s3')->size($this->tempFilePath);
            Log::info("Temp file size: {$tempFileSize} bytes");

            $extension = pathinfo($this->originalFileName, PATHINFO_EXTENSION);
            $fileName = time() . '_' . Str::random(10) . '.' . $extension;
            $finalPath = "{$this->clientName}/records/{$this->type}/{$this->recordId}/attachments/{$fileName}";

            Log::info("Final path: {$finalPath}");

            $copySuccess = $this->copyFileOnS3($this->tempFilePath, $finalPath);

            if (!$copySuccess) {
                throw new \Exception("Failed to copy file from {$this->tempFilePath} to {$finalPath}");
            }

            if (!Storage::disk('s3')->exists($finalPath)) {
                throw new \Exception("Final file not found after copy: {$finalPath}");
            }

            $finalFileSize = Storage::disk('s3')->size($finalPath);
            Log::info("Final file size: {$finalFileSize} bytes");

            if ($finalFileSize !== $tempFileSize) {
                Log::warning("File size mismatch! Temp: {$tempFileSize}, Final: {$finalFileSize}");
            } else {
                Log::info("File sizes match - copy successful");
            }

            DB::table('records')
                ->where('id', $this->recordId)
                ->update([
                    'attachment' => $finalPath,
                    'attachment_status' => 'completed',
                    'updated_at' => now()
                ]);

            $this->cleanupTempFile($this->tempFilePath);

            Log::info("Attachment processing completed successfully for record {$this->recordId}: {$finalPath}");
            Log::info("=== PROCESSING ATTACHMENT JOB END ===");

        } catch (\Exception $e) {
            Log::error("Failed to process attachment for record {$this->recordId}: " . $e->getMessage());
            Log::error("Stack trace: " . $e->getTraceAsString());

            DB::table('records')
                ->where('id', $this->recordId)
                ->update([
                    'attachment_status' => 'failed',
                    'updated_at' => now()
                ]);

            $this->cleanupTempFile($this->tempFilePath);

            throw $e;
        }
    }

    /**
     * Enhanced S3 copy with multiple fallback approaches
     */
    private function copyFileOnS3($sourcePath, $destinationPath)
    {
        Log::info("Attempting S3 copy from {$sourcePath} to {$destinationPath}");

        try {
            Log::info("Trying Method 1: Standard S3 copy");
            $copyResult = Storage::disk('s3')->copy($sourcePath, $destinationPath);

            if ($copyResult && Storage::disk('s3')->exists($destinationPath)) {
                Log::info("Method 1 successful: Standard S3 copy");
                return true;
            } else {
                Log::warning("Method 1 failed: Standard S3 copy returned " . ($copyResult ? 'true' : 'false'));
            }
        } catch (\Exception $e) {
            Log::warning("Method 1 exception: " . $e->getMessage());
        }

        try {
            Log::info("Trying Method 2: Read and write");

            $fileContent = Storage::disk('s3')->get($sourcePath);
            if (!$fileContent) {
                throw new \Exception("Could not read source file content");
            }

            $writeResult = Storage::disk('s3')->put($destinationPath, $fileContent);

            if ($writeResult && Storage::disk('s3')->exists($destinationPath)) {
                Log::info("Method 2 successful: Read and write");
                return true;
            } else {
                Log::warning("Method 2 failed: Write returned " . ($writeResult ? 'true' : 'false'));
            }
        } catch (\Exception $e) {
            Log::warning("Method 2 exception: " . $e->getMessage());
        }

        try {
            Log::info("Trying Method 3: Stream copy");

            $sourceStream = Storage::disk('s3')->readStream($sourcePath);
            if (!$sourceStream) {
                throw new \Exception("Could not open source stream");
            }

            $writeResult = Storage::disk('s3')->writeStream($destinationPath, $sourceStream);

            if (is_resource($sourceStream)) {
                fclose($sourceStream);
            }

            if ($writeResult && Storage::disk('s3')->exists($destinationPath)) {
                Log::info(" Method 3 successful: Stream copy");
                return true;
            } else {
                Log::warning("Method 3 failed: Stream write returned " . ($writeResult ? 'true' : 'false'));
            }
        } catch (\Exception $e) {
            Log::warning("Method 3 exception: " . $e->getMessage());
        }

        Log::error("All S3 copy methods failed");
        return false;
    }

    /**
     * Clean up temp file with error handling
     */
    private function cleanupTempFile($tempPath)
    {
        try {
            if (Storage::disk('s3')->exists($tempPath)) {
                $deleted = Storage::disk('s3')->delete($tempPath);
                if ($deleted) {
                    Log::info("Temp file deleted: {$tempPath}");
                } else {
                    Log::warning("Failed to delete temp file: {$tempPath}");
                }
            } else {
                Log::info("Temp file already gone: {$tempPath}");
            }
        } catch (\Exception $e) {
            Log::error("Error deleting temp file {$tempPath}: " . $e->getMessage());
        }
    }

    public function failed(\Exception $exception)
    {
        Log::error("=== JOB PERMANENTLY FAILED ===");
        Log::error("Client: {$this->clientName}");
        Log::error("Record ID: {$this->recordId}");
        Log::error("Exception: " . $exception->getMessage());

        DB::table('records')
            ->where('id', $this->recordId)
            ->update([
                'attachment_status' => 'failed',
                'updated_at' => now()
            ]);

        $this->cleanupTempFile($this->tempFilePath);
    }

    /**
     * Get job tags for monitoring
     */
    public function tags()
    {
        return [
            'attachment',
            'client:' . $this->clientName,
            'record:' . $this->recordId,
            'type:' . $this->type,
            'file:' . basename($this->tempFilePath)
        ];
    }
}
