<?php

namespace App\Core;

/**
 * للتحقق من النتائج مقابل المدى المرجعي والقيم الحرجة
 * يدعم المدى المرجعي حسب العمر والجنس (reference_ranges)
 */
class ResultValidator
{
    /**
     * جلب المدى المرجعي والقيم الحرجة من reference_ranges حسب العمر والجنس
     * @param \PDO $db
     * @param int $testId
     * @param float|null $ageYears العمر بالسنوات (من date_of_birth)
     * @param string|null $gender M أو F
     * @return array{reference_range: string|null, critical_min: float|null, critical_max: float|null}
     */
    public static function getReferenceRangeForPatient($db, int $testId, ?float $ageYears, ?string $gender): array
    {
        $result = ['reference_range' => null, 'critical_min' => null, 'critical_max' => null];
        try {
            $db->query('SELECT id FROM reference_ranges LIMIT 1');
        } catch (\Throwable $e) {
            return $result;
        }
        $g = $gender === 'M' || $gender === 'F' ? $gender : 'both';
        $age = $ageYears !== null ? (float)$ageYears : null;
        $stmt = $db->prepare(
            'SELECT reference_range, critical_value_min, critical_value_max
             FROM reference_ranges
             WHERE test_id = ?
             AND (gender = ? OR gender = ?)
             AND (age_min_years IS NULL OR age_min_years <= ?)
             AND (age_max_years IS NULL OR age_max_years >= ?)
             ORDER BY CASE WHEN gender = ? THEN 1 ELSE 0 END DESC, age_min_years DESC
             LIMIT 1'
        );
        $stmt->execute([$testId, $g, 'both', $age ?? 999, $age ?? 0, $g]);
        $row = $stmt->fetch();
        if ($row) {
            $result['reference_range'] = $row['reference_range'];
            $result['critical_min'] = $row['critical_value_min'] !== null ? (float)$row['critical_value_min'] : null;
            $result['critical_max'] = $row['critical_value_max'] !== null ? (float)$row['critical_value_max'] : null;
        }
        return $result;
    }

    /**
     * يفسر المدى المرجعي "3.5-5.5" أو ">10" أو "<5"
     * @return array{min: float|null, max: float|null}
     */
    public static function parseReferenceRange(?string $ref): array
    {
        if ($ref === null || trim($ref) === '') {
            return ['min' => null, 'max' => null];
        }
        $ref = trim($ref);
        if (preg_match('/^([\d.]+)\s*-\s*([\d.]+)$/', $ref, $m)) {
            return ['min' => (float)$m[1], 'max' => (float)$m[2]];
        }
        if (preg_match('/^>\s*([\d.]+)$/', $ref, $m)) {
            return ['min' => (float)$m[1], 'max' => null];
        }
        if (preg_match('/^<\s*([\d.]+)$/', $ref, $m)) {
            return ['min' => null, 'max' => (float)$m[1]];
        }
        return ['min' => null, 'max' => null];
    }

    /**
     * @return 'normal'|'high'|'low'|'critical'
     */
    public static function checkValue(
        ?string $value,
        ?string $referenceRange,
        ?float $criticalMin = null,
        ?float $criticalMax = null
    ): string {
        if ($value === null || trim($value) === '') {
            return 'normal';
        }
        $num = self::parseNumericValue(trim($value));
        if ($num === null) {
            return 'normal';
        }

        $ref = self::parseReferenceRange($referenceRange);

        if ($criticalMin !== null && $num <= $criticalMin) {
            return 'critical';
        }
        if ($criticalMax !== null && $num >= $criticalMax) {
            return 'critical';
        }

        if ($ref['min'] !== null && $num < $ref['min']) {
            return 'low';
        }
        if ($ref['max'] !== null && $num > $ref['max']) {
            return 'high';
        }

        return 'normal';
    }

    private static function parseNumericValue(string $val): ?float
    {
        $val = str_replace(',', '.', $val);
        if (is_numeric($val)) {
            return (float)$val;
        }
        return null;
    }
}
