<?php

use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Model;

/**
 * Generate a pseudo-random UUID string of hexadecimal characters.
 *
 * @param int $length The desired length of the UUID. Default is 6.
 * @return string A string containing the generated UUID.
 */
function getUuid(int $length = 6): string
{
    return bin2hex(random_bytes($length - 1));
}

/**
 * Generates a random one-time password (OTP) of a given length.
 *
 * The generated OTP is a string of numeric characters, zero-padded to the
 * left to achieve the desired length. The default length is 5 digits.
 *
 * @param int $length The desired length of the OTP. Defaults to 5.
 * @return string A string containing the generated OTP.
 */
function getOtp(int $length = 5)
{
    return str_pad(random_int(0, 99999), $length, '0', STR_PAD_LEFT);
}

/**
 * Truncates a given text string to a certain length and appends
 * an ellipsis if the string is longer than the specified length.
 *
 * @param string $text The text string to truncate.
 * @param int $max The maximum length of the string. Defaults to 35.
 * @return string The truncated string.
 */
function ellipsis(string|null $text = null, int $max = 35): string
{
    return empty($text) || strlen($text) <= $max ? $text : mb_substr($text, 0, $max, 'UTF-8') . '...';
}

/**
 * If the given value is empty, returns a string indicating that the value is
 * unavailable. Otherwise, returns the given value.
 *
 * @param mixed $value The value to check for emptiness. Defaults to null.
 * @return string The given value if non-empty, otherwise a HTML string indicating that the value is unavailable.
 */
function canEmpty(mixed $value = null): string
{
    return empty($value) ? '<small><i>Unavailable</i></small>' : $value;
}

/**
 * Convert a given string from underscore notation to a human-readable format.
 *
 * This function replaces all underscores with spaces and upper-cases the first
 * letter of each word to create a string suitable for human consumption.
 *
 * @param string $string The string to be humanized.
 * @return string The humanized string.
 */
function humanize(string $string): string
{
    if (empty($string)) return '';

    return ucwords(str_replace('_', ' ', $string));
}

/**
 * Format a given amount as a currency string.
 *
 * @param float|null $amount The amount to format. If null, return $0.00.
 * @param bool $withCurrencySymbol Whether to include the currency symbol. Defaults to true.
 * @return string|float The formatted amount as a string if $withCurrencySymbol is true, otherwise the amount as a float.
 */
function currency(float|null $amount = null, $withCurrencySymbol = true): string|float
{
    if (is_null($amount)) {
        $amount = 0.00;
    }

    $amount = number_format($amount, 0, '.', ',');

    return $withCurrencySymbol
        ? env('CURRENCY_SYMBOL', '$') . ' ' . $amount
        : $amount;
}

/**
 * Returns a random CSS class name for background color styling.
 *
 * This function randomly selects a class name from a predefined list of
 * CSS classes that represent different background colors.
 *
 * @return string A randomly selected CSS class name for background color.
 */
function getRandomColorClass(): string
{
    $classes = ['bg-warning', 'bg-info', 'bg-danger', 'bg-blue', 'bg-pink', 'bg-indigo', 'bg-secondary', 'bg-purple'];

    return $classes[mt_rand(0, count($classes) - 1)];
}

/**
 * Returns the initials of a full name.
 *
 * This function takes a full name as input and returns a string consisting of the
 * first letter of the first name and the first letter of the last name, all
 * uppercase. If the full name is a single word, it simply returns the first
 * letter of that word, all uppercase.
 *
 * @param string $fullName The full name.
 * @return string The initials of the full name.
 */
function getNameInitials(string $fullName): string
{
    $words = explode(' ', $fullName);

    if (count($words) > 1) {
        return strtoupper(substr($words[0], 0, 1) . substr($words[1], 0, 1));
    }

    return strtoupper(substr($words[0], 0, 1));
}

/**
 * Returns HTML for displaying the user's avatar.
 *
 * This function takes a user object as an optional argument. If no user object
 * is provided, it defaults to the currently authenticated user.
 *
 * If the user has a profile picture, this function returns an HTML string
 * containing an `<img>` element with the URL of the user's avatar. Otherwise,
 * it returns an HTML string containing a `<span>` element with the user's
 * initials.
 *
 * @param App\Models\User|null $user The user object. Defaults to the authenticated user.
 * @return string The HTML string for displaying the user's avatar.
 */
function getAvatarHtml(App\Models\User|null $user = null): string
{
    $user = !empty($user) ? $user : auth()->user();

    if (
        !empty($user->avatar)
        && Illuminate\Support\Facades\Storage::disk('public')->exists($user->avatar)
    ) {
        return '
            <span style="display: none;">' . getNameInitials($user->fullName) . '</span>
            <img src="' . Illuminate\Support\Facades\Storage::url('thumbnails/' . $user->avatar) . '" alt="avatar">
        ';
    }

    return '
        <span>' . getNameInitials($user->fullName) . '</span>
        <img src="" alt="avatar" style="display: none;">
    ';
}

/**
 * Retrieve the URL for an image stored in the public disk.
 *
 * This function checks if the provided path exists in the public disk. If so,
 * it returns the URL to the image. Optionally, it can return the URL to the
 * thumbnail version of the image if $isThumbnail is true. If the path does not
 * exist or is empty, it returns the URL of a placeholder image.
 *
 * @param string|null $path The path of the image in the storage.
 * @param bool $isThumbnail Indicates whether to return the URL for the thumbnail version. Defaults to false.
 * @return string The URL of the image or the placeholder image if the path is not valid.
 */
function getImageUrlByPath(string|null $path = null, $isThumbnail = false): string
{
    if (
        !empty($path)
        && Illuminate\Support\Facades\Storage::disk('public')->exists($path)
    ) {
        return Illuminate\Support\Facades\Storage::url(
            ($isThumbnail ? 'thumbnails/' : '') . $path
        );
    }

    return asset('assets/images/placeholder.png');
}

/**
 * Truncates a given text string to a certain length and appends
 * an ellipsis if the string is longer than the specified length.
 *
 * @param string $text The text string to truncate.
 * @param int $max The maximum length of the string. Defaults to 35.
 * @return string The truncated string.
 */
function addEllipsis($text, $max = 35)
{
    return strlen($text) > $max ? mb_substr($text, 0, $max, 'UTF-8') . '...' : $text;
}

function generateUniqueSlug(Model $model, string $name, string $column = 'slug'): string
{
    $slug = Str::slug($name);
    $originalSlug = $slug;
    $count = 1;

    while ($model->where($column, $slug)->exists()) {
        $slug = $originalSlug . '-' . $count;
        $count++;
    }

    return $slug;
}

/**
 * Converts a number of days to years, months, and days.
 *
 * @param int $days The number of days to convert.
 * @return string A string describing the time span in years, months, and days.
 */
function convertDaysToMonthsYears($days)
{
    if (!is_numeric($days) || $days < 0) return 'Invalid input'; // Handle non-numeric or negative days

    $years = floor($days / 365);  // Calculate full years
    $remainingDays = $days % 365;
    $months = floor($remainingDays / 30); // Calculate remaining months
    $remainingDays = $remainingDays % 30; // Calculate leftover days

    $result = [];
    if ($years > 0) $result[] = "$years year" . ($years > 1 ? 's' : '');

    if ($months > 0) $result[] = "$months month" . ($months > 1 ? 's' : '');

    if ($remainingDays > 0) $result[] = "$remainingDays day" . ($remainingDays > 1 ? 's' : '');

    return implode(', ', $result) ?: '0 days'; // Default if no meaningful time unit
}

/**
 * Replaces underscores with spaces and capitalizes the first letter of each word.
 *
 * @param string $string The string to modify.
 * @return string The modified string.
 */
function replaceUnderscores($string)
{
    return ucwords(str_replace('_', ' ', $string));
}

/**
 * Trims the module name from a permission string.
 *
 * @param string $permissionName The permission name to trim.
 * @return string The trimmed permission name.
 */
function trimModuleNameFromPermission(string $permissionName)
{
    if (strpos($permissionName, '.') !== false)
        $permissionName = trim(explode('.', $permissionName, 2)[1]);

    return ucwords($permissionName);
}
