<?php
declare(strict_types=1);

/** Experimental only: input must come from paired exact-decimal/stringify PDO. */
final class ExactFetchValue
{
    public static function convert($value, array $meta)
    {
        if ($value === null) { return null; }
        $type = strtolower((string)($meta['native_type'] ?? ''));
        if (in_array($type, ['tinyint','smallint','int','integer','bigint'], true)) {
            if (!is_string($value) || !preg_match('/^-?\d+$/D', $value)) {
                throw new UnexpectedValueException('Exact integer text required');
            }
            $negative = isset($value[0]) && $value[0] === '-';
            $digits = ltrim($negative ? substr($value,1) : $value,'0');
            $digits = $digits === '' ? '0' : $digits;
            $limit = $negative ? substr((string)PHP_INT_MIN,1) : (string)PHP_INT_MAX;
            if (strlen($digits) > strlen($limit) || (strlen($digits) === strlen($limit) && strcmp($digits,$limit) > 0)) {
                return $value;
            }
            return (int)$value;
        }
        if (in_array($type, ['float','double','real'], true)) {
            if (!is_string($value) || !is_numeric($value) || !is_finite((float)$value)) {
                throw new UnexpectedValueException('Finite floating point text required');
            }
            return (float)$value;
        }
        if (in_array($type, ['number','decimal','numeric'], true)) {
            if (!is_string($value)) { throw new UnexpectedValueException('Lossless decimal text required'); }
            $precision = (int)($meta['precision'] ?? 0);
            $scale = (int)($meta['scale'] ?? 0);
            if ($precision <= 0) { return $value; } // Unknown expression scale must not be guessed.
            if ($scale < 0 || $scale > $precision || !preg_match('/^(-?)(\d+)(?:\.(\d+))?$/D',$value,$m)) {
                throw new UnexpectedValueException('Plain decimal and declared scale required');
            }
            $fraction = $m[3] ?? '';
            if (strlen($fraction) > $scale && trim(substr($fraction,$scale),'0') !== '') {
                throw new UnexpectedValueException('Refusing to truncate decimal value');
            }
            $whole = ltrim($m[2],'0');
            $whole = $whole === '' ? '0' : $whole;
            $fraction = str_pad(substr($fraction,0,$scale),$scale,'0');
            $sign = $whole === '0' && trim($fraction,'0') === '' ? '' : $m[1];
            return $sign . $whole . ($scale > 0 ? '.' . $fraction : '');
        }
        return $value;
    }
}
