<?php

namespace App\Services\BulkExport;

use App\Services\CsvMapping;
use Bus;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Storage;

abstract class BaseBulkExport
{
    public $model;
    public $fileName;
    public $directory;
    public $disk;
    protected $query;
    protected $databaseColumns;
    protected $csvMapping;

    public function __construct($directory, $fileName, $model, string $disk = 'local', array $excludes = [])
    {
        $this->directory = $directory;
        $this->file_name = $fileName;
        $this->model = resolve($model);
        $this->disk = $disk;
        // Database Columns List For Storing
        $this->csvMapping = new CsvMapping();
        $this->databaseColumns = $this->csvMapping->getColumnListings($this->model::class, $excludes);
    }

    /**
     * @param int $chunkSize
     * @param bool $queue
     * @param bool $hasCancel
     * @param null $batchId
     * @return mixed
     */
    abstract function processExport(int $chunkSize = 1000, bool $queue = true, bool $hasCancel = true, $batchId = null);

    /**
     * @param $query
     * @param $mapColumnTypes
     * @return mixed
     */
    abstract protected function validate($query, $mapColumnTypes);

    /**
     * @return mixed
     */
    protected function selectColumnFromModel(): mixed
    {
        $model = $this->model::query();
        foreach ($this->databaseColumns as $column) {
            $model = $model->addSelect($column);
        }

        return $model;
    }

    /**
     * @return array
     */
    protected function getColumnNameAndType(): array
    {
        $mapColumnTypes = [];
        $getTable = (new $this->model())->getTable();
        foreach ($this->databaseColumns as $databaseColumn) {
            $columnType = Schema::getColumnType($getTable, $databaseColumn);
            $mapColumnTypes[] = [
                'column_name' => $databaseColumn,
                'column_type' => $columnType
            ];
        }
        return $mapColumnTypes;
    }

    /**
     * @param string $fMode
     * @return false|resource
     */
    protected function fileOpen(string $fMode = 'w')
    {
        $path = Storage::disk($this->disk)->path($this->directory);
        if (!file_exists($path)) {
            mkdir($path, 0777, true);
        }

        $filePath = Storage::disk($this->disk)->path($this->directory . '/' . $this->file_name . '.csv');
        return fopen($filePath, $fMode);
    }

    /**
     * @param $file
     */
    protected function putHeaderInCsv($file)
    {
        fputcsv($file, $this->databaseColumns);
    }

    /**
     * @param $file
     * @param $validatedData
     */
    protected function putDataInCsv($file, $validatedData)
    {
        fputcsv($file, $validatedData);
    }

    /**
     * @param $file
     */
    protected function fileClose($file)
    {
        fclose($file);
    }

    /**
     * @param $batchId
     * @return bool
     */
    protected function cancelJob($batchId): bool
    {
        $batch = Bus::findBatch($batchId);
        return $batch->cancelledAt !== null;
    }
}
