<?php

namespace App\Libraries;

/**
 * Test Validation Service
 * 
 * Validates TestType, ResultType, and RefType combinations
 * according to business rules for CLQMS test definitions.
 */
class TestValidationService
{
    /**
     * Valid TestType to ResultType mappings
     */
    private const TEST_TYPE_RESULT_TYPES = [
        'TEST'  => ['NMRIC', 'RANGE', 'TEXT', 'VSET'],
        'PARAM' => ['NMRIC', 'RANGE', 'TEXT', 'VSET'],
        'CALC'  => ['NMRIC'],
        'GROUP' => ['NORES'],
        'TITLE' => ['NORES'],
    ];

    /**
     * Valid ResultType to RefType mappings
     */
    private const RESULT_TYPE_REF_TYPES = [
        'NMRIC' => ['RANGE', 'THOLD'],
        'RANGE' => ['RANGE', 'THOLD'],
        'VSET'  => ['VSET'],
        'TEXT'  => ['TEXT'],
        'NORES' => ['NOREF'],
    ];

    /**
     * Reference table mapping based on ResultType and RefType
     */
    private const REFERENCE_TABLES = [
        'NMRIC' => [
            'RANGE' => 'refnum',
            'THOLD' => 'refnum',
        ],
        'RANGE' => [
            'RANGE' => 'refnum',
            'THOLD' => 'refnum',
        ],
        'VSET' => [
            'VSET' => 'reftxt',
        ],
        'TEXT' => [
            'TEXT' => 'reftxt',
        ],
        'NORES' => [
            'NOREF' => null,
        ],
    ];

    /**
     * Validate TestType and ResultType combination
     *
     * @param string $testType
     * @param string $resultType
     * @return array ['valid' => bool, 'error' => string|null]
     */
    public static function validateTestTypeResultType(string $testType, string $resultType): array
    {
        $testType = strtoupper($testType);
        $resultType = strtoupper($resultType);

        if (!isset(self::TEST_TYPE_RESULT_TYPES[$testType])) {
            return [
                'valid' => false,
                'error' => "Invalid TestType '{$testType}'. Allowed: TEST, PARAM, CALC, GROUP, TITLE"
            ];
        }

        $validResultTypes = self::TEST_TYPE_RESULT_TYPES[$testType];
        
        if (!in_array($resultType, $validResultTypes, true)) {
            return [
                'valid' => false,
                'error' => "Invalid ResultType '{$resultType}' for TestType '{$testType}'. Allowed: " . implode(', ', $validResultTypes)
            ];
        }

        return ['valid' => true, 'error' => null];
    }

    /**
     * Validate ResultType and RefType combination
     *
     * @param string $resultType
     * @param string $refType
     * @return array ['valid' => bool, 'error' => string|null]
     */
    public static function validateResultTypeRefType(string $resultType, string $refType): array
    {
        $resultType = strtoupper($resultType);
        $refType = strtoupper($refType);

        if (!isset(self::RESULT_TYPE_REF_TYPES[$resultType])) {
            return [
                'valid' => false,
                'error' => "Invalid ResultType '{$resultType}'. Allowed: NMRIC, RANGE, TEXT, VSET, NORES"
            ];
        }

        $validRefTypes = self::RESULT_TYPE_REF_TYPES[$resultType];
        
        if (!in_array($refType, $validRefTypes, true)) {
            return [
                'valid' => false,
                'error' => "Invalid RefType '{$refType}' for ResultType '{$resultType}'. Allowed: " . implode(', ', $validRefTypes)
            ];
        }

        return ['valid' => true, 'error' => null];
    }

    /**
     * Validate complete test type combination
     *
     * @param string $testType
     * @param string $resultType
     * @param string $refType
     * @return array ['valid' => bool, 'error' => string|null]
     */
    public static function validate(string $testType, string $resultType, string $refType): array
    {
        // First validate TestType and ResultType
        $testResultValidation = self::validateTestTypeResultType($testType, $resultType);
        if (!$testResultValidation['valid']) {
            return $testResultValidation;
        }

        // Then validate ResultType and RefType
        return self::validateResultTypeRefType($resultType, $refType);
    }

    /**
     * Get valid ResultTypes for a TestType
     *
     * @param string $testType
     * @return array
     */
    public static function getValidResultTypes(string $testType): array
    {
        $testType = strtoupper($testType);
        return self::TEST_TYPE_RESULT_TYPES[$testType] ?? [];
    }

    /**
     * Get valid RefTypes for a ResultType
     *
     * @param string $resultType
     * @return array
     */
    public static function getValidRefTypes(string $resultType): array
    {
        $resultType = strtoupper($resultType);
        return self::RESULT_TYPE_REF_TYPES[$resultType] ?? [];
    }

    /**
     * Get reference table name based on ResultType and RefType
     *
     * @param string $resultType
     * @param string $refType
     * @return string|null Returns table name or null if no reference table needed
     */
    public static function getReferenceTable(?string $resultType, ?string $refType): ?string
    {
        if ($resultType === null || $refType === null) {
            return null;
        }

        $resultType = strtoupper($resultType);
        $refType = strtoupper($refType);

        return self::REFERENCE_TABLES[$resultType][$refType] ?? null;
    }

    /**
     * Check if a test needs reference ranges
     *
     * @param string $resultType
     * @return bool
     */
    public static function needsReferenceRanges(?string $resultType): bool
    {
        if ($resultType === null) {
            return false;
        }

        $resultType = strtoupper($resultType);
        return $resultType !== 'NORES';
    }

    /**
     * Check if a test uses refnum table
     *
     * @param string $resultType
     * @param string $refType
     * @return bool
     */
    public static function usesRefNum(?string $resultType, ?string $refType): bool
    {
        return self::getReferenceTable($resultType, $refType) === 'refnum';
    }

    /**
     * Check if a test uses reftxt table
     *
     * @param string $resultType
     * @param string $refType
     * @return bool
     */
    public static function usesRefTxt(?string $resultType, ?string $refType): bool
    {
        return self::getReferenceTable($resultType, $refType) === 'reftxt';
    }

    /**
     * Check if TestType is CALC
     *
     * @param string $testType
     * @return bool
     */
    public static function isCalc(string $testType): bool
    {
        return strtoupper($testType) === 'CALC';
    }

    /**
     * Check if TestType is GROUP
     *
     * @param string $testType
     * @return bool
     */
    public static function isGroup(string $testType): bool
    {
        return strtoupper($testType) === 'GROUP';
    }

    /**
     * Check if TestType is TITLE
     *
     * @param string $testType
     * @return bool
     */
    public static function isTitle(string $testType): bool
    {
        return strtoupper($testType) === 'TITLE';
    }

    /**
     * Check if TestType is TEST or PARAM (technical tests)
     *
     * @param string $testType
     * @return bool
     */
    public static function isTechnicalTest(string $testType): bool
    {
        $testType = strtoupper($testType);
        return in_array($testType, ['TEST', 'PARAM'], true);
    }
}
