<?php
namespace App\Core;

/** Format dan parsing angka/mata uang gaya Indonesia. */
class Num
{
    /** Mengubah teks angka apa pun menjadi float. Menangani "Rp 1.250.000,50", "1,250,000.50", "(1.000)". */
    public static function parse($v): float
    {
        if ($v === null || $v === '') return 0.0;
        if (is_int($v) || is_float($v)) return (float)$v;
        $s = trim((string)$v);
        if ($s === '' || $s === '-') return 0.0;

        // Nilai error Excel
        if ($s !== '' && $s[0] === '#') return 0.0;

        $neg = false;
        if (preg_match('/^\((.*)\)$/', $s, $m)) { $neg = true; $s = $m[1]; }
        $s = preg_replace('/[^0-9,.\-]/u', '', $s) ?? '';
        if ($s === '' || $s === '-') return 0.0;
        if (str_starts_with($s, '-')) { $neg = true; $s = ltrim($s, '-'); }

        $lastDot   = strrpos($s, '.');
        $lastComma = strrpos($s, ',');

        if ($lastDot !== false && $lastComma !== false) {
            // Pemisah desimal adalah yang paling kanan
            if ($lastComma > $lastDot) { $s = str_replace('.', '', $s); $s = str_replace(',', '.', $s); }
            else                       { $s = str_replace(',', '', $s); }
        } elseif ($lastComma !== false) {
            $dec = strlen($s) - $lastComma - 1;
            // "1,250,000" -> ribuan ; "1250,50" -> desimal
            if ($dec === 3 && substr_count($s, ',') >= 1 && strlen(str_replace(',', '', $s)) > 3
                && preg_match('/^\d{1,3}(,\d{3})+$/', $s)) {
                $s = str_replace(',', '', $s);
            } else {
                $s = str_replace(',', '.', $s);
            }
        } elseif ($lastDot !== false) {
            if (preg_match('/^\d{1,3}(\.\d{3})+$/', $s)) $s = str_replace('.', '', $s);
        }
        $f = (float)$s;
        return $neg ? -$f : $f;
    }

    /** Apakah teks tampak seperti angka (untuk validasi import). */
    public static function looksNumeric($v): bool
    {
        if (is_int($v) || is_float($v)) return true;
        if (!is_string($v)) return false;
        $s = trim($v);
        if ($s === '') return false;
        return (bool)preg_match('/^\(?\s*-?\s*(Rp\.?\s*)?[0-9][0-9.,\s]*\)?$/iu', $s);
    }

    /** Format angka biasa dengan pemisah Indonesia. */
    public static function fmt($v, ?int $dec = null): string
    {
        $dec = $dec ?? Settings::int('format.desimal_tampilan', 0);
        $ts  = Settings::str('format.pemisah_ribuan', '.');
        $ds  = Settings::str('format.pemisah_desimal', ',');
        return number_format((float)$v, $dec, $ds, $ts);
    }

    /** Format mata uang: "Rp 1.250.000" ; negatif -> "(Rp 1.250.000)". */
    public static function rupiah($v, ?int $dec = null, bool $withSymbol = true): string
    {
        $v   = (float)$v;
        $sym = Settings::str('umum.simbol_mata_uang', 'Rp');
        if (abs($v) < 0.005 && Settings::bool('format.nol_sebagai_strip', true)) return '-';
        $abs = self::fmt(abs($v), $dec);
        $txt = ($withSymbol ? $sym . ' ' : '') . $abs;
        return $v < 0 ? '(' . $txt . ')' : $txt;
    }

    /** Versi HTML: negatif diberi warna merah. */
    public static function rupiahHtml($v, ?int $dec = null): string
    {
        $v = (float)$v;
        $txt = htmlspecialchars(self::rupiah($v, $dec), ENT_QUOTES, 'UTF-8');
        if ($v < 0 && Settings::bool('format.negatif_merah', true)) {
            return '<span class="text-danger fw-semibold">' . $txt . '</span>';
        }
        return $txt;
    }

    public static function percent($v, int $dec = 1): string
    {
        if ($v === null) return '-';
        return self::fmt((float)$v, $dec) . '%';
    }

    public static function days($v): string
    {
        if ($v === null || $v === '') return '-';
        return self::fmt((float)$v, 0) . ' hari';
    }

    /** Pembagian aman: mengembalikan null bila pembagi nol. */
    public static function div($a, $b): ?float
    {
        $b = (float)$b;
        if (abs($b) < 1e-9) return null;
        return (float)$a / $b;
    }

    public static function median(array $values): ?float
    {
        $v = array_values(array_filter($values, fn($x) => $x !== null));
        if (!$v) return null;
        sort($v, SORT_NUMERIC);
        $n = count($v);
        $mid = intdiv($n, 2);
        return $n % 2 ? (float)$v[$mid] : ((float)$v[$mid - 1] + (float)$v[$mid]) / 2;
    }
}
