<?php
/**
 * Model Trait
 *
 * @version     1.0.$Revision:$
 * @version     SVN: $Id:$
 * @package     bplan-base/globals
 * @subpackage  Traits
 * @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\Traits;


use Illuminate\Database\Eloquent\Builder;


/**
 * Model Trait
 *
 * @version     1.0.0 / 2026-03-06
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 */
trait ModelHasMaterializedPath
{


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


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


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


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


    /**
     * @var         string
     */
    protected static string $_pathColumn = 'path';


    /**
     * @var         string
     */
    protected static string $_pathSeparator = '.';


    /**
     * @var         integer
     */
    protected static int $_segmentLength = 2;


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


    /**
     *
     * @return      void
     *
     * @version     1.0.0 / 2026-03-06
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function children()
    {
        return static::directChildren($this->{static::$_pathColumn});

    } // children()


    /**
     * Branch Scope
     *
     * Get the entire subtree
     * Usage:
     *      Tenant::branch('00.01')->orderBy('path')->get();
     *
     * @param       Builder $Query
     *
     * @param       string $path
     *
     * @return      Builder
     *
     * @version     1.0.0 / 2026-03-06
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function scopeBranch(Builder $Query, string $path): Builder
    {
        $column = static::$_pathColumn;

        return $Query->where($column, 'like', $path.'%');

    } // scopeBranch()


    /**
     * Direct Child Scope
     *
     * Get the direct child elements.
     * Usage:
     *      Tenant::directChildren('00.01')->get();
     *
     * @param       Builder $Query
     *
     * @param       string $path
     *
     * @return      Builder
     *
     * @version     1.0.0 / 2026-03-06
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function scopeDirectChildren(Builder $Query, string $path): Builder
    {
        $column = static::$_pathColumn;
        $separator = static::$_pathSeparator;
        $segmentLength = static::$_segmentLength;

        return $Query->where($column, 'like', $path.$separator.'%')
            ->whereRaw(
                'LENGTH('.$column.') = ?',
                [strlen($path) + strlen($separator) + $segmentLength]
            );
    } // scopeDirectChildren()


    /**
     *
     * @return
     *
     * @version     1.0.0 / 2026-03-06
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function parent()
    {
        $column = static::$_pathColumn;
        $separator = static::$_pathSeparator;

        $parentPath = preg_replace(
            '/'.$separator.'[^'.$separator.']+$/',
            '',
            $this->$column
        );
        return static::where($column, $parentPath);

    } // parent()


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


    /**
     *
     * @return      void
     *
     * @version     1.0.0 / 2026-03-06
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    protected static function bootModelHasMaterializedPath()
    {
        static::creating(function ($Model) {

            $column = static::$_pathColumn;
            $separator = static::$_pathSeparator;

            if (!empty($Model->$column)) {
                return;
            }
            if (empty($Model->parent_path)) {
                return;
            }
            $Model->$column = static::nextPath($Model->parent_path);
        });
    } // bootModelHasMaterializedPath()


    /**
     *
     * @param       string $parentPath
     *
     * @param       integer $offset
     *
     * @return      string
     *
     * @version     1.0.0 / 2026-03-06
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public static function nextPath(string $parentPath, int $offset = 0): string
    {
        return $parentPath
            .static::$_pathSeparator
            .static::nextPathSegment($parentPath, $offset);

    } // nextPath()


    /**
     *
     * @param       string $parentPath
     *
     * @param       integer $offset
     *
     * @return      string
     *
     * @version     1.0.0 / 2026-03-06
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public static function nextPathSegment(string $parentPath, int $offset = 0): string
    {
        $column = static::$_pathColumn;
        $separator = static::$_pathSeparator;
        $segmentLength = static::$_segmentLength;

        /*
        **  Alle Pfade, die direkt unter dem Parent liegen, ermitteln. */
        $paths = static::query()
            ->where($column, 'like', $parentPath . $separator . '%')
            ->whereRaw(
                'LENGTH(' . $column . ') = ?',
                [strlen($parentPath) + strlen($separator) + $segmentLength]

            ) ->pluck($column); // Nur die Spalte abrufen

        /*
        **  Letztes Segment extrahieren und Hex->Decimal umwandeln. */
        $max = collect($paths)->map(function ($path) use ($separator) {
            $segments = explode($separator, $path);

            return hexdec(end($segments)); // letztes Segment

        })->max();
        /*
        **  Nächsthöheren Wert errechnen. */
        $next = ($max ?? 0) + 1 + $offset; // Offset hinzufügen
        /*
        **  In Hex konvertieren und auffüllen. */
        return strtoupper(
            str_pad(dechex($next), $segmentLength, '0', STR_PAD_LEFT)
        );
    } // nextPathSegment()


} // trait ModelHasMaterializedPath {}
