<?php
declare(strict_types=1);

function translation_overrides(): array
{
    return array(
        'Bill Management' => 'จัดการบิล',
        'Bill management' => 'จัดการบิล',
        'Open Bills' => 'จัดการบิล',
        'View bills' => 'ดูบิล',
        'Bill filters' => 'ตัวกรองบิล',
        'Bill list' => 'รายการบิล',
        'Current scope' => 'ขอบเขตข้อมูลปัจจุบัน',
        'View' => 'ดู',
        'Reprint' => 'พิมพ์ซ้ำ',
        'Bill Receipt' => 'ใบเสร็จบิล',
        'Bill was not found.' => 'ไม่พบบิล',
        'Pickup due' => 'กำหนดรับรถ',
        'All accessible branches' => 'ทุกสาขาที่มีสิทธิ์',
        'Bill no. / Plate / Phone' => 'เลขที่บิล / ทะเบียนรถ / เบอร์โทร',
        'Cancelled' => 'ยกเลิก',
        'Paid / Partial / Unpaid' => 'ชำระครบ / ชำระบางส่วน / ยังไม่ชำระ',
        'Period' => 'ช่วงเวลา',
        'Open bill details, reprint receipts and cancel bills when allowed.' => 'เปิดดูรายละเอียดบิล พิมพ์ใบเสร็จซ้ำ และยกเลิกบิลได้ตามสิทธิ์',
        'Review bills by day or month, search by bill number, plate number or phone number, then open bill details, reprint or cancel when permitted.' => 'ตรวจสอบบิลตามวันหรือเดือน ค้นหาจากเลขที่บิล ทะเบียนรถ หรือเบอร์โทร แล้วเปิดดูรายละเอียด พิมพ์ซ้ำ หรือยกเลิกได้ตามสิทธิ์',
        'Filter the bill list by selected period and branch, then search specific bills, plates or customer phones.' => 'กรองรายการบิลตามช่วงเวลาและสาขาที่เลือก แล้วค้นหาบิล ทะเบียนรถ หรือเบอร์โทรที่ต้องการ',
        'No bills found for the selected filters.' => 'ไม่พบบิลตามตัวกรองที่เลือก',
        'Use View bills to load the bill list.' => 'กด View bills เพื่อเรียกดูรายการบิล',
        'Use View bills to load data for the selected period and branch.' => 'กด View bills เพื่อโหลดข้อมูลตามช่วงเวลาและสาขาที่เลือก',
    );
}

function translation_dictionary(): array
{
    static $dictionary = null;

    if (is_array($dictionary)) {
        return $dictionary;
    }

    $path = __DIR__ . '/translations.php';
    $loaded = is_file($path) ? require $path : array();
    $dictionary = is_array($loaded) ? array_replace($loaded, translation_overrides()) : translation_overrides();

    return $dictionary;
}

function translation_dictionary_for_language(string $language): array
{
    static $cache = array();

    $language = normalize_language($language);

    if (isset($cache[$language])) {
        return $cache[$language];
    }

    $forward = translation_dictionary();

    if ($language === 'th') {
        $cache[$language] = $forward;

        return $cache[$language];
    }

    $reverse = array();
    foreach ($forward as $english => $thai) {
        $english = (string) $english;
        $thai = (string) $thai;

        if ($thai !== '' && !isset($reverse[$thai])) {
            $reverse[$thai] = $english;
        }
    }

    $cache[$language] = $reverse;

    return $cache[$language];
}

function current_language_dictionary_json(): string
{
    $json = json_encode(
        translation_dictionary_for_language(current_language()),
        JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
    );

    return is_string($json) ? $json : '{}';
}

function translate_text(string $text, ?string $language = null): string
{
    $language = $language !== null ? normalize_language($language) : current_language();

    if ($text === '') {
        return $text;
    }

    $dictionary = translation_dictionary_for_language($language);

    if (isset($dictionary[$text])) {
        return (string) $dictionary[$text];
    }

    static $sortedKeys = array();

    if (!isset($sortedKeys[$language])) {
        $keys = array_keys($dictionary);
        usort($keys, static function ($left, $right): int {
            return mb_strlen((string) $right, 'UTF-8') <=> mb_strlen((string) $left, 'UTF-8');
        });
        $sortedKeys[$language] = $keys;
    }

    foreach ($sortedKeys[$language] as $source) {
        $source = (string) $source;

        if ($source === '' || !str_contains($text, $source)) {
            continue;
        }

        $translated = (string) ($dictionary[$source] ?? '');

        if ($translated === '' || $translated === $source) {
            continue;
        }

        $pattern = '/(?<![\p{L}\p{N}])' . preg_quote($source, '/') . '(?![\p{L}\p{N}])/u';
        $text = preg_replace_callback($pattern, static function () use ($translated): string {
            return $translated;
        }, $text) ?? $text;
    }

    return $text;
}

function translate_tag_attributes(string $tag, string $language): string
{
    $attributes = array(
        'placeholder',
        'title',
        'aria-label',
        'aria-placeholder',
        'alt',
        'data-closed-label',
        'data-open-label',
    );

    foreach ($attributes as $attributeName) {
        $pattern = '/\b(' . preg_quote($attributeName, '/') . ')\s*=\s*(["\'])(.*?)\2/us';
        $tag = preg_replace_callback($pattern, static function (array $matches) use ($language): string {
            $decodedValue = htmlspecialchars_decode((string) ($matches[3] ?? ''), ENT_QUOTES);
            $translatedValue = translate_text($decodedValue, $language);

            return (string) $matches[1]
                . '='
                . (string) $matches[2]
                . htmlspecialchars($translatedValue, ENT_QUOTES, 'UTF-8')
                . (string) $matches[2];
        }, $tag) ?? $tag;
    }

    return $tag;
}

function translate_html_output(string $html): string
{
    if ($html === '') {
        return $html;
    }

    $language = current_language();
    $chunks = preg_split('/(<[^>]+>)/u', $html, -1, PREG_SPLIT_DELIM_CAPTURE);

    if (!is_array($chunks)) {
        return translate_text($html, $language);
    }

    $translated = '';
    $skipTextTag = '';

    foreach ($chunks as $chunk) {
        if ($chunk === '') {
            continue;
        }

        if ($chunk[0] === '<') {
            if ($skipTextTag !== '' && preg_match('/^<\s*\/\s*' . preg_quote($skipTextTag, '/') . '\b/i', $chunk)) {
                $translated .= $chunk;
                $skipTextTag = '';
                continue;
            }

            if (
                $skipTextTag === ''
                && preg_match('/^<\s*([a-z0-9:-]+)\b/i', $chunk, $tagMatches)
                && preg_match('/\bdata-no-translate(?:\s*=\s*(["\']).*?\1)?\b/i', $chunk)
            ) {
                $translated .= $chunk;

                if (!preg_match('/\/\s*>$/', $chunk)) {
                    $skipTextTag = strtolower((string) ($tagMatches[1] ?? ''));
                }

                continue;
            }

            if (preg_match('/^<\s*(script|style|textarea)\b/i', $chunk, $matches)) {
                $translated .= translate_tag_attributes($chunk, $language);
                $skipTextTag = strtolower((string) ($matches[1] ?? ''));
                continue;
            }

            $translated .= translate_tag_attributes($chunk, $language);
            continue;
        }

        $translated .= $skipTextTag !== '' ? $chunk : translate_text($chunk, $language);
    }

    return $translated;
}

function language_start_buffer(): void
{
    if (!empty($GLOBALS['ccw_language_buffer_started'])) {
        return;
    }

    ob_start('translate_html_output');
    $GLOBALS['ccw_language_buffer_started'] = true;
}

function language_end_buffer(): void
{
    if (empty($GLOBALS['ccw_language_buffer_started'])) {
        return;
    }

    $GLOBALS['ccw_language_buffer_started'] = false;
    if (ob_get_level() > 0) {
        ob_end_flush();
    }
}

function normalize_plate(string $plate): string
{
    $plate = preg_replace('/\s+/', '', trim($plate));

    return strtoupper($plate ?? '');
}

function normalize_phone(string $phone): string
{
    $digits = preg_replace('/\D+/', '', trim($phone));

    return $digits ?? '';
}

function customer_lookup_query_kind(string $query): string
{
    $query = trim($query);
    if ($query === '') {
        return '';
    }

    $phoneDigits = normalize_phone($query);
    $plateValue = normalize_plate($query);
    $looksLikePhone = $phoneDigits !== ''
        && preg_match('/^[\d\s\-\+\(\)]+$/u', $query)
        && strlen($phoneDigits) >= 8;

    if ($looksLikePhone) {
        return 'phone';
    }

    if ($plateValue !== '') {
        return 'plate';
    }

    return '';
}

function normalized_phone_sql(string $expression): string
{
    return "REPLACE(REPLACE(REPLACE(REPLACE(REPLACE({$expression}, '-', ''), ' ', ''), '+', ''), '(', ''), ')', '')";
}

function format_money($amount): string
{
    return number_format((float) $amount, 2);
}

function format_datetime(?string $value): string
{
    if (!$value) {
        return '-';
    }

    $timestamp = strtotime($value);

    if ($timestamp === false) {
        return '-';
    }

    return date('d/m/Y H:i', $timestamp);
}

function datetime_form_parts(?string $value): array
{
    if (!$value) {
        return array(
            'date' => '',
            'time' => '',
        );
    }

    $timestamp = strtotime($value);
    if ($timestamp === false) {
        return array(
            'date' => '',
            'time' => '',
        );
    }

    return array(
        'date' => date('Y-m-d', $timestamp),
        'time' => date('H:i', $timestamp),
    );
}

function localized_month_name(int $month, ?string $language = null): string
{
    if ($month < 1 || $month > 12) {
        return '';
    }

    $timestamp = strtotime(sprintf('2000-%02d-01', $month));
    if ($timestamp === false) {
        return '';
    }

    return translate_text(date('F', $timestamp), $language);
}

function localized_month_short_name(int $month, ?string $language = null): string
{
    $language = $language !== null ? normalize_language($language) : current_language();

    if ($month < 1 || $month > 12) {
        return '';
    }

    if ($language === 'th') {
        $months = array(
            1 => 'ม.ค.',
            2 => 'ก.พ.',
            3 => 'มี.ค.',
            4 => 'เม.ย.',
            5 => 'พ.ค.',
            6 => 'มิ.ย.',
            7 => 'ก.ค.',
            8 => 'ส.ค.',
            9 => 'ก.ย.',
            10 => 'ต.ค.',
            11 => 'พ.ย.',
            12 => 'ธ.ค.',
        );

        return $months[$month] ?? '';
    }

    $timestamp = strtotime(sprintf('2000-%02d-01', $month));

    if ($timestamp === false) {
        return '';
    }

    return date('M', $timestamp);
}

function localized_year_month_label(string $value, ?string $language = null): string
{
    if (preg_match('/^\d{4}-\d{2}$/', $value)) {
        $value .= '-01';
    }

    $timestamp = strtotime($value);
    if ($timestamp === false) {
        return $value;
    }

    $monthName = localized_month_name((int) date('n', $timestamp), $language);
    $year = date('Y', $timestamp);

    return trim($monthName . ' ' . $year);
}

function localized_month_period_options(string $selectedValue, int $pastYears = 5, int $futureYears = 1): array
{
    $selectedValue = preg_match('/^\d{4}-\d{2}$/', $selectedValue) ? $selectedValue : date('Y-m');
    $selectedTimestamp = strtotime($selectedValue . '-01');
    if ($selectedTimestamp === false) {
        $selectedTimestamp = strtotime(date('Y-m-01'));
    }

    $currentYear = (int) date('Y');
    $startYear = $currentYear + $futureYears;
    $endYear = $currentYear - $pastYears;
    $options = array();

    for ($year = $startYear; $year >= $endYear; $year--) {
        for ($month = 12; $month >= 1; $month--) {
            $value = sprintf('%04d-%02d', $year, $month);
            $options[$value] = localized_year_month_label($value . '-01');
        }
    }

    if (!isset($options[$selectedValue])) {
        $options[$selectedValue] = localized_year_month_label($selectedValue . '-01');
        krsort($options);
    }

    return $options;
}

function render_month_picker(string $name, string $id, string $value, string $ariaLabel = 'Monthly period'): string
{
    $value = preg_match('/^\d{4}-\d{2}$/', $value) ? $value : date('Y-m');
    $timestamp = strtotime($value . '-01');
    $year = $timestamp === false ? (int) date('Y') : (int) date('Y', $timestamp);

    ob_start();
    ?>
    <div class="ccw-month-picker" data-month-picker data-month-picker-year="<?= e((string) $year) ?>">
        <input id="<?= e($id) ?>" class="ccw-month-picker-native" data-month-picker-native name="<?= e($name) ?>" type="hidden" value="<?= e($value) ?>">
        <button class="ccw-month-picker-display" data-month-picker-button type="button" aria-label="<?= e(translate_text($ariaLabel)) ?>">
            <span data-month-picker-label><?= e(localized_year_month_label($value)) ?></span>
            <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 2h2v2h6V2h2v2h2a2 2 0 0 1 2 2v13a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V6a2 2 0 0 1 2-2h2V2zm12 8H5v9a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-9z" fill="currentColor"/></svg>
        </button>

        <div class="ccw-month-picker-panel" data-month-picker-panel hidden>
            <div class="ccw-month-picker-panel-head">
                <button class="ccw-month-picker-nav" data-month-picker-prev-year type="button" aria-label="<?= e(translate_text('Previous year')) ?>">
                    <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M15.4 5.4 9.8 11l5.6 5.6-1.4 1.4L7 11l7-7z" fill="currentColor"/></svg>
                </button>
                <strong data-month-picker-year-label><?= e((string) $year) ?></strong>
                <button class="ccw-month-picker-nav" data-month-picker-next-year type="button" aria-label="<?= e(translate_text('Next year')) ?>">
                    <svg viewBox="0 0 24 24" aria-hidden="true"><path d="m8.6 18.6 5.6-5.6-5.6-5.6L10 6l7 7-7 7z" fill="currentColor"/></svg>
                </button>
            </div>

            <div class="ccw-month-picker-grid">
                <?php for ($month = 1; $month <= 12; $month++): ?>
                    <?php $isSelected = $value === sprintf('%04d-%02d', $year, $month); ?>
                    <button
                        class="ccw-month-picker-month<?= $isSelected ? ' is-selected' : '' ?>"
                        data-month-picker-month="<?= e(sprintf('%02d', $month)) ?>"
                        type="button"
                        aria-pressed="<?= $isSelected ? 'true' : 'false' ?>"
                    ><?= e(localized_month_short_name($month)) ?></button>
                <?php endfor; ?>
            </div>
        </div>
    </div>
    <?php

    return (string) ob_get_clean();
}
