<?php

namespace App\Jobs;

use App\Models\AdminBatchJob;
use App\Models\User;
use App\Services\WalletService;
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\Storage;

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

    public function __construct(private readonly int $batchId)
    {
    }

    public function handle(WalletService $walletService): void
    {
        $batch = AdminBatchJob::query()->find($this->batchId);
        if (! $batch) {
            return;
        }

        $batch->status = 'processing';
        $batch->started_at = now();
        $batch->save();

        $path = Storage::disk('local')->path($batch->file_path);
        if (! file_exists($path)) {
            $batch->status = 'failed';
            $batch->finished_at = now();
            $batch->save();
            return;
        }

        $handle = fopen($path, 'r');
        if (! $handle) {
            $batch->status = 'failed';
            $batch->finished_at = now();
            $batch->save();
            return;
        }

        $headers = fgetcsv($handle);
        if (! $headers) {
            fclose($handle);
            $batch->status = 'failed';
            $batch->finished_at = now();
            $batch->save();
            return;
        }

        $headers = array_map(fn ($h) => strtolower(trim((string) $h)), $headers);
        $rowNumber = 1;
        $errors = [];
        $processed = 0;
        $failed = 0;

        while (($row = fgetcsv($handle)) !== false) {
            $rowNumber++;
            $data = array_combine($headers, $row) ?: [];
            $userId = (int) ($data['user_id'] ?? 0);
            $memberCode = trim((string) ($data['member_code'] ?? ''));
            $walletType = strtolower(trim((string) ($data['wallet_type'] ?? '')));
            $direction = strtolower(trim((string) ($data['direction'] ?? 'credit')));
            $amount = (float) ($data['amount'] ?? 0);
            $narration = trim((string) ($data['narration'] ?? 'Bulk adjustment'));
            $sourceType = trim((string) ($data['source_type'] ?? 'bulk_adjust'));

            if ($userId <= 0 && $memberCode !== '') {
                $userId = (int) User::query()->where('member_code', $memberCode)->value('id');
            }

            if ($userId <= 0 || $amount <= 0) {
                $errors[] = ['row' => $rowNumber, 'error' => 'Invalid user or amount'];
                $failed++;
                continue;
            }

            try {
                if ($direction === 'debit') {
                    $walletService->debit($userId, $walletType, $amount, $sourceType, null, [
                        'narration' => $narration,
                        'created_by' => $batch->created_by,
                    ]);
                } else {
                    $walletService->credit($userId, $walletType, $amount, $sourceType, null, [
                        'narration' => $narration,
                        'created_by' => $batch->created_by,
                    ]);
                }
                $processed++;
            } catch (\Throwable $exception) {
                $errors[] = ['row' => $rowNumber, 'error' => $exception->getMessage()];
                $failed++;
            }
        }

        fclose($handle);

        $errorPath = null;
        if ($errors !== []) {
            $errorPath = 'imports/wallet/errors-'.$batch->id.'.json';
            Storage::disk('local')->put($errorPath, json_encode($errors, JSON_PRETTY_PRINT));
        }

        $batch->status = $failed > 0 ? 'completed_with_errors' : 'completed';
        $batch->total_rows = $processed + $failed;
        $batch->processed_rows = $processed;
        $batch->failed_rows = $failed;
        $batch->error_report_path = $errorPath;
        $batch->finished_at = now();
        $batch->save();
    }
}
