<?php

namespace App\Jobs;

use App\Models\Department;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\QueryException;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

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

    /** @var Department */
    private $department;

    /** @var int */
    private $semester;

    /** @var int */
    private $batch;

    /** @var int  */
    private $codeLength = 10;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct(Department $department, int $semester, int $batch)
    {
        $this->department = $department;
        $this->semester = $semester;
        $this->batch = $batch;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        $this->insertCodes($this->prepareCodes());
    }

    /**
     * @param array $codes
     */
    protected function insertCodes(array $codes)
    {
        collect($codes)->chunk(100)->each(function (Collection $codes) {
            $codes = $codes->toArray();

            do {
                $inserted = false;

                try {
                    DB::table('codes')->insert($codes);

                    $inserted = true;
                } catch (QueryException $e) {
                    dump('duplikat');
                    $duplicateCode = $this->parseDuplicateCode($e);
                    $this->replaceDuplicateCode($duplicateCode, $codes);
                }
            } while (!$inserted);
        });
    }

    /**
     * @return array
     */
    protected function prepareCodes()
    {
        $codes = [];

        for ($i = 0; $i < $this->batch; $i++) {
            $code = $this->generateCode();
            $codes[$code['code']] = $code;
        }

        return $codes;
    }

    protected function parseDuplicateCode(QueryException $e)
    {
        return collect($e->getBindings())->filter(function ($binding) {
            return is_string($binding);
        })->first();
    }

    protected function replaceDuplicateCode(string $code, array &$codes)
    {
        unset($codes[$code]);

        $newCode = $this->generateCode();
        $codes[$newCode['code']] = $newCode;
    }

    protected function generateCode()
    {
        return [
            'code'          => Str::random($this->codeLength),
            'semester'      => $this->semester,
            'department_id' => $this->department->id,
        ];
    }
}
