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

namespace BplanBase\Globals\DataGrid;


use BplanBase\Globals\Enums\Number;
use BplanBase\Globals\Enums\CaseStyle;
use BplanBase\Globals\Helpers\StringHelper;
use Closure;
use Illuminate\Contracts\Support\Arrayable;
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     7.1.0 / 2025-04-22
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 */
class DataGrid
{


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


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


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


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


    /**
     * Gibt an, ob aktive Suchfilter vorhanden sind
     *
     * @var     bool
     */
    protected bool $_activeFilters = false;


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


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


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


    /**
     * Assoziatives Array mit CSS-Klassen pro Spalte
     *
     * @var     array<string, string>
     */
    protected array $_columnClasses = [];


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


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


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


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


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


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


    /**
     * Eine Funktion die den CSS-Klassen-String für eine Zeile ermittelt
     *
     * @var     callable|null
     */
    protected $_rowClassCallback = null;


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


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


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


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


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


    /**
     * @var     string
     */
    protected string $_type = '';


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


    /**
     * @var     bool
     */
    protected bool $_useDefaultCreateButton = false;


    /**
     * @var     bool
     */
    protected bool $_useDefaultSearch = false;


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


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


    /**
     *
     * @param       string $type
     *              Erwartet den Typ im Case-Style "Slug".
     *
     * @version     1.0.0 / 2026-04-04
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function __construct(string $type)
    {
        $this->_type = $type;

    } // __construct()


    /**
     * 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 den SVG-Code eines Action-Icons
     *
     * @param       string $action
     *
     * @return      string
     *
     * @version     1.0.0 / 2026-04-03
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function getActionSvgIcon(string $action): string
    {
        return $this->_actionIcons[$action] ?? $action;

    } // getActionSvgIcon()


    /**
     * 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 CSS-Klassen für eine Spalte
     *
     * @param       string $column
     *
     * @return      string
     *
     * @version     1.0.0 / 2026-04-04
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function getColumnClass(string $column): string
    {
        return $this->_columnClasses[$column] ?? '';

    } // getColumnClass()


    /**
     * 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     2.0.0 / 2025-04-06
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function getDisplayValue(array|stdClass|Model $item, string $column): mixed
    {
        $value = is_array($item) ? ($item[$column] ?? '-') : ($item->{$column} ?? '-');

        if ($value instanceof stdClass) {
            $value = json_encode($value);
        }
        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()


    /**
     * Liefert den CSS-Klassen-String für eine Zeile
     *
     * @param       array|Model $item
     *
     * @return      string
     *
     * @version     2.0.0 / 2025-04-06
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function getRowClass(array|stdClass|Model $item): string
    {
        if ($this->_rowClassCallback instanceof Closure) {
            return ($this->_rowClassCallback)($item);
        }
        if (is_callable($this->_rowClassCallback)) {
            return call_user_func($this->_rowClassCallback, $item);
        }
        return '';

    } // getRowClass()


    /**
     *
     * @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()


    /**
     *
     * @return      string|null
     *
     * @version     1.0.0 / 2025-04-06
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function getToggleCallback(): string|null
    {
        return $this->_toggleCallback;

    } // getToggleCallback()


    /**
     *
     * @return      string|null
     *
     * @version     1.0.0 / 2025-04-06
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function getToggleField(): string|null
    {
        return $this->_toggleField;

    } // getToggleField()


    /**
     *
     * @return      string
     *
     * @version     2.0.0 / 2026-04-05
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function getType(CaseStyle|null $CaseStyle = null, Number|null $Number = null): string
    {
        return StringHelper::reformat($this->_type, $CaseStyle, $Number);

    } // getType()


    /**
     * 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()


    /**
     * Gibt an, ob aktive Suchfilter vorhanden sind
     *
     * @return      bool
     *
     * @version     1.0.0 / 2026-04-05
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function hasActiveFilters(): bool
    {
        return $this->_activeFilters;

    } // hasActiveFilters()


    /**
     * Prüft das Vorhandensein von Daten
     *
     * @return      bool
     *
     * @version     1.0.1 / 2026-04-05
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function hasData(): bool
    {
        if ($this->_data instanceof LengthAwarePaginator) {
            return !$this->_data->isEmpty();
        }
        return !empty($this->_data);

    } // hasData()


    /**
     *
     * @return 	    bool
     *
     * @version     1.0.0 / 2025-04-06
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function hasToggleCallBack(): bool
    {
        return !empty($this->_toggleCallback);

    } // hasToggleCallBack()


    /**
     * 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|null
     *
     * @version     1.3.0 / 2025-04-22
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function lc(string $key): string|null
    {
        $localeString = __($this->_localeFile.'.grid-locales.'.$key);
        /*
        **  Durch die Zuweisung von NULL kann dafür gesorgt werden, dass 
        **  bei Locales die nicht ausgegeben werden sollen, keine Ausgabe 
        **  erfolgt. */
        if ($localeString === null) {
            return null;
        }
        return $localeString;

    } // lc()


    /**
     * Definiert Actions, die als Buttons eingebaut werden
     *
     * @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<string, string> $icons
     *
     * @return      $this
     *
     * @version     1.0.0 / 2026-04-03
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function setActionSvgIcons(array $icons): self
    {
        $this->_actionIcons = $icons;

        return $this;

    } // setActionSvgIcons()


    /**
     * Setzt das Flag für aktive Suchfilter
     *
     * @param       bool $hasActiveFilters
     *
     * @return      $this
     *
     * @version     1.0.0 / 2026-04-05
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function setActiveFilters(bool $hasActiveFilters): self
    {
        $this->_activeFilters = $hasActiveFilters;

        return $this;

    } // setActiveFilters()


    /**
     * 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()


    /**
     * Setzt CSS-Klassen für einzelne Spalten
     *
     * @param       array<string, string> $classes
     *              Assoziatives Array: Spaltenname => CSS-Klassen-String
     *
     * @return      $this
     *
     * @version     1.0.0 / 2026-04-04
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function setColumnClasses(array $classes): self
    {
        $this->_columnClasses = $classes;

        return $this;

    } // setColumnClasses()


    /**
     * 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.1.0 / 2025-04-06
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function setLocaleFile(string $fileName): self
    {
        $this->_localeFile = $fileName;

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

        return $this;

    } // setLocaleFile()


    /**
     * Setzt einen Callback zur Ermittlung der CSS-Klassen einer Zeile
     *
     * @param       callable $callback
     *              Erhält $item (Model|array), gibt einen CSS-Klassen-String zurück
     *
     * @return      $this
     *
     * @version     1.0.0 / 2026-04-04
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function setRowClassCallback(callable $callback): self
    {
        $this->_rowClassCallback = $callback;

        return $this;

    } // setRowClassCallback()


    /**
     *
     * @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()


    /**
     *
     * @param       string $callback
     *
     * @return      $this
     *
     * @version     1.0.0 / 2025-04-06
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function setToggleCallback(string $callback, string $toggleField): self
    {
        $this->_toggleCallback = $callback;
        $this->_toggleField    = $toggleField;

        return $this;

    } // setToggleCallback()


    /**
     * 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()


    /**
     * Steuert die Anzeige des Standard-Suchfilters
     *
     * @param       boolean|null $useDefaultSearch
     *
     * @return      boolean|self
     *
     * @version     1.0.0 / 2025-04-06
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function useDefaultCreateButton(bool|null $useDefaultCreateButton = null): bool|self
    {
        if ($useDefaultCreateButton === null) {
            return $this->_useDefaultCreateButton;
        }
        $this->_useDefaultCreateButton = $useDefaultCreateButton;

        return $this;

    } // useDefaultCreateButton()


    /**
     * Steuert die Anzeige des Standard-Suchfilters
     *
     * @param       boolean|null $useDefaultSearch
     *
     * @return      boolean|self
     *
     * @version     1.0.0 / 2025-04-05
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function useDefaultSearch(bool|null $useDefaultSearch = null): bool|self
    {
        if ($useDefaultSearch === null) {
            return $this->_useDefaultSearch;
        }
        $this->_useDefaultSearch = $useDefaultSearch;

        return $this;

    } // useDefaultSearch()


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


} // class DataGrid {}
