<?php
declare(strict_types=1);

use App\Core\Auth;
use App\Core\Csrf;
use App\Core\Session;
use App\Core\View;

function config(?string $key = null, mixed $default = null): mixed
{
    static $config;
    if ($config === null) {
        $config = require CONFIG_PATH . '/config.php';
    }
    if ($key === null) {
        return $config;
    }
    $bits = explode('.', $key);
    $value = $config;
    foreach ($bits as $bit) {
        if (!is_array($value) || !array_key_exists($bit, $value)) {
            return $default;
        }
        $value = $value[$bit];
    }
    return $value;
}

function e(?string $value): string
{
    return htmlspecialchars((string) $value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

function url(string $path = ''): string
{
    $base = rtrim((string) config('app.base_path', ''), '/');
    $path = '/' . ltrim($path, '/');
    if ($path === '/') {
        return $base . '/';
    }
    return $base . $path;
}

function asset(string $path): string
{
    $path = ltrim($path, '/');
    $url = url('assets/' . $path);
    $full = BASE_PATH . '/assets/' . $path;
    if (is_file($full)) {
        $url .= '?v=' . filemtime($full);
    }
    return $url;
}

function redirect(string $path, int $code = 302): never
{
    header('Location: ' . url($path), true, $code);
    exit;
}

function csrf_token(): string
{
    return Csrf::token();
}

function csrf_field(): string
{
    return '<input type="hidden" name="_csrf" value="' . e(Csrf::token()) . '">';
}

function auth(): Auth
{
    return Auth::instance();
}

function current_user(): ?array
{
    return Auth::instance()->user();
}

function current_account(): ?array
{
    return Auth::instance()->account();
}

function can_see(string $page): bool
{
    return Auth::instance()->canSee($page);
}

function flash(string $key, ?string $message = null): ?string
{
    if ($message !== null) {
        Session::flash($key, $message);
        return null;
    }
    return Session::getFlash($key);
}

function money_fmt(float|int|string|null $amount): string
{
    return '$' . number_format((float) $amount, 0);
}

function name_initials(?string $first, ?string $last = ''): string
{
    $a = mb_substr(trim((string) $first), 0, 1);
    $b = mb_substr(trim((string) $last), 0, 1);
    return strtoupper($a . $b) ?: '?';
}

function cpt_pill(string $status): string
{
    $st = strtolower(str_replace([' ', '-'], '_', $status));
    return 'cpt-pill is-' . preg_replace('/[^a-z0-9_]/', '', $st);
}

/** @return array{text: string, tone: string, pill: string} */
function cpt_status_label(string $status): array
{
    $st = strtolower(str_replace([' ', '-'], '_', $status));
    $text = match ($st) {
        'in_progress' => 'In progress',
        'pending' => 'Waiting',
        default => ucfirst(str_replace('_', ' ', $st)),
    };
    $tone = match ($st) {
        'paid', 'fulfilled', 'signed', 'done', 'active' => 'done',
        'partial', 'in_progress', 'pending' => 'progress',
        'unpaid', 'expired', 'cancelled', 'void', 'retired' => 'alert',
        default => 'open',
    };
    return ['text' => $text, 'tone' => $tone, 'pill' => cpt_pill($st)];
}

function dt_fmt(?string $datetime, string $format = 'M j, Y'): string
{
    if (!$datetime) {
        return '—';
    }
    $ts = strtotime($datetime);
    return $ts ? date($format, $ts) : '—';
}

function json_out(array $payload, int $code = 200): never
{
    while (ob_get_level() > 0) {
        ob_end_clean();
    }
    http_response_code($code);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode($payload, JSON_UNESCAPED_UNICODE);
    exit;
}

function view(string $template, array $data = [], ?string $layout = 'layouts/app'): void
{
    View::render($template, $data, $layout);
}

function request_ip(): string
{
    return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}

function is_ajax(): bool
{
    return strtolower($_SERVER['HTTP_X_REQUESTED_WITH'] ?? '') === 'xmlhttprequest'
        || str_contains($_SERVER['HTTP_ACCEPT'] ?? '', 'application/json');
}
