<?php 

use App\Models\GeneralSetting;
use App\Models\ParentCategory;
use App\Models\Category;
use App\Models\Slide;
use App\Models\Post;
use Carbon\Carbon;
use Illuminate\Support\Str;
use App\Models\SiteSocialLink;


/**
 * Einstellungen
 * Lädt die allgemeinen Website-Einstellungen aus der Datenbank. 
 *

 * Hinweis:
 * - Die Funktion gibt aktuell nur dann etwas zurück, wenn Settings existieren.
 *   Andernfalls endet sie ohne Return (entspricht in PHP: null).
 *
 * @example
 * $title = settings()?->site_title; // PHP 8+ Nullsafe Operator
 * $logo  = settings()?->site_logo;
 */

if(!function_exists("settings")) {
    function settings() {
        $settings = GeneralSetting::take(1)->first();

        if(!is_null($settings)) { return $settings; }
    }
}

/**
 * Seiten Social Link
 * Lädt die Social-Media-Links der Website aus der Datenbank.
 *
 * Hinweis:
 * - Die Funktion gibt nur dann einen Wert zurück, wenn Einträge existieren.
 *   Andernfalls endet sie ohne expliziten Return (entspricht in PHP: null).
 *
 * @example
 * $facebook = site_social_links()?->facebook;
 * $twitter  = site_social_links()?->twitter;
 */
if(!function_exists('site_social_links')) {
    function site_social_links() {
        $links = SiteSocialLink::take(1)->first();
        if(!is_null($links)) { return $links; }
    }
}

/**
 * Dynamisches Navigations Menu
 * Generiert die HTML-Navigation basierend auf Kategorien mit Beiträgen.
 *
 * Hinweis:
 * - Es werden nur Kategorien berücksichtigt, die mindestens einen Beitrag haben.
 * - Parent-Kategorien werden als Dropdown dargestellt, sofern Kinder mit Beiträgen existieren.
 * - Kategorien ohne Parent werden als normale Navigationslinks ausgegeben.
 *
 * @return string  HTML-Markup der Navigationsliste
 *
 * @example
 * {!! navigations() !!}
 */
if(!function_exists('navigations')) {
    function navigations() {
        $navigations_html = '';

        $pcategories = ParentCategory::whereHas('children', function($q) {
            $q->whereHas('posts');
        })->orderBy('name', 'asc')->get();

        $categories = Category::whereHas('posts')->where('parent', 0)->orderBy('name', 'asc')->get();

        if(count($pcategories) > 0) {
            foreach($pcategories as $item) {
                $navigations_html.='
                    <li class="nav-item dropdown">
                        <a class="nav-link" href="#!" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                            '.$item->name.' <i class="ti-angle-down ml-1"></i>
                        </a>
                        <div class="dropdown-menu">
                    ';

                    foreach($item->children as $category) {
                        if($category->posts->count() > 0) {
                            $navigations_html.='<a class="dropdown-item" href="'.route('category_posts', $category->slug).'">'.$category->name.'</a>';
                        }
                    }

                $navigations_html.='
                    </div>
                </li>
                ';
            }
        }

        if(count($categories) > 0) {
            foreach($categories as $item) {
                $navigations_html.='
                    <li class="nav-item">
                        <a class="nav-link" href="'.route('category_posts', $item->slug).'">'.$item->name.'</a>
                    </li>
                ';
            }
        }

        return $navigations_html;
    }
}

/**
 * DATE FORMAT January 12, 2024
 * Formatiert ein Datum in ein lesbares, lokalisiertes Format.
 *
 * Hinweis:
 * - Erwartet ein Datum im Format `Y-m-d H:i:s`.
 * - Die Ausgabe erfolgt im ISO-Format `LL` (z. B. „14. September 2025“).
 *
 * @param  string  $value  Datum/Zeit im Format `Y-m-d H:i:s`
 * @return string          Formatiertes Datum
 *
 * @example
 * {{ date_formatter($post->created_at) }}
 */
if(!function_exists('date_formatter')) {
    function date_formatter($value) {
        return Carbon::createFromFormat('Y-m-d H:i:s', $value)->isoFormat('LL');
    }
}

/**
 * Kürzt Wörter
 * Kürzt einen Text auf eine bestimmte Anzahl von Wörtern.
 *
 * Hinweis:
 * - HTML-Tags werden vor der Kürzung entfernt.
 * - Am Ende des Textes wird optional ein Abschlusszeichen angehängt.
 *
 * @param  string  $value  Ausgangstext
 * @param  int     $words  Maximale Anzahl an Wörtern (Standard: 15)
 * @param  string  $end    Angehängter Text am Ende (Standard: "...")
 * @return string          Gekürzter Text
 *
 * @example
 * {{ words($post->content, 30) }}
 */
if(!function_exists('words')) {
    function words($value, $words = 15, $end = '...') {
        return Str::words(strip_tags($value), $words, $end);
    }
}

/**
 * Lesezeit
 * Berechnet die geschätzte Lesezeit eines Textes.
 *
 * Hinweis:
 * - Die Berechnung basiert auf ca. 200 Wörtern pro Minute.
 * - Die minimale Rückgabe beträgt immer 1 Minute.
 * - Mehrere Textteile können als Argumente übergeben werden.
 *
 * @param  string  ...$text  Ein oder mehrere Textinhalte
 * @return int               Geschätzte Lesezeit in Minuten
 *
 * @example
 * {{ readDuration($post->title, $post->content) }} Min. Lesezeit
 */
if (!function_exists('readDuration')) {
    function readDuration(...$text): int
    {
        // Alles zu einem String zusammenfügen
        $content = implode(' ', $text);
        // HTML-Tags entfernen
        $content = strip_tags($content);
        // Wörter zählen (für DE ausreichend gut)
        $totalWords = str_word_count($content);
        // Lesegeschwindigkeit (Wörter pro Minute) – nach Bedarf anpassen
        $wordsPerMinute = 220;
        // Minuten berechnen und immer aufrunden
        $minutesToRead = (int) ceil($totalWords / max(1, $wordsPerMinute));
        // Mindestens 1 Minute zurückgeben
        return max(1, $minutesToRead);
    }
}

/**
 * Zeigt letzten Post an
 * Gibt die neuesten sichtbaren Blogbeiträge zurück.
 *
 * Hinweis:
 * - Es werden nur veröffentlichte Beiträge berücksichtigt (`visibility = 1`).
 * - Über `$skip` können Einträge übersprungen werden (z. B. für Slider oder Pagination).
 *
 * @param  int  $skip   Anzahl der zu überspringenden Beiträge (Standard: 0)
 * @param  int  $limit  Maximale Anzahl der zurückgegebenen Beiträge (Standard: 5)
 * @return \Illuminate\Support\Collection  Sammlung von Blogbeiträgen
 *
 * @example
 * @foreach(latest_posts(0, 3) as $post)
 *     {{ $post->title }}
 * @endforeach
 */
if(!function_exists('latest_posts')) {
    function latest_posts($skip = 0, $limit = 5) {
        return Post::skip($skip)->limit($limit)->where('visibility', 1)->orderBy('created_at', 'desc')->get();
    }
}

/**
 * Listing Categories With Number of Posts on Sidebar
 * Gibt Kategorien für die Sidebar zurück, sortiert nach Beitragsanzahl.
 *
 * Hinweis:
 * - Es werden nur Kategorien mit mindestens einem Beitrag berücksichtigt.
 * - Die Sortierung erfolgt absteigend nach Anzahl der Beiträge.
 *
 * @param  int  $limit  Maximale Anzahl der Kategorien (Standard: 8)
 * @return \Illuminate\Support\Collection  Sammlung von Kategorien
 *
 * @example
 * @foreach(sidebar_categories() as $category)
 *     {{ $category->name }} ({{ $category->posts_count }})
 * @endforeach
 */
if(!function_exists('sidebar_categories')) {
    function sidebar_categories($limit = 8) {
        return Category::withCount('posts')->having('posts_count', '>', 0)->limit($limit)->orderBy('posts_count', 'desc')->get();
    }
}


/**
 * FETCH ALL TAGS FROM THE 'POSTS' TABLE
 * Gibt eine eindeutige Liste aller verwendeten Tags zurück.
 *
 * Hinweis:
 * - Tags werden aus dem `tags`-Feld der Beiträge gelesen (kommasepariert).
 * - Leere Tag-Felder werden ignoriert.
 * - Die Rückgabe ist alphabetisch sortiert und eindeutig.
 *
 * @param  int|null  $limit  Maximale Anzahl der Tags (optional)
 * @return array             Liste der Tags
 *
 * @example
 * @foreach(getTags() as $tag)
 *     {{ $tag }}
 * @endforeach
 */
if (!function_exists('getTags')) {
    function getTags($limit = null) {
        $tags = Post::whereNotNull('tags')
            ->where('tags', '!=', '')
            ->pluck('tags');

        $uniqueTags = $tags->flatMap(function ($tagsString) {
                return explode(',', $tagsString);
            })
            ->map(fn($tag) => normalizeTag($tag))
            ->filter()
            ->unique()
            ->sort()
            ->values();

        if ($limit) {
            $uniqueTags = $uniqueTags->take($limit);
        }

        return $uniqueTags->all();
    }
}

function normalizeTag(string $tag): string
{
    // urldecode: wandelt %20 und auch + -> Leerzeichen (wichtig!)
    $tag = urldecode($tag);

    // Whitespace vereinheitlichen
    $tag = preg_replace('/\s+/u', ' ', trim($tag));

    return $tag;
}


/**
 * Listing Sidebar Latest posts
 * Gibt die neuesten sichtbaren Blogbeiträge für die Sidebar zurück.
 *
 * Hinweis:
 * - Es werden nur veröffentlichte Beiträge berücksichtigt (`visibility = 1`).
 * - Optional kann ein bestimmter Beitrag ausgeschlossen werden.
 * - Die Sortierung erfolgt nach Erstellungsdatum (neueste zuerst).
 *
 * @param  int       $limit   Maximale Anzahl der Beiträge (Standard: 5)
 * @param  int|null  $except  ID eines auszuschließenden Beitrags (optional)
 * @return \Illuminate\Support\Collection  Sammlung von Blogbeiträgen
 *
 * @example
 * @foreach(sidebar_latest_posts(5, $post->id) as $item)
 *     {{ $item->title }}
 * @endforeach
 */
if(!function_exists('sidebar_latest_posts')) {
    function sidebar_latest_posts($limit = 5, $except = null) {
        $posts = Post::limit($limit);
        if($except) {
            $posts = $posts->where('id', '!=', $except);
        }

        return $posts->where('visibility', 1)->orderBy('created_at', 'desc')->get();
    }
}

/**
 * Home Slider
 * Gibt aktive Slider-Einträge für den Home-Slider zurück.
 *
 * Hinweis:
 * - Es werden nur Slides mit aktivem Status (`status = 1`) berücksichtigt.
 * - Die Sortierung erfolgt nach der definierten Reihenfolge (`ordering`).
 *
 * @param  int  $limit  Maximale Anzahl der Slides (Standard: 5)
 * @return \Illuminate\Support\Collection  Sammlung von Slides
 *
 * @example
 * @foreach(get_slides() as $slide)
 *     {{ $slide->title }}
 * @endforeach
 */
if(!function_exists('get_slides')) {
    function get_slides($limit = 5) {
        return  Slide::where('status', 1)->limit($limit)->orderBy('ordering', 'asc')->get();
    }
}
?>