<?php

use Mtc\Core\Currency;
use Illuminate\Support\Collection;
use Mtc\Core\Permissions;
use Twig\Extra\Intl\IntlExtension;
use Twig\Extra\String\StringExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;

/*
 *   Extends Twig with a function that allows you to check if something is
 * numeric
 */


$test = new \Twig\TwigTest('numeric', function ($value) {
    return is_numeric($value);
});

$twig->addTest($test);

/*
 *   Add twig function for returning execution time of a script.
 */

$twig->addFunction(new TwigFunction('execution_time', function () {
    if (DEV_MODE) {
        return round(microtime(true) - START_TIME, 4) . 's';
    }
}));

/*
 *
 *  Expose the date_create_from_format function to Twig
 *  use as {{ create_from_format($date, 'd/m/Y H:i', 'jS F Y') }}
 *
 */

$twig->addFunction(new TwigFunction('create_from_format', function ($date, $format_from, $format_to) {
    if (!empty($date) && !empty($format_from) && !empty($format_to)) {
        return date_create_from_format($format_from, $date)->format($format_to);
    }
}));

/*
 *  Generate a hash value using the contents of a given file
 */

$twig->addFunction(new TwigFunction('uriHash', function ($uri) {
    return $uri . '?_' . hash_file('crc32', SITE_PATH . $uri);
}));

$twig->addFunction(new TwigFunction('md5', function ($string) {
    return md5($string);
}));

$twig->addFunction(new TwigFunction('copyRightRange', function ($date) {
    $ret = date('Y', strtotime($date));
    if ($ret < date('Y')) {
        $ret = $ret . ' &ndash; ' . date('Y');
    }
    return $ret;
}));

/**
 * Ordinal Filter
 */
$twig->addFilter(new \Twig\TwigFilter('ordinal', function ($number) {
    $ends = ['th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th'];
    if ((($number % 100) >= 11) && (($number % 100) <= 13)) {
        return $number . 'th';
    }

    return $number . $ends[$number % 10];
}, [
    'is_safe' => ['html']
]));

/*
 *  Print_r for twig (dev only)
 */
$twig->addFunction(new TwigFunction('e', function ($array) {
    if (!DEV_MODE) {
        return;
    }

    echo '<pre>';
    print_r($array);
    echo '</pre>';
}));

$twig->addFunction(new TwigFunction('dd', function (...$args) {
    if (!DEV_MODE) {
        return;
    }

    if (function_exists('dd')) {
        dd(...$args);
    }
}));

/*
 *
 *  Hooks function
 *  {{ action('header') }} would introduce a new action filter inside a template
 * file
 *
 */

$twig->addFunction(new TwigFunction('hooks_do_action', function () {
    call_user_func_array('HooksAdapter::do_action', func_get_args());
}));

$twig->addFunction(new TwigFunction('site_menu', function ($menu_name) {
    static $cache = [];
    if (array_key_exists($menu_name, $cache)) {
        return $cache[$menu_name];
    }

    $cache[$menu_name] = \Mtc\Plugins\SiteMenu\Classes\SiteMenu::fetchTree($menu_name);
    return $cache[$menu_name];
}));

/*
 *
 *  CSRF function
 *  {{ csrf_token('form_name') }} would add a security token to form
 *
 */
$twig->addFunction(new TwigFunction('csrf_token', function ($form_name) {
    return CSRF::token($form_name);
}));

$twig->addFunction(new TwigFunction('load_assets', function () {
    (new AssetLoader)->all();
}));

/*
 * Check if an array contains an array or object that has an attribute
 * with given value.
 * @param $needle value of attribute
 * @param @array_of_objects
 * @param @attribute_name
 * @return bool $success
 */
$twig->addFunction(
    new TwigFunction(
        'in_array_of_objects_or_array',
        function ($needle, $array_of_objects, $attribute_name = "id") {
            if (empty($array_of_objects)) {
                return false;
            }
            foreach ($array_of_objects as $object) {
                if (is_array($object)) {
                    $array = $object;
                } else {
                    $array = get_object_vars($object);
                }
                if (isset($array[$attribute_name]) && $array[$attribute_name] == $needle) {
                    return true;
                }
            }
            return false;
        }
    )
);

/**
 * get contents of file
 * @param $file string
 * @return $file string
 * @author Kieran McBurney <kieran.mcburney@mtcmedia.co.uk>
 */
$twig->addFunction(
    new TwigFunction(
        'file_get_contents',
        function ($file) {

            $fileinfo = pathinfo($file);
            $allowed_extensions = array('svg');

            if (in_array(strtolower($fileinfo['extension']), $allowed_extensions) && file_exists($file)) {
                $file = file_get_contents($file);
                return $file;
            } else {
                return '';
            }
        }
    )
);

/**
 * get the current URI
 * @return string
 * @author Aidan Ferguson <aidan.ferguson@mtcmedia.co.uk>
 */
$twig->addFunction(new TwigFunction('current_uri', function ($stip_vars = false) {

    $uri = $_SERVER['REQUEST_URI'];

    if ($stip_vars) {
        $uri = preg_replace('/\?.*?$/', '', $uri);
    }


    return $uri;
}));


/**
 * Filter user input of telephone number for output
 * @return string
 * @author Andrew Morgan <andrew.morgan@mtcmedia.co.uk>
 */
$twig->addFunction(new TwigFunction('filter_tel', function ($unsanitized_phone_number) {

    // Filter out all characters except numbers 0 to 9 and the plus character
    $unsanitized_phone_number = preg_replace('/[^0-9+]/', '', $unsanitized_phone_number);

    //matches country codes like +44 or 0044 at the start of a string when followed by a 0
    $regex = '/(^(\+|00)[1-9]{1,3})0/';

    //strip the predial 0 if a country code is used
    $sanitized_phone_number = preg_replace($regex, '$1', $unsanitized_phone_number);

    return $sanitized_phone_number;
}));

/**
 * casts values as strings and compares them if they are equal
 * @return bool
 * @author Rihards Silins <rihards.silins@mtcmedia.co.uk>
 */
$twig->addFunction(new TwigFunction('strings_are_equal', function ($first_value, $second_value) {
    return ((string) $first_value === (string) $second_value);
}));

/**
 * currency_format filter
 *
 * @param float $amount number to output
 * @return string the number in correct currency format
 */
$twig->addFilter(new TwigFilter('currency_format', function ($amount) {

    $sign = '';

    if ($amount < 0) {
        $sign = '-';
        $amount = -$amount;
    }

    $currency = $_SESSION['currency'] ?? Currency::getCurrencyFromCode();

    return $sign . $currency['symbol']
        . number_format($amount * $currency['ratio'], 2);
}, [
    'is_safe' => ['html']
]));

/**
 * json_decode filter - decode JSON string to array
 *
 * @param string $value JSON string
 * @return array|null decoded array or null on failure
 */
$twig->addFilter(new TwigFilter('json_decode', function ($value) {
    if (empty($value) || !is_string($value)) {
        return [];
    }
    return json_decode($value, true) ?? [];
}));

$twig->addFilter(new TwigFilter('truncate', function ($value, $length = 30, $preserve = false, $separator = '...') {
    if (mb_strlen($value) <= $length) {
        return $value;
    }

    if ($preserve) {
        $breakpoint = mb_strpos($value, ' ', $length);
        if ($breakpoint !== false) {
            $length = $breakpoint;
        }
    }

    return mb_substr($value, 0, $length) . $separator;
}));

/**
 * Return enabled currencies
 *
 * @author Andrew Morgan <andrew.morgan@mtcmedia.co.uk>
 *
 * @return Array Returns currency object of all enabled currencies
 */
$twig->addFunction(new TwigFunction('enabled_currencies', function () {
    return Currency::getEnabledCurrencies();
}));

/**
 * Ability to call a static method through twig
 *
 * @author Lukas Giegerich <lukas@mtcmedia.co.uk>
 * @author Stackoverflow ({@link http://stackoverflow.com/questions/6844266/twig-template-engine-using-a-static-function-or-variable})
 *
 * @param string $class
 * @param string $function
 * @param array  $args
 *
 * @return mixed|null
 */


$twig->addFunction(new TwigFunction('staticCall', function ($class, $function, $args = []) {
    if (class_exists($class) && method_exists($class, $function)) {
        return call_user_func_array([$class, $function], $args);
    }

    return null;
}));

/**
 * Add a new Twig function for rendering form fields
 */
$twig->addFunction(new TwigFunction('form_field', function ($options) use ($twig) {
    return \Mtc\Core\FormField::renderField($options, $twig);
}, [
    'is_safe' => ['html']
]));

/*
 * Creates the html for all the available currencies.
 */

$filter = new TwigFilter('multi_currency', function ($amount) use ($twig) {

    $currency_data = Currency::getCurrencyFromCode($_SESSION['selected_currency'] ?? null);
    $options = [
        'amount' => (float) number_format($amount, 2),
        'session_currency' => $currency_data,
    ];

    return $twig->render('currency/multi_currency.twig', $options);
}, [
    'is_safe' => ['html']
]);

$twig->addFilter($filter);

$twig->addFunction(new TwigFunction('mergeMenus', function ($items, $categories) {
    $menu = new Collection();
    $menu = $menu->merge($items);
    $menu = $menu->merge($categories);
    return $menu->slice(0, 8)->all();
}));


$twig->addFunction(new TwigFunction('method_field', function ($method) {
    return method_field($method);
}, [
    'is_safe' => ['html']
]));

/**
 * Generate the URL to a named route.
 *
 * @param  array|string  $name
 * @param  mixed  $parameters
 * @param  bool  $absolute
 * @return string
 */
$twig->addFunction(new TwigFunction('route', function ($name, $parameters = [], $absolute = true) {
    return route($name, $parameters, $absolute);
}));

$twig->addFunction(new TwigFunction('routeName', function () {
    return request()->route()->getName();
}));

$twig->addFunction(new TwigFunction('event', function ($event, $parameters) {
    $templates = \Illuminate\Support\Facades\Event::dispatch($event, new $event($parameters));
    foreach ($templates as $markup) {
        echo $markup;
    }
}));

$twig->addFunction(new TwigFunction('can', function ($permission) {
    return Permissions::can($permission);
}));


$twig->addFunction(new TwigFunction('file_exists', function ($url) {
    if (file_exists($_SERVER["DOCUMENT_ROOT"] . $url)) {
        return true;
    }
}));

/**
 * Load values from previous request
 *
 * @param  string  $key
 * @param  mixed  $default
 * @return string
 */
$twig->addFunction(new TwigFunction('old', function ($key, $default = null) {
    return old($key, $default);
}));

$twig->addFunction(new TwigFunction('config', function ($key = null, $default = null) {
    return config($key, $default);
}));
$twig->addFunction(new TwigFunction('request', function ($key = null, $default = null) {
    return request($key, $default);
}));

/**
 * Find the number of member new messages
 */
$twig->addFunction(new TwigFunction('member_new_messages', function () {
    $member = \Mtc\Modules\Members\Classes\Auth::getLoggedInMember();
    $unread_messages = \Mtc\Plugins\MembersMessaging\Classes\Message::query()
        ->whereHas('participants', function ($query) use ($member) {
            $query->where('member_id', $member->id)
                ->where('admin', '=', '0');
        })
        ->whereNull('read_at')
        ->where('private', '=', '0')
        ->where('admin', '=', '1')
        ->count();
    return $unread_messages ? '( ' . $unread_messages . ' )' : '';
}));

/**
 * Vite asset loading functions
 * Used for loading JS and CSS assets from Vite build
 */
$twig->addFunction(new TwigFunction('vite_asset', function (string $full_key) {
    if (config('site.vite_dev_server') === true) {
        $dev_server_url = config('site.vite_dev_server_url') ?: 'http://localhost:5173';
        return "{$dev_server_url}/public/build/{$full_key}";
    }

    static $manifest = null;
    static $manifestLoaded = false;
    $manifest_path = SITE_PATH . '/public/build/manifest.json';
    if (!$manifestLoaded) {
        if (!file_exists($manifest_path)) {
            throw new Exception("Asset manifest not found: {$manifest_path}");
        }
        $manifest = json_decode((string)file_get_contents($manifest_path), true, 512, JSON_THROW_ON_ERROR);
        $manifestLoaded = true;
    }
    if (!isset($manifest[$full_key])) {
        throw new Exception("Asset not found in Vite manifest: {$full_key}");
    }
    return '/public/build/' . $manifest[$full_key]['file'];
}));

$twig->addFunction(new TwigFunction('vite', function ($asset_filename) {
    if (config('site.vite_dev_server') === true) {
        // Get the dev server URL from an environment variable
        $dev_server_url = config('site.vite_dev_server_url') ?: 'http://localhost:5173';
        // Correctly append the base path and the full asset filename
        return "{$dev_server_url}/public/build/sites/default/" . $asset_filename;
    }

    // Production mode - use the manifest file
    static $manifest = null;
    static $manifestLoaded = false;
    $manifest_path = SITE_PATH . '/public/build/manifest.json';
    if (!$manifestLoaded) {
        if (!file_exists($manifest_path)) {
            throw new Exception("Asset manifest not found: {$manifest_path}");
        }
        $manifest = json_decode((string)file_get_contents($manifest_path), true, 512, JSON_THROW_ON_ERROR);
        $manifestLoaded = true;
    }
    $key = 'sites/default/' . $asset_filename;
    if (!isset($manifest[$key])) {
        throw new Exception("Asset not found in Vite manifest: {$key}");
    }
    return '/public/build/' . $manifest[$key]['file'];
}));

$twig->addFunction(new TwigFunction('vite_css', function ($asset_filename) {
    // In dev, Vue HMR injects CSS automatically; no link tags needed.
    if (config('site.vite_dev_server') === true) {
        return [];
    }

    static $manifest = null;
    static $manifestLoaded = false;
    $manifest_path = SITE_PATH . '/public/build/manifest.json';
    if (!$manifestLoaded) {
        if (!file_exists($manifest_path)) {
            throw new Exception("Asset manifest not found: {$manifest_path}");
        }
        $manifest = json_decode((string)file_get_contents($manifest_path), true, 512, JSON_THROW_ON_ERROR);
        $manifestLoaded = true;
    }

    $key = 'sites/default/' . $asset_filename;
    if (!isset($manifest[$key])) {
        throw new Exception("Asset not found in Vite manifest: {$key}");
    }

    $collected = [];

    $collectCss = function ($entryKey) use ($manifest, &$collected, &$collectCss) {
        if (!isset($manifest[$entryKey])) return;
        $entry = $manifest[$entryKey];
        if (!empty($entry['css'])) {
            foreach ($entry['css'] as $css) {
                $collected[] = '/public/build/' . $css;
            }
        }
        if (!empty($entry['imports'])) {
            foreach ($entry['imports'] as $importKey) {
                $collectCss($importKey);
            }
        }
    };

    $collectCss($key);

    // Remove duplicates and reindex
    return array_values(array_unique($collected));
}));

/**
 * Generate responsive image HTML with srcset from block image data
 * Usage: {{ responsive_image(imageData, {sizes: '100vw', class: 'hero', loading: 'lazy'}) }}
 */
$twig->addFunction(new TwigFunction('responsive_image', function ($imageData, $options = []) {
    if (empty($imageData)) {
        return '';
    }

    // Handle both array and string input
    if (is_string($imageData) && str_starts_with($imageData, '{')) {
        $imageData = json_decode($imageData, true) ?: ['url' => $imageData];
    } elseif (is_string($imageData)) {
        $imageData = ['url' => $imageData];
    }

    // Get URL - support multiple key patterns
    $url = $imageData['url'] ?? $imageData['value'] ?? '';
    if (empty($url)) {
        return '';
    }

    $alt = htmlspecialchars($imageData['alt'] ?? '', ENT_QUOTES, 'UTF-8');
    $processed = $imageData['processed'] ?? [];
    $srcset = $imageData['srcset'] ?? '';

    // Default options
    $class = htmlspecialchars($options['class'] ?? '', ENT_QUOTES, 'UTF-8');
    $sizes = htmlspecialchars($options['sizes'] ?? '100vw', ENT_QUOTES, 'UTF-8');
    $loading = htmlspecialchars($options['loading'] ?? 'lazy', ENT_QUOTES, 'UTF-8');

    // Build srcset if we have processed images and no pre-built srcset
    if (empty($srcset) && !empty($processed)) {
        $srcsetParts = [];
        foreach ($processed as $width => $path) {
            $srcsetParts[] = htmlspecialchars($path, ENT_QUOTES, 'UTF-8') . ' ' . $width . 'w';
        }
        $srcset = implode(', ', $srcsetParts);
    }

    // Use the largest processed image as src fallback when available
    if (!empty($processed)) {
        $largestWidth = max(array_keys($processed));
        $url = $processed[$largestWidth];
    }

    // Build srcset attribute
    $srcsetAttr = !empty($srcset) ? ' srcset="' . $srcset . '" sizes="' . $sizes . '"' : '';

    // Build class attribute
    $classAttr = !empty($class) ? ' class="' . $class . '"' : '';

    return sprintf(
        '<img src="%s" alt="%s"%s%s loading="%s">',
        htmlspecialchars($url, ENT_QUOTES, 'UTF-8'),
        $alt,
        $srcsetAttr,
        $classAttr,
        $loading
    );
}, ['is_safe' => ['html']]));

/**
 * Get srcset string from block image data
 * Usage: <img srcset="{{ image_srcset(imageData) }}" sizes="100vw">
 */
$twig->addFunction(new TwigFunction('image_srcset', function ($imageData) {
    if (empty($imageData)) {
        return '';
    }

    // Handle JSON-encoded strings
    if (is_string($imageData) && str_starts_with($imageData, '{')) {
        $imageData = json_decode($imageData, true);
    }

    if (!is_array($imageData)) {
        return '';
    }

    // Return pre-built srcset if available (sanitize for safety)
    if (!empty($imageData['srcset'])) {
        return htmlspecialchars($imageData['srcset'], ENT_QUOTES, 'UTF-8');
    }

    // Build from processed array
    $processed = $imageData['processed'] ?? [];
    if (empty($processed)) {
        return '';
    }

    $srcsetParts = [];
    foreach ($processed as $width => $path) {
        $srcsetParts[] = htmlspecialchars($path, ENT_QUOTES, 'UTF-8') . ' ' . $width . 'w';
    }

    return implode(', ', $srcsetParts);
}));
