<?php

use App\Services\Frontend\FrontendContentStore;

/**
 * Frontend content helper — the single read point for the public
 * marketing site's editable copy, colors, and media.
 *
 *   {{ fc('home.hero.headline', 'The complete WhatsApp platform') }}
 *   style="background: {{ fc('theme.wa.deep', '#075E54') }}"
 *
 * The 2nd argument is the hardcoded default (today's shipped value), so
 * an un-edited install renders identically. Returns the published value
 * for visitors and the draft value for an admin in the live editor.
 *
 * Registered via AppServiceProvider::register() (require_once), so it is
 * available everywhere without a composer "files" autoload entry.
 */
if (! function_exists('fc')) {
    function fc(string $key, mixed $default = null): mixed
    {
        return app(FrontendContentStore::class)->get($key, $default);
    }
}

if (! function_exists('fc_editing')) {
    /** True when the current request is the admin live-editor preview. */
    function fc_editing(): bool
    {
        return app(FrontendContentStore::class)->editing();
    }
}

if (! function_exists('site_info')) {
    /**
     * Shared "site identity" values managed at /admin/site-settings and
     * stored under SystemSetting `site.*` keys (emails, phone, address,
     * social links). Read across the footer, contact, and about pages so
     * one edit updates them everywhere.
     *
     *   {{ site_info('email_support', 'team@wadesk.io') }}
     */
    function site_info(string $key, mixed $default = null): mixed
    {
        try {
            $val = \App\Models\SystemSetting::get('site.' . $key, null);
        } catch (\Throwable $e) {
            return $default;
        }
        return ($val === null || $val === '') ? $default : $val;
    }
}

if (! function_exists('brand_name')) {
    /**
     * The admin-configured platform name (/admin/settings/general).
     * Use anywhere a user-visible string needs the product name so the
     * whole app re-brands from one setting:  {{ brand_name() }}
     */
    function brand_name(): string
    {
        return \App\Support\Brand::name();
    }
}

if (! function_exists('fc_page')) {
    /**
     * Which editable marketing page is being rendered right now, derived
     * from the route name. Returns 'home' | 'features' | 'pricing', or null
     * when the current request isn't one of those (so callers fall back to
     * a global, page-agnostic key).
     */
    function fc_page(): ?string
    {
        $name = optional(request()->route())->getName();
        return match ($name) {
            'frontend.home'     => 'home',
            'frontend.features' => 'features',
            'frontend.pricing'  => 'pricing',
            'frontend.about'    => 'about',
            'frontend.contact'  => 'contact',
            default             => null,
        };
    }
}

if (! function_exists('fc_skey')) {
    /**
     * Page-scope a content key. On a known page, "faq.headline" becomes
     * "home.faq.headline" so the same shared component edited on a
     * different page keeps an independent value. Off-page → unscoped.
     */
    function fc_skey(string $key): string
    {
        $p = fc_page();
        return $p ? "{$p}.{$key}" : $key;
    }
}

if (! function_exists('fcp')) {
    /** Page-scoped read: fc() against the page-scoped key. */
    function fcp(string $key, mixed $default = null): mixed
    {
        return app(FrontendContentStore::class)->get(fc_skey($key), $default);
    }
}

if (! function_exists('fc_section_order')) {
    /**
     * The order to render a page's sections in. Reads the admin's saved
     * order (`<page>.__order`) and intersects it with the sections that
     * actually exist ($available, in their default order). Any section not
     * in the saved order is appended in its default position, so adding a
     * new section later can never make it vanish. No saved order → default.
     *
     * @param  string    $page
     * @param  string[]  $available  default-ordered slugs present on the page
     * @return string[]
     */
    function fc_section_order(string $page, array $available): array
    {
        $saved = app(FrontendContentStore::class)->get("{$page}.__order", []);
        if (! is_array($saved) || empty($saved)) {
            return $available;
        }
        $ordered = [];
        foreach ($saved as $slug) {
            if (in_array($slug, $available, true) && ! in_array($slug, $ordered, true)) {
                $ordered[] = $slug;
            }
        }
        foreach ($available as $slug) {
            if (! in_array($slug, $ordered, true)) {
                $ordered[] = $slug;
            }
        }
        return $ordered;
    }
}

if (! function_exists('node_token')) {
    /**
     * The single shared secret between Laravel and the Node bridge, used both
     * to AUTHENTICATE inbound Node→Laravel calls and to SIGN outbound
     * Laravel→Node calls. Resolution order (first non-empty wins):
     *   1. SystemSetting 'node_webhook_token'   — set by admin at
     *      /admin/settings/wadesk-message (encrypted at rest)
     *   2. SystemSetting 'baileys_callback_token' — legacy key, kept for
     *      installs that set it before the rename
     *   3. .env NODE_WEBHOOK_TOKEN               — what the installer writes
     *
     * IMPORTANT: the Node bridge validates against its OWN process.env
     * NODE_WEBHOOK_TOKEN, so whatever value the admin sets here must also be
     * set in node/.env (they must match on both sides).
     */
    function node_token(): string
    {
        try {
            $v = (string) \App\Models\SystemSetting::get('node_webhook_token', '');
            if ($v === '') {
                $v = (string) \App\Models\SystemSetting::get('baileys_callback_token', '');
            }
        } catch (\Throwable $e) {
            $v = '';
        }
        return $v !== '' ? $v : (string) env('NODE_WEBHOOK_TOKEN', '');
    }
}

if (! function_exists('wd_node_url')) {
    /**
     * Base URL of the Node bridge. DB `baileys_server_url` (set by admin at
     * /admin/settings/wadesk-message) is authoritative; falls back to the
     * .env SERVER_URL the installer writes. Mirrors node_token() so the whole
     * app reads the same place — no DB-vs-env drift. Returned without a
     * trailing slash.
     */
    function wd_node_url(): string
    {
        try {
            $v = (string) \App\Models\SystemSetting::get('baileys_server_url', '');
        } catch (\Throwable $e) {
            $v = '';
        }
        if ($v === '') {
            $v = (string) env('SERVER_URL', '');
        }
        return rtrim($v, '/');
    }
}

if (! function_exists('wd_base')) {
    /**
     * The URL sub-folder this install is served from — "/public" when the
     * web root is the project root and the app is reached at example.com/public/,
     * or "" when the web root already points at public/ (clean install).
     *
     * Computed purely from $_SERVER (the physical location of index.php vs the
     * web server's document root), so it can NEVER be wrong because of a cached
     * config, a stale APP_URL, or a host that miscomputes Laravel's base path.
     * This is the single source of truth used by AppServiceProvider (to force
     * the URL root) and by the layouts (to feed the client-side AJAX prefix).
     *
     *   <a href="{{ wd_base() }}/admin">   →   /public/admin  (or /admin at root)
     */
    function wd_base(): string
    {
        static $cached = null;
        if ($cached !== null) {
            return $cached;
        }

        $docRoot   = rtrim(str_replace('\\', '/', (string) ($_SERVER['DOCUMENT_ROOT'] ?? '')), '/');
        $scriptDir = rtrim(str_replace('\\', '/', dirname((string) ($_SERVER['SCRIPT_FILENAME'] ?? ''))), '/');

        $base = '';
        if ($docRoot !== '' && $scriptDir !== '' && str_starts_with($scriptDir, $docRoot)) {
            // e.g. scriptDir=/home/u/wadesk/public, docRoot=/home/u/wadesk → "/public"
            $base = rtrim(substr($scriptDir, strlen($docRoot)), '/');
        }

        if ($base === '') {
            // Fallback: directory of the script in the URL (e.g. /public/index.php).
            $script = str_replace('\\', '/', (string) ($_SERVER['SCRIPT_NAME'] ?? ''));
            $dir    = rtrim(dirname($script), '/');
            $base   = ($dir === '/' || $dir === '.') ? '' : $dir;
        }

        // Normalize: convert any Windows backslashes first (PHP's dirname()
        // returns "\" for a root path on Windows, e.g. dirname('/index.php')),
        // then strip slashes and collapse to "" or "/sub" — NEVER a bare "/" or
        // anything with a "\" (both produce broken "//x" / "/\/x" URLs).
        $base = '/' . trim(str_replace('\\', '/', $base), '/');
        if ($base === '/') {
            $base = '';
        }

        return $cached = $base;
    }
}

if (! function_exists('fc_section_visible')) {
    /**
     * Should a composition page render this section? False only when an
     * admin hid it via the live editor. Defaults to visible, so an
     * un-edited install shows every section exactly as shipped.
     */
    function fc_section_visible(string $page, string $slug): bool
    {
        return \App\Services\Frontend\FrontendRegistry::visible($page, $slug);
    }
}

if (! function_exists('app_font_catalog')) {
    /** Curated Google-font catalog for the admin font-family picker. */
    function app_font_catalog(): array
    {
        return [
            'inter'        => ['label' => 'Inter',              'stack' => "'Inter', system-ui, sans-serif",             'g' => 'Inter:wght@400;500;600;700'],
            'roboto'       => ['label' => 'Roboto',             'stack' => "'Roboto', system-ui, sans-serif",            'g' => 'Roboto:wght@400;500;700'],
            'open-sans'    => ['label' => 'Open Sans',          'stack' => "'Open Sans', system-ui, sans-serif",         'g' => 'Open+Sans:wght@400;500;600;700'],
            'poppins'      => ['label' => 'Poppins',            'stack' => "'Poppins', system-ui, sans-serif",           'g' => 'Poppins:wght@400;500;600;700'],
            'lato'         => ['label' => 'Lato',               'stack' => "'Lato', system-ui, sans-serif",              'g' => 'Lato:wght@400;700'],
            'montserrat'   => ['label' => 'Montserrat',         'stack' => "'Montserrat', system-ui, sans-serif",        'g' => 'Montserrat:wght@400;500;600;700'],
            'nunito'       => ['label' => 'Nunito',             'stack' => "'Nunito', system-ui, sans-serif",            'g' => 'Nunito:wght@400;600;700'],
            'work-sans'    => ['label' => 'Work Sans',          'stack' => "'Work Sans', system-ui, sans-serif",         'g' => 'Work+Sans:wght@400;500;600;700'],
            'dm-sans'      => ['label' => 'DM Sans',            'stack' => "'DM Sans', system-ui, sans-serif",           'g' => 'DM+Sans:wght@400;500;700'],
            'rubik'        => ['label' => 'Rubik',              'stack' => "'Rubik', system-ui, sans-serif",             'g' => 'Rubik:wght@400;500;600;700'],
            'manrope'      => ['label' => 'Manrope',            'stack' => "'Manrope', system-ui, sans-serif",           'g' => 'Manrope:wght@400;500;600;700'],
            'plus-jakarta' => ['label' => 'Plus Jakarta Sans', 'stack' => "'Plus Jakarta Sans', system-ui, sans-serif", 'g' => 'Plus+Jakarta+Sans:wght@400;500;600;700'],
        ];
    }
}

if (! function_exists('app_font')) {
    /**
     * Resolve the admin-selected UI font (general settings → font_family).
     * Returns ['key','label','stack','url']. Empty key = theme default, so no
     * Google font is loaded and no CSS override is emitted.
     */
    function app_font(): array
    {
        try {
            $key = (string) \App\Models\SystemSetting::get('font_family', '');
        } catch (\Throwable $e) {
            $key = '';
        }

        $catalog = app_font_catalog();
        if ($key === '' || ! isset($catalog[$key])) {
            return ['key' => '', 'label' => 'Theme default', 'stack' => '', 'url' => ''];
        }

        $f = $catalog[$key];
        return [
            'key'   => $key,
            'label' => $f['label'],
            'stack' => $f['stack'],
            'url'   => 'https://fonts.googleapis.com/css2?family=' . $f['g'] . '&display=swap',
        ];
    }
}
