<?php
/**
 * DataGrid Class
 *
 * @version     1.0.$Revision:$
 * @version     SVN: $Id:$
 * @package     bplan-base/globals
 * @subpackage  DataGrid
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 * @copyright   Copyright (C) 2025 bplan-solutions GmbH & Co. KG <https://www.bplan-solutions.de/>
 * /Δ\
 */

namespace BplanBase\Globals\DataGrid;


use Arrayable;
use Closure;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use stdClass;


/**
 * DataGrid Class
 *
 * @version     2.1.0 / 2025-09-30
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 */
class DataGrid
{


/* +++ TRAITS +++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */


/* +++ CLASS CONSTANTS +++ ++++++++++++++++++++++++++++++++++++++++++++++++++ */


/* +++ OBJECT MEMBERS +++ +++++++++++++++++++++++++++++++++++++++++++++++++++ */


    /**
     * @var     array $_actions
     */
    protected array $_actions = [];


    /**
     * Assoziatives Array für Spaltenüberschriften
     *
     * @var     array $_captions
     */
    protected array $_captions = [];


    /**
     * Eine Funktion die auf jedes Element angewendet wird
     *
     * @var     callable $_columnCallback
     */
    protected $_columnCallback;

    /**
     * Liste der anzuzeigenden Spalten
     *
     * @var     array|null $_columns
     */
    protected ?array $_columns = null;


    /**
     * @var     iterable $_data
     */
    protected iterable $_data;


    /**
     * @var     string $_identifyingColumn
     */
    protected string $_identifyingColumn = 'id';


    /**
     * @var     string $_localeFile
     */
    protected string $_localeFile = 'locale-file-missing';


    /**
     * @var     array $_locales
     */
    protected array $_locales = [];


    /**
     * @var     string $_name
     */
    protected string $_name = 'mainGrid';


    /**
     * @var     null|string $_sortColumn
     */
    protected null|string $_sortColumn;


    /**
     * @var     array $_sortColumns
     */
    protected array $_sortColumns;


    /**
     * @var     null|string $_sortDirection
     */
    protected null|string $_sortDirection;


    /**
     * @var     bool $_useCaptions
     */
    protected bool $_useCaptions = true;


/* +++ CLASS MEMBERS +++ ++++++++++++++++++++++++++++++++++++++++++++++++++++ */


/* +++ OBJECT METHODS +++ +++++++++++++++++++++++++++++++++++++++++++++++++++ */


    /**
     * Liefert den Array mit den Daten
     *
     * @return      array
     *
     * @version     1.0.0 / 2025-09-07
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function getActions(): array
    {
        return $this->_actions;

    } // getActions()


    /**
     * Liefert die finalen Captions für den Table-Header
     *
     * @return      array
     *
     * @version     1.0.0 / 2025-09-07
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function getCaptions(): array
    {
        return collect($this->getVisibleColumns())
            ->map(fn($key) => $this->_captions[$key] ?? $key)
            ->toArray();

    } // getCaptions()


    /**
     * Liefert die Daten
     *
     * @return      iterable
     *
     * @version     1.0.0 / 2025-09-07
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function getData(): iterable
    {
        return $this->_data;

    } // getData()


    /**
     * Liefert den Anzeigewert für eine Spalte
     *
     * @param       array|Model $item
     *
     * @param       string $column
     *
     * @param       mixed
     *
     * @version     1.0.0 / 2025-09-07
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function getDisplayValue(array|Model $item, string $column): mixed
    {
        $value = is_array($item) ? ($item[$column] ?? '-') : ($item->{$column} ?? '-');

        if ($this->_columnCallback instanceof Closure) {
            return ($this->_columnCallback)($item, $column, $value);
        }
        if (is_callable($this->_columnCallback)) {
            return call_user_func($this->_columnCallback, $item, $column, $value);
        }
        return $value;

    } // getDisplayValue()


    /**
     * Liefert den Namen der ID-Spalte
     *
     * @return      string
     *
     * @version     1.0.0 / 2025-09-07
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function getidentifyingColumn(): string
    {
        return $this->_identifyingColumn;

    } // getidentifyingColumn()


    /**
     * Liefert den Namen des Grids
     *
     * @return      string
     *
     * @version     1.0.0 / 2025-09-07
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function getName(): string
    {
        return $this->_name;

    } // getName()


    /**
     *
     * @return      array
     *
     * @version     1.0.0 / 2025-09-21
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function getSorting(): array
    {
        return [
            'column' => $this->_sortColumn,
            'columns' => $this->_sortColumns,
            'direction' => $this->_sortDirection,
        ];
    } // getSorting()


    /**
     * Gibt die Liste der zu verwendenden Spalten zurück
     *
     * Falls keine gesetzt, werden automatisch die Keys der ersten Zeile verwendet.
     *
     * @todo        Das ermitteln von $firstRow mit resolveData() muss in Ordnung
     *              gebracht werden. Ansonsten ist eine Verwendung ohne explizite
     *              Angabe der zu verwendenden Spalten nicht möglich.
     *
     * @return      array
     *
     * @version     1.0.0 / 2025-09-07
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function getVisibleColumns(): array
    {
        if (!empty($this->_columns)) {
            return $this->_columns;
        }
        $firstRow = $this->resolveData()->first();

        if (empty($firstRow)) {
            return [];
        }
        // 1) Eloquent Model -> nur echte Attribute (keine internen/protected keys)
        if ($firstRow instanceof Model) {
            return array_keys($firstRow->getAttributes());
        }

        // 2) Arrayable (z.B. DTOs, einige Collection-Items) -> toArray()
        if ($firstRow instanceof Arrayable) {
            $arr = $firstRow->toArray();

            return is_array($arr) ? array_keys($arr) : [];
        }

        // 3) Reines Array
        if (is_array($firstRow)) {
            return array_keys($firstRow);
        }

        // 4) Sonstiges Objekt (stdClass etc.) -> cast + säubern (protected keys enthalten Null-Bytes)
        if (is_object($firstRow)) {
            $arr = (array) $firstRow;
            $keys = array_keys($arr);
            $clean = [];

            foreach ($keys as $key) {
                if (strpos($key, "\0") !== false) {
                    // Objekt-Cast erzeugt Keys wie "\0*\0property" oder "\0ClassName\0prop"
                    $parts = explode("\0", $key);
                    $key = end($parts);
                }
                $clean[] = $key;
            }
            return $clean;
        }
        // Fallback
        return [];

    } // getVisibleColumns()


    /**
     * Prüft das Vorhandensein von Daten
     *
     * @return      bool
     *
     * @version     1.0.0 / 2025-09-07
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function hasData(): bool
    {
        return !empty($this->_data);

    } // hasData()


    /**
     * Gibt den Locale-String zum übergebenen Schlüssel
     *
     * Wenn kein passender String existiert, dann wird der Schlüssel zurückgegeben.
     *
     * @param       string $key
     *
     * @return      string
     *
     * @version     1.0.0 / 2025-09-07
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function lc(string $key): string
    {
        if (isset($this->_locales[$key])) {
            return $this->_locales[$key];
        }
        return $key;

    } // lc()


    /**
     * Definiert die Überschriften der Spalten
     *
     * @param       array $captions
     *
     * @return      $this
     *
     * @version     1.0.0 / 2025-09-07
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function setActions(array $actions): self
    {
        $this->_actions = $actions;

        return $this;

    } // setActions()


    /**
     * Definiert die Überschriften der Spalten
     *
     * @param       array $captions
     *
     * @return      $this
     *
     * @version     1.0.0 / 2025-09-07
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function setCaptions(array $captions): self
    {
        $this->_captions = $captions;

        return $this;

    } // setCaptions()


    /**
     * Definiert explizit, welche Spalten angezeigt werden sollen
     * und in welcher Reihenfolge.
     *
     * @param       array $columns
     *
     * @return      $this
     *
     * @version     1.0.0 / 2025-09-07
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function setColumns(array $columns): self
    {
        $this->_columns = $columns;

        return $this;

    } // setColumns()


    /**
     * Definiert explizit, welche Spalten angezeigt werden sollen
     * und in welcher Reihenfolge.
     *
     * @param       callable $callback
     *
     * @return      $this
     *
     * @version     1.0.0 / 2025-09-07
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function setColumnCallback(callable $callback): self
    {
        $this->_columnCallback = $callback;

        return $this;

    } // setColumnCallback()


    /**
     * Setzt die Datenquelle des Grids
     *
     * Akzeptiert Arrays oder Collections.
     *
     * @param       iterable $data
     *
     * @return      $this
     *
     * @version     1.0.0 / 2025-09-07
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function setData(iterable $data): self
    {
        $this->_data = $data;

        return $this;

    } // setData()


    /**
     * Setzt den Namen der Id-Spalte
     *
     * @param       string $columnName
     *
     * @return      $this
     *
     * @version     1.0.0 / 2025-09-07
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function setIdentifyingColumn(string $columnName): self
    {
        $this->_identifyingColumn = $columnName;

        return $this;

    } // setIdentifyingColumn()


    /**
     * Setzt den Namen der Locale-Datei
     *
     * @param       string $fileName
     *
     * @return      $this
     *
     * @version     1.0.0 / 2025-09-07
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function setLocaleFile(string $fileName): self
    {
        $this->_localeFile = $fileName;

        $this->_captions = __($this->_localeFile.'.grid-locales.captions');
        $this->_locales = __($this->_localeFile.'.grid-locales');

        return $this;

    } // setLocaleFile()


    /**
     *
     * @param       array $columns
     *
     * @param       null|string $column
     *
     * @param       null|string $direction
     *
     * @version     1.1.0 / 2025-09-30
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function setSorting(array $columns, null|string $column = null, null|string $direction = null): self
    {
        $this->_sortColumn = $column;
        $this->_sortColumns = $columns;
        $this->_sortDirection = $direction ?? 'asc';

        return $this;

    } // setSorting()


    /**
     * Steuert die Anzeige von Spaltenüberschriften
     *
     * Wenn kein Status übergeben wird, dann liefert die Methode den aktuellen Status.
     *
     * @param       bool|null $useCaptions
     *
     * @return      bool|self
     *
     * @version     1.0.0 / 2025-09-07
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function useCaptions(bool|null $useCaptions = null): bool|self
    {
        if ($useCaptions === null) {
            return $this->_useCaptions;
        }
        $this->_useCaptions = $useCaptions;

        return $this;

    } // useCaptions()


/* +++ CLASS METHODS +++ ++++++++++++++++++++++++++++++++++++++++++++++++++++ */


} // class DataGrid {}
