<?php

namespace Ignite\Core\Models\Grid;

use Ignite\Core\Contracts\Table;
use Illuminate\Support\Facades\Storage;
use Yajra\DataTables\DataTableAbstract;
use Yajra\DataTables\DataTables;
use Yajra\DataTables\Services\DataTable;

abstract class AbstractTable extends DataTable implements Table
{
    /**
     * @var DataTables
     */
    protected $datatables;

    /**
     * The dynamic parameters to apply to the report, if any.
     * @var array
     */
    protected $params;

    /**
     * BlockTable constructor.
     *
     * @param DataTables $datatables
     * @param array $params
     */
    public function __construct(DataTables $datatables, array $params = [])
    {
        $this->datatables = $datatables;
        $this->params = $params;
    }

    /**
     * The columns to show.
     *
     * @return array
     */
    abstract public function columns();

    /**
     * The table query.
     *
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder|\Illuminate\Support\Collection
     */
    abstract public function query();

    /**
     * Attempt to merge any form label information if form information exists.
     *
     * @return array
     */
    protected function getColumns()
    {
        // Get the columns for the report instance.
        return $this->columns();
    }

    /**
     * Render the table as HTML.
     *
     * @return \Yajra\DataTables\Html\Builder
     */
    public function html()
    {
        return parent::html()->ajax([
                'method' => 'POST',
                'headers' => [
                    'X-CSRF-TOKEN' => csrf_token()
                ]
            ])
            ->columns($this->getColumns())
            ->parameters($this->getBuilderParameters());
    }

    /**
     * Get the configuration for the default column visibility button.
     *
     * @return array
     */
    protected function getColumnVisibilityButton()
    {
        $columns = collect($this->getColumns())->keyBy('name')->keys()->toArray();

        return [
            'extend' => 'colvis',
            'columns' => array_map(
                function ($col) {
                    return $col . ':name';
                },
                $columns
            )
        ];
    }

    /**
     * Get default builder parameters.
     *
     * @return array
     */
    protected function getBuilderParameters()
    {
        return [
            'stateSave' => true,
            'responsive' => true,
            'filter' => true,
            'lengthMenu' => $this->params['length'] ?? [25, 50, 100, 250],
            'order'   => [[0, 'desc']],
            'dom' => '<"#dt-header.clearfix"Bf>rtip',
            'ajax' => ['method' => 'POST'],
            'buttons' => [
                'pageLength',
                $this->getColumnVisibilityButton(),
                'postCsv',
                'postExcel',
                'print'
            ],
        ];
    }

    /**
     * Apply query scopes.
     *
     * @param \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder $query
     * @return mixed
     */
    protected function applyScopes($query)
    {
        foreach ($this->scopes as $scope) {
            $scope->apply($query);
        }

        return $query;
    }

    /**
     * Obscure sensitive data fields.
     *
     * @param  DataTableAbstract $table
     * @return DataTableAbstract
     */
    protected function _dataTable(DataTableAbstract $table)
    {
        $columns = $this->getColumns();
        $sensitive = collect($columns)->where('sensitive', true)->keys();
        $allowDecrypt = property_exists($this, 'allowDecrypt') && $this->allowDecrypt === true;

        foreach ($sensitive as $key) {
            $table->editColumn($key, function ($column) use ($key, $allowDecrypt) {
                $value = trim($column->$key);

                if (empty($value)) {
                    return '';
                }

                try {
                    $value = decrypt($value);
                    if ($allowDecrypt) {
                        return $value;
                    }
                    return str_repeat('*', strlen($value));
                } catch (\Illuminate\Contracts\Encryption\DecryptException $e) {
                    return str_repeat('*', 8);
                }
            });
        }

        if (method_exists($this, 'getColumnFormattingMap')) {
            $specials = $this->getColumnFormattingMap();

            foreach ($specials as $name => $content) {
                if (array_key_exists($name, $this->getColumns())) {
                    $table->editColumn($name, $content);
                }
            }
        }

        return $table;
    }

    /**
     * Create the basic configuration for an actions column in Ignite.
     *
     * @return array
     */
    protected function actionsColumn()
    {
        return [
            'name' => 'action',
            'title' => 'Actions',
            'searchable' => false,
            'orderable' => false,
            'exportable' => false,
            'printable' => false,
            'visible' => true,
            'width' => '80px',
            'class' => 'actions'
        ];
    }

    /**
     * Create the basic configuration for a checkbox column in Ignite.
     *
     * @return array
     */
    protected function checkboxColumn()
    {
        return [
            'defaultContent' => '',
            'title'          => '&nbsp;',
            'name'           => 'checkbox',
            'targets'        => 0,
            'sensitive'      => false,
            'orderable'      => false,
            'searchable'     => false,
            'exportable'     => false,
            'printable'      => true,
            'width'          => '10px',
            'order'          => 1,
        ];
    }

    /**
     * Create the basic configuration for a deleted_at column in Ignite.
     *
     * @return array
     */
    protected function deletedAtColumn()
    {
        return [
            'defaultContent' => '',
            'title'          => 'Deleted At',
            'name'           => 'claim_participant.deleted_at',
            'targets'        => 0,
            'sensitive'      => false,
            'orderable'      => false,
            'searchable'     => true,
            'exportable'     => true,
            'printable'      => true,
            'visible'        => true,
        ];
    }

    /**
     * Replace a column configuration.
     *
     * @param  array  $columns
     * @param  array  $replace
     * @param  string $column
     * @return array
     */
    protected function replaceColumn(array $columns, array $replace, $column)
    {
        $offset = array_search($column, array_keys($columns));

        unset($columns[$column]);

        return array_slice($columns, 0, $offset, true) +
               $replace +
               array_slice($columns, $offset, null, true);
    }

    /**
     * Export results to Excel and store in a filesystem.
     *
     * @param  string $filepath
     * @param  string $disk
     * @param  string $extension
     * @return bool|\Illuminate\Foundation\Bus\PendingDispatch
     */
    public function storeAsExcel($filepath = '', $disk = 'local', $extension = '')
    {
        $extension = empty($extension) ? strtolower($this->excelWriter) : $extension;
        if (empty($filepath)) {
            $filepath = $this->getFilename() . '.' . ltrim($extension, '.');
        }

        return $this->buildExcelFile()->store($filepath, $disk, $this->excelWriter);
    }
}
