<?php

use Carbon\CarbonImmutable;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;

if (!function_exists('log_message')) {
    function log_message(string $level, string $message, array $context = []): void
    {
        $level = strtolower($level);
        $logger = Log::channel(config('logging.default'));
        $context = $context;
        if (!empty($context)) {
            $message = strtr($message, array_combine(
                array_map(fn($k) => "{" . $k . "}", array_keys($context)),
                array_map('strval', $context)
            ));
        }
        $logger->{$level}($message);
    }
}

if (!function_exists('esc')) {
    function esc($value): string
    {
        return e($value);
    }
}

if (!function_exists('utc_now')) {
    function utc_now(): string
    {
        return CarbonImmutable::now('UTC')->toDateTimeString();
    }
}

if (!function_exists('local_date')) {
    function local_date(string $utcDateTime, string $format = 'Y-m-d H:i:s'): string
    {
        try {
            $carbon = CarbonImmutable::parse($utcDateTime)->setTimezone(config('app.timezone'));
            return $carbon->format($format);
        } catch (\Throwable $e) {
            return CarbonImmutable::parse($utcDateTime)->format($format);
        }
    }
}

if (!function_exists('user_timezone')) {
    function user_timezone(): string
    {
        return (string) (config('School')->attendance['timezone'] ?? config('app.timezone', 'America/New_York'));
    }
}

if (!function_exists('model')) {
    function model(string $class)
    {
        return app()->make($class);
    }
}

if (!function_exists('pbkdf2_hash')) {
    function pbkdf2_hash(string $password, string $algo = 'sha256', int $iterations = 100000, int $length = 64): string
    {
        $salt = random_bytes(16);
        $saltHex = bin2hex($salt);
        $derived = hash_pbkdf2($algo, $password, $salt, $iterations, $length, true);
        $derivedHex = bin2hex($derived);

        return implode('$', [$algo, $iterations, $saltHex, $derivedHex]);
    }
}

if (!function_exists('pbkdf2_verify')) {
    function pbkdf2_verify(string $password, string $storedHash): bool
    {
        $delim = strpos($storedHash, '$') !== false ? '$' : ':';
        $parts = explode($delim, $storedHash);
        if (count($parts) !== 4) {
            return false;
        }

        [$algo, $iterations, $saltHex, $derivedHex] = $parts;
        if ($saltHex === '' || $derivedHex === '') {
            return false;
        }

        $salt = ctype_xdigit($saltHex) ? hex2bin($saltHex) : false;
        $derived = ctype_xdigit($derivedHex) ? hex2bin($derivedHex) : false;
        if ($salt === false || $derived === false) {
            return false;
        }

        $calc = hash_pbkdf2($algo, $password, $salt, (int) $iterations, strlen($derived), true);
        return hash_equals($derived, $calc);
    }
}

if (!function_exists('legacy_password_verify')) {
    function legacy_password_verify(string $password, ?string $storedHash): bool
    {
        return ci_password_verify($password, $storedHash);
    }
}

if (!function_exists('ci_password_verify')) {
    function ci_password_verify(string $password, ?string $storedHash): bool
    {
        if (!$storedHash) {
            return false;
        }

        if (strpos($storedHash, ':') !== false || strpos($storedHash, '$') !== false) {
            return pbkdf2_verify($password, $storedHash);
        }

        return Hash::check($password, $storedHash);
    }
}
